diff options
-rw-r--r-- | src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala | 16 | ||||
-rw-r--r-- | src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala | 230 | ||||
-rw-r--r-- | src/main/scala/uk/ac/ox/cs/rsacomb/util/Logger.scala | 45 | ||||
-rw-r--r-- | src/main/scala/uk/ac/ox/cs/rsacomb/util/RDFoxUtil.scala | 68 |
4 files changed, 230 insertions, 129 deletions
diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala index d41ca8c..5246d2f 100644 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala | |||
@@ -10,7 +10,7 @@ import tech.oxfordsemantic.jrdfox.logic.sparql.statement.SelectQuery | |||
10 | import tech.oxfordsemantic.jrdfox.logic.expression.{IRI, Term} | 10 | import tech.oxfordsemantic.jrdfox.logic.expression.{IRI, Term} |
11 | 11 | ||
12 | /* Local imports */ | 12 | /* Local imports */ |
13 | import util.{RDFoxUtil, RSA} | 13 | import util.{Logger, RDFoxUtil, RSA} |
14 | import sparql.ConjunctiveQuery | 14 | import sparql.ConjunctiveQuery |
15 | 15 | ||
16 | object RSAComb extends App { | 16 | object RSAComb extends App { |
@@ -53,15 +53,17 @@ object RSAComb extends App { | |||
53 | 53 | ||
54 | val ontology = RSAOntology(ontoPaths: _*) | 54 | val ontology = RSAOntology(ontoPaths: _*) |
55 | if (ontology.isRSA) { | 55 | if (ontology.isRSA) { |
56 | //println("ONTOLOGY IS RSA") | 56 | |
57 | Logger print "Ontology is RSA!" | ||
57 | 58 | ||
58 | /** Read SPARQL query from file */ | 59 | /** Read SPARQL query from file */ |
59 | val source = io.Source.fromFile(queryPath.getAbsoluteFile) | 60 | val query = RDFoxUtil.loadQueryFromFile(queryPath.getAbsoluteFile) |
60 | val query = source.getLines mkString "\n" | ||
61 | source.close() | ||
62 | 61 | ||
63 | /* Compute answers to query */ | 62 | /* Compute answers to query */ |
64 | val answers = ConjunctiveQuery(query).map(ontology ask _) | 63 | ConjunctiveQuery(query).map(ontology ask _) match { |
65 | answers map (_.toString) foreach println | 64 | case Some(answers) => Logger print answers |
65 | case None => | ||
66 | throw new RuntimeException("Submitted query is not conjunctive") | ||
67 | } | ||
66 | } | 68 | } |
67 | } | 69 | } |
diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala index 52d4905..87a2312 100644 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala | |||
@@ -54,6 +54,7 @@ import uk.ac.ox.cs.rsacomb.converter._ | |||
54 | import uk.ac.ox.cs.rsacomb.suffix._ | 54 | import uk.ac.ox.cs.rsacomb.suffix._ |
55 | import uk.ac.ox.cs.rsacomb.sparql._ | 55 | import uk.ac.ox.cs.rsacomb.sparql._ |
56 | import uk.ac.ox.cs.rsacomb.util.{RDFoxUtil, RSA} | 56 | import uk.ac.ox.cs.rsacomb.util.{RDFoxUtil, RSA} |
57 | import uk.ac.ox.cs.rsacomb.util.Logger | ||
57 | 58 | ||
58 | object RSAOntology { | 59 | object RSAOntology { |
59 | 60 | ||
@@ -141,87 +142,91 @@ class RSAOntology(val ontology: OWLOntology) { | |||
141 | * why the ontology might not be RSA. This could help a second | 142 | * why the ontology might not be RSA. This could help a second |
142 | * step of approximation of an Horn-ALCHOIQ to RSA | 143 | * step of approximation of an Horn-ALCHOIQ to RSA |
143 | */ | 144 | */ |
144 | lazy val isRSA: Boolean = { | 145 | lazy val isRSA: Boolean = Logger.timed( |
145 | 146 | { | |
146 | val unsafe = this.unsafeRoles | 147 | val unsafe = this.unsafeRoles |
147 | 148 | ||
148 | /* DEBUG: print rules in DL syntax and unsafe roles */ | 149 | // val renderer = new DLSyntaxObjectRenderer() |
149 | //val renderer = new DLSyntaxObjectRenderer() | 150 | // println() |
150 | //println("\nDL rules:") | 151 | // println("Unsafe roles:") |
151 | //axioms.foreach(x => println(renderer.render(x))) | 152 | // println(unsafe) |
152 | //println("\nUnsafe roles:") | 153 | // println() |
153 | //println(unsafe) | 154 | // println("DL rules:") |
154 | 155 | // tbox.foreach(x => println(renderer.render(x))) | |
155 | object RSAConverter extends RDFoxConverter { | 156 | |
156 | 157 | object RSAConverter extends RDFoxConverter { | |
157 | override def convert( | 158 | |
158 | expr: OWLClassExpression, | 159 | override def convert( |
159 | term: Term, | 160 | expr: OWLClassExpression, |
160 | unsafe: List[OWLObjectPropertyExpression], | 161 | term: Term, |
161 | skolem: SkolemStrategy, | 162 | unsafe: List[OWLObjectPropertyExpression], |
162 | suffix: RSASuffix | 163 | skolem: SkolemStrategy, |
163 | ): Shards = | 164 | suffix: RSASuffix |
164 | (expr, skolem) match { | 165 | ): Shards = |
165 | 166 | (expr, skolem) match { | |
166 | case (e: OWLObjectSomeValuesFrom, c: Constant) | 167 | |
167 | if unsafe contains e.getProperty => { | 168 | case (e: OWLObjectSomeValuesFrom, c: Constant) |
168 | val (res, ext) = super.convert(e, term, unsafe, skolem, suffix) | 169 | if unsafe contains e.getProperty => { |
169 | (RSA.PE(term, c.iri) :: RSA.U(c.iri) :: res, ext) | 170 | val (res, ext) = super.convert(e, term, unsafe, skolem, suffix) |
170 | } | 171 | (RSA.PE(term, c.iri) :: RSA.U(c.iri) :: res, ext) |
171 | 172 | } | |
172 | case (e: OWLDataSomeValuesFrom, c: Constant) | 173 | |
173 | if unsafe contains e.getProperty => { | 174 | case (e: OWLDataSomeValuesFrom, c: Constant) |
174 | val (res, ext) = super.convert(e, term, unsafe, skolem, suffix) | 175 | if unsafe contains e.getProperty => { |
175 | (RSA.PE(term, c.iri) :: RSA.U(c.iri) :: res, ext) | 176 | val (res, ext) = super.convert(e, term, unsafe, skolem, suffix) |
177 | (RSA.PE(term, c.iri) :: RSA.U(c.iri) :: res, ext) | ||
178 | } | ||
179 | |||
180 | case _ => super.convert(expr, term, unsafe, skolem, suffix) | ||
176 | } | 181 | } |
177 | 182 | ||
178 | case _ => super.convert(expr, term, unsafe, skolem, suffix) | 183 | } |
179 | } | 184 | |
180 | 185 | /* Ontology convertion into LP rules */ | |
181 | } | 186 | val term = RSAOntology.genFreshVariable() |
182 | 187 | val datalog = axioms | |
183 | /* Ontology convertion into LP rules */ | 188 | .map(a => RSAConverter.convert(a, term, unsafe, new Constant(a), Empty)) |
184 | val term = RSAOntology.genFreshVariable() | 189 | .unzip |
185 | val datalog = axioms | 190 | val facts = datalog._1.flatten |
186 | .map(a => RSAConverter.convert(a, term, unsafe, new Constant(a), Empty)) | 191 | val rules = datalog._2.flatten |
187 | .unzip | 192 | |
188 | val facts = datalog._1.flatten | 193 | //println("Datalog rules:") |
189 | val rules = datalog._2.flatten | 194 | //rules foreach println |
190 | 195 | ||
191 | /* DEBUG: print datalog rules */ | 196 | // Open connection with RDFox |
192 | //println("\nDatalog rules:") | 197 | val (server, data) = RDFoxUtil.openConnection("RSACheck") |
193 | //rules.foreach(println) | 198 | |
194 | 199 | /* Add built-in rules | |
195 | // Open connection with RDFox | 200 | * TODO: substitute with RDFoxUtil.addRules |
196 | val (server, data) = RDFoxUtil.openConnection("RSACheck") | 201 | */ |
197 | 202 | data.importData( | |
198 | /* Add built-in rules | 203 | UpdateType.ADDITION, |
199 | * TODO: substitute with RDFoxUtil.addRules | 204 | RSA.Prefixes, |
200 | */ | 205 | "rsa:E[?X,?Y] :- rsa:PE[?X,?Y], rsa:U[?X], rsa:U[?Y] ." |
201 | data.importData( | 206 | ) |
202 | UpdateType.ADDITION, | ||
203 | RSA.Prefixes, | ||
204 | "rsa:E[?X,?Y] :- rsa:PE[?X,?Y], rsa:U[?X], rsa:U[?Y] ." | ||
205 | ) | ||
206 | |||
207 | /* Add ontology facts and rules */ | ||
208 | RDFoxUtil.addFacts(data, facts) | ||
209 | RDFoxUtil.addRules(data, rules) | ||
210 | |||
211 | /* Build graph */ | ||
212 | val graph = this.rsaGraph(data); | ||
213 | //println(graph) | ||
214 | |||
215 | // Close connection to RDFox | ||
216 | RDFoxUtil.closeConnection(server, data) | ||
217 | 207 | ||
218 | /* To check if the graph is tree-like we check for acyclicity in a | 208 | /* Add ontology facts and rules */ |
219 | * undirected graph. | 209 | RDFoxUtil.addFacts(data, facts) |
220 | * | 210 | RDFoxUtil.addRules(data, rules) |
221 | * TODO: Implement additional checks (taking into account equality) | 211 | |
222 | */ | 212 | /* Build graph */ |
223 | graph.isAcyclic | 213 | val graph = this.rsaGraph(data); |
224 | } | 214 | //println("Graph:") |
215 | //println(graph) | ||
216 | |||
217 | // Close connection to RDFox | ||
218 | RDFoxUtil.closeConnection(server, data) | ||
219 | |||
220 | /* To check if the graph is tree-like we check for acyclicity in a | ||
221 | * undirected graph. | ||
222 | * | ||
223 | * TODO: Implement additional checks (taking into account equality) | ||
224 | */ | ||
225 | graph.isAcyclic | ||
226 | }, | ||
227 | "RSA check", | ||
228 | Logger.DEBUG | ||
229 | ) | ||
225 | 230 | ||
226 | lazy val unsafeRoles: List[OWLObjectPropertyExpression] = { | 231 | lazy val unsafeRoles: List[OWLObjectPropertyExpression] = { |
227 | 232 | ||
@@ -288,9 +293,17 @@ class RSAOntology(val ontology: OWLOntology) { | |||
288 | } | 293 | } |
289 | 294 | ||
290 | def filteringProgram(query: ConjunctiveQuery): FilteringProgram = | 295 | def filteringProgram(query: ConjunctiveQuery): FilteringProgram = |
291 | new FilteringProgram(query, individuals) | 296 | Logger.timed( |
297 | new FilteringProgram(query, individuals), | ||
298 | "Generating filtering program", | ||
299 | Logger.DEBUG | ||
300 | ) | ||
292 | 301 | ||
293 | lazy val canonicalModel = new CanonicalModel(this) | 302 | lazy val canonicalModel = Logger.timed( |
303 | new CanonicalModel(this), | ||
304 | "Generating canonical model program", | ||
305 | Logger.DEBUG | ||
306 | ) | ||
294 | 307 | ||
295 | // TODO: the following functions needs testing | 308 | // TODO: the following functions needs testing |
296 | def confl( | 309 | def confl( |
@@ -321,27 +334,42 @@ class RSAOntology(val ontology: OWLOntology) { | |||
321 | * @param query query to execute | 334 | * @param query query to execute |
322 | * @return a collection of answers | 335 | * @return a collection of answers |
323 | */ | 336 | */ |
324 | def ask(query: ConjunctiveQuery): ConjunctiveQueryAnswers = { | 337 | def ask(query: ConjunctiveQuery): ConjunctiveQueryAnswers = Logger.timed( |
325 | import implicits.JavaCollections._ | 338 | { |
326 | val (server, data) = RDFoxUtil.openConnection(RSAOntology.DataStore) | 339 | import implicits.JavaCollections._ |
327 | val filter = this.filteringProgram(query) | 340 | val (server, data) = RDFoxUtil.openConnection(RSAOntology.DataStore) |
328 | RDFoxUtil.addRules(data, this.canonicalModel.rules) | 341 | val canon = this.canonicalModel |
329 | RDFoxUtil.addFacts(data, this.canonicalModel.facts) | 342 | val filter = this.filteringProgram(query) |
330 | RDFoxUtil.addRules(data, filter.rules) | 343 | |
331 | RDFoxUtil.addFacts(data, filter.facts) | 344 | Logger print s"Canonical model: ${canon.rules.length} rules" |
332 | val answers = RDFoxUtil | 345 | RDFoxUtil.addRules(data, this.canonicalModel.rules) |
333 | .submitQuery( | 346 | |
334 | data, | 347 | Logger print s"Canonical model: ${canon.facts.length} facts" |
335 | RDFoxUtil.buildDescriptionQuery("Ans", query.answer.size), | 348 | RDFoxUtil.addFacts(data, this.canonicalModel.facts) |
336 | RSA.Prefixes | 349 | |
337 | ) | 350 | RDFoxUtil printStatisticsFor data |
338 | .map( | 351 | |
339 | new ConjunctiveQueryAnswers(query.bcq, _) | 352 | Logger print s"Filtering program: ${filter.rules.length} rules" |
340 | ) | 353 | RDFoxUtil.addRules(data, filter.rules) |
341 | .get | 354 | |
342 | RDFoxUtil.closeConnection(server, data) | 355 | Logger print s"Filtering program: ${filter.facts.length} facts" |
343 | answers | 356 | RDFoxUtil.addFacts(data, filter.facts) |
344 | } | 357 | |
358 | RDFoxUtil printStatistics data | ||
359 | |||
360 | val answers = { | ||
361 | val ans = RDFoxUtil.buildDescriptionQuery("Ans", query.answer.size) | ||
362 | RDFoxUtil | ||
363 | .submitQuery(data, ans, RSA.Prefixes) | ||
364 | .map(new ConjunctiveQueryAnswers(query.bcq, query.variables, _)) | ||
365 | .get | ||
366 | } | ||
367 | RDFoxUtil.closeConnection(server, data) | ||
368 | answers | ||
369 | }, | ||
370 | "Answers computation", | ||
371 | Logger.DEBUG | ||
372 | ) | ||
345 | 373 | ||
346 | /** Query the RDFox data store used for query answering. | 374 | /** Query the RDFox data store used for query answering. |
347 | * | 375 | * |
diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/util/Logger.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/util/Logger.scala new file mode 100644 index 0000000..74797a2 --- /dev/null +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/util/Logger.scala | |||
@@ -0,0 +1,45 @@ | |||
1 | package uk.ac.ox.cs.rsacomb.util | ||
2 | |||
3 | import java.util.Calendar | ||
4 | import java.io.PrintStream | ||
5 | |||
6 | /** Rough implementation of a logger. | ||
7 | * | ||
8 | * This is a WIP class for debugging and benchmarking. | ||
9 | */ | ||
10 | object Logger { | ||
11 | |||
12 | /** Output stream for the logger. */ | ||
13 | var output: PrintStream = System.out | ||
14 | |||
15 | /** Logger levels (i.e., verbosity of output) */ | ||
16 | sealed abstract class Level(val level: Int, val name: String) | ||
17 | extends Ordered[Level] { | ||
18 | def compare(that: Level) = this.level - that.level | ||
19 | override def toString = name | ||
20 | } | ||
21 | case object QUIET extends Level(0, "normal") | ||
22 | case object NORMAL extends Level(1, "normal") | ||
23 | case object DEBUG extends Level(2, "debug") | ||
24 | case object VERBOSE extends Level(3, "verbose") | ||
25 | |||
26 | /** Currend logger level */ | ||
27 | var level: Level = DEBUG | ||
28 | |||
29 | def print(str: Any, lvl: Level = NORMAL): Unit = { | ||
30 | if (lvl <= level) { | ||
31 | val time = Calendar.getInstance.getTime | ||
32 | output println s"[$lvl][$time] $str" | ||
33 | } | ||
34 | } | ||
35 | |||
36 | def timed[A](expr: => A, desc: String = "", lvl: Level = NORMAL): A = { | ||
37 | val t0 = System.currentTimeMillis() | ||
38 | print(s"$desc (START)", lvl) | ||
39 | val result = expr | ||
40 | val t1 = System.currentTimeMillis() | ||
41 | print(s"$desc (END): ${(t1 - t0).toFloat / 1000}s", lvl) | ||
42 | result | ||
43 | } | ||
44 | |||
45 | } | ||
diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/util/RDFoxUtil.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/util/RDFoxUtil.scala index 9dd52cf..51ef903 100644 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/util/RDFoxUtil.scala +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/util/RDFoxUtil.scala | |||
@@ -1,5 +1,6 @@ | |||
1 | package uk.ac.ox.cs.rsacomb.util | 1 | package uk.ac.ox.cs.rsacomb.util |
2 | 2 | ||
3 | import java.io.File | ||
3 | import java.io.StringReader | 4 | import java.io.StringReader |
4 | import tech.oxfordsemantic.jrdfox.Prefixes | 5 | import tech.oxfordsemantic.jrdfox.Prefixes |
5 | import tech.oxfordsemantic.jrdfox.client.{ | 6 | import tech.oxfordsemantic.jrdfox.client.{ |
@@ -20,6 +21,7 @@ import tech.oxfordsemantic.jrdfox.logic.datalog.{ | |||
20 | import tech.oxfordsemantic.jrdfox.logic.expression.{Resource} | 21 | import tech.oxfordsemantic.jrdfox.logic.expression.{Resource} |
21 | import tech.oxfordsemantic.jrdfox.logic.sparql.statement.SelectQuery | 22 | import tech.oxfordsemantic.jrdfox.logic.sparql.statement.SelectQuery |
22 | import uk.ac.ox.cs.rsacomb.suffix.Nth | 23 | import uk.ac.ox.cs.rsacomb.suffix.Nth |
24 | import uk.ac.ox.cs.rsacomb.util.Logger | ||
23 | 25 | ||
24 | /** A collection of helper methods for RDFox */ | 26 | /** A collection of helper methods for RDFox */ |
25 | object RDFoxUtil { | 27 | object RDFoxUtil { |
@@ -67,18 +69,21 @@ object RDFoxUtil { | |||
67 | (server, data) | 69 | (server, data) |
68 | } | 70 | } |
69 | 71 | ||
70 | /** Gather statistics from RDFox datastore. | 72 | /** Prints statistics from RDFox datastore. |
73 | * | ||
74 | * Prints something only when Logger level is set to DEBUG or more. | ||
71 | * | 75 | * |
72 | * @see [[https://docs.oxfordsemantic.tech/programmatic-access-APIs.html#in-depth-diagnostic-information]] | 76 | * @see [[https://docs.oxfordsemantic.tech/programmatic-access-APIs.html#in-depth-diagnostic-information]] |
73 | * and [[https://docs.oxfordsemantic.tech/programmatic-access-APIs.html#managing-statistics]] | 77 | * and [[https://docs.oxfordsemantic.tech/programmatic-access-APIs.html#managing-statistics]] |
74 | * for more ways of gathering diagnostics from RDFox. | 78 | * for more ways of gathering diagnostics from RDFox. |
75 | */ | 79 | */ |
76 | def gatherStatistics(data: DataStoreConnection): String = { | 80 | def printStatisticsFor(data: DataStoreConnection): Unit = { |
77 | val info = data.getComponentInfo(true) | 81 | val info = data.getComponentInfo(true) |
78 | s"${info.getName}: ${info.getPropertyValues}" | 82 | val stats = s"${info.getName}: ${info.getPropertyValues}" |
79 | .replaceAll("\\{", "{\n ") | 83 | .replaceAll("\\{", "{\n ") |
80 | .replaceAll(", ", ",\n ") | 84 | .replaceAll(", ", ",\n ") |
81 | .replaceAll("\\}", "\n}") | 85 | .replaceAll("\\}", "\n}") |
86 | Logger.print(stats, Logger.DEBUG) | ||
82 | } | 87 | } |
83 | 88 | ||
84 | /** Adds a collection of rules to a data store. | 89 | /** Adds a collection of rules to a data store. |
@@ -87,7 +92,11 @@ object RDFoxUtil { | |||
87 | * @param rules collection of rules to be added to the data store | 92 | * @param rules collection of rules to be added to the data store |
88 | */ | 93 | */ |
89 | def addRules(data: DataStoreConnection, rules: Seq[Rule]): Unit = | 94 | def addRules(data: DataStoreConnection, rules: Seq[Rule]): Unit = |
90 | data addRules rules | 95 | Logger.timed( |
96 | data addRules rules, | ||
97 | "Loading rules", | ||
98 | Logger.DEBUG | ||
99 | ) | ||
91 | 100 | ||
92 | /** Adds a collection of facts to a data store. | 101 | /** Adds a collection of facts to a data store. |
93 | * | 102 | * |
@@ -95,12 +104,25 @@ object RDFoxUtil { | |||
95 | * @param facts collection of facts to be added to the data store | 104 | * @param facts collection of facts to be added to the data store |
96 | */ | 105 | */ |
97 | def addFacts(data: DataStoreConnection, facts: Seq[TupleTableAtom]): Unit = | 106 | def addFacts(data: DataStoreConnection, facts: Seq[TupleTableAtom]): Unit = |
98 | data.importData( | 107 | Logger.timed( |
99 | UpdateType.ADDITION, | 108 | data.importData( |
100 | RSA.Prefixes, | 109 | UpdateType.ADDITION, |
101 | facts.map(_.toString(Prefixes.s_emptyPrefixes)).mkString("", ".\n", ".") | 110 | RSA.Prefixes, |
111 | facts.map(_.toString(Prefixes.s_emptyPrefixes)).mkString("", ".\n", ".") | ||
112 | ), | ||
113 | "Loading facts", | ||
114 | Logger.DEBUG | ||
102 | ) | 115 | ) |
103 | 116 | ||
117 | /** Load SPARQL query from file. */ | ||
118 | def loadQueryFromFile(file: File): String = { | ||
119 | val source = io.Source.fromFile(file) | ||
120 | val query = source.getLines mkString "\n" | ||
121 | Logger print s"Loaded query:\n$query" | ||
122 | source.close() | ||
123 | query | ||
124 | } | ||
125 | |||
104 | /** Parse a SELECT query from a string in SPARQL format. | 126 | /** Parse a SELECT query from a string in SPARQL format. |
105 | * | 127 | * |
106 | * @param query the string containing the SPARQL query | 128 | * @param query the string containing the SPARQL query |
@@ -136,19 +158,23 @@ object RDFoxUtil { | |||
136 | data: DataStoreConnection, | 158 | data: DataStoreConnection, |
137 | query: SelectQuery, | 159 | query: SelectQuery, |
138 | opts: RDFoxOpts = RDFoxOpts() | 160 | opts: RDFoxOpts = RDFoxOpts() |
139 | ): QueryAnswers = { | 161 | ): QueryAnswers = Logger.timed( |
140 | val cursor = data.createCursor(query, opts) | 162 | { |
141 | var answers = QueryAnswers() | 163 | val cursor = data.createCursor(query, opts) |
142 | var mul = cursor.open() | 164 | var answers = QueryAnswers() |
143 | while (mul > 0) { | 165 | var mul = cursor.open() |
144 | val answer = | 166 | while (mul > 0) { |
145 | (0 until cursor.getArity).map(cursor.getResource(_)).toList | 167 | val answer = |
146 | answers = answer :: answers | 168 | (0 until cursor.getArity).map(cursor.getResource(_)).toList |
147 | mul = cursor.advance() | 169 | answers = answer :: answers |
148 | } | 170 | mul = cursor.advance() |
149 | cursor.close(); | 171 | } |
150 | answers | 172 | cursor.close(); |
151 | } | 173 | answers |
174 | }, | ||
175 | "Answer query", | ||
176 | Logger.DEBUG | ||
177 | ) | ||
152 | 178 | ||
153 | /** Execute a query over a given datastore connection. | 179 | /** Execute a query over a given datastore connection. |
154 | * | 180 | * |