aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala34
-rw-r--r--src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala248
-rw-r--r--src/main/scala/uk/ac/ox/cs/rsacomb/sparql/ConjunctiveQueryAnswers.scala3
-rw-r--r--src/main/scala/uk/ac/ox/cs/rsacomb/util/Logger.scala45
-rw-r--r--src/main/scala/uk/ac/ox/cs/rsacomb/util/RDFoxUtil.scala72
5 files changed, 272 insertions, 130 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 c7ace0f..6891c8c 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
10import tech.oxfordsemantic.jrdfox.logic.expression.{IRI, Term} 10import tech.oxfordsemantic.jrdfox.logic.expression.{IRI, Term}
11 11
12/* Local imports */ 12/* Local imports */
13import util.{RDFoxUtil, RSA} 13import util.{Logger, RDFoxUtil, RSA}
14import sparql.ConjunctiveQuery 14import sparql.ConjunctiveQuery
15 15
16object RSAComb extends App { 16object RSAComb extends App {
@@ -53,15 +53,33 @@ 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 strQuery = RDFoxUtil.loadQueryFromFile(queryPath.getAbsoluteFile)
60 val query = source.getLines mkString "\n" 61 val query = ConjunctiveQuery parse strQuery
61 source.close() 62
63 query match {
64 case Some(query) => {
65 val answers = ontology ask query
66 Logger.print(s"$answers", Logger.QUIET)
67 Logger print s"Number of answer: ${answers.length}"
68
69 val unfiltered = ontology askUnfiltered query
70 val percentage = unfiltered match {
71 case Some(u) =>
72 if (u.length > 0) (1 - answers.length / u.length) * 100 else 0
73 case None => 0
74 }
75 Logger.print(
76 s"Percentage of spurious answers: $percentage%",
77 Logger.DEBUG
78 )
79 }
80 case None =>
81 throw new RuntimeException("Submitted query is not conjunctive")
82 }
62 83
63 /* Compute answers to query */
64 val answers = ConjunctiveQuery.parse(query).map(ontology ask _)
65 answers map (_.toString) foreach println
66 } 84 }
67} 85}
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 0fb6c96..dc64c79 100644
--- a/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala
+++ b/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala
@@ -56,6 +56,7 @@ import uk.ac.ox.cs.rsacomb.converter._
56import uk.ac.ox.cs.rsacomb.suffix._ 56import uk.ac.ox.cs.rsacomb.suffix._
57import uk.ac.ox.cs.rsacomb.sparql._ 57import uk.ac.ox.cs.rsacomb.sparql._
58import uk.ac.ox.cs.rsacomb.util.{RDFoxUtil, RSA} 58import uk.ac.ox.cs.rsacomb.util.{RDFoxUtil, RSA}
59import uk.ac.ox.cs.rsacomb.util.Logger
59 60
60object RSAOntology { 61object RSAOntology {
61 62
@@ -109,6 +110,10 @@ class RSAOntology(val ontology: OWLOntology) {
109 110
110 val axioms: List[OWLLogicalAxiom] = abox ::: tbox ::: rbox 111 val axioms: List[OWLLogicalAxiom] = abox ::: tbox ::: rbox
111 112
113 Logger.print(s"Original TBox: ${tbox.length} axioms", Logger.DEBUG)
114 Logger.print(s"Original RBox: ${rbox.length} axioms", Logger.DEBUG)
115 Logger.print(s"Original ABox: ${abox.length} axioms", Logger.DEBUG)
116
112 /* Retrieve individuals in the original ontology 117 /* Retrieve individuals in the original ontology
113 */ 118 */
114 val individuals: List[IRI] = 119 val individuals: List[IRI] =
@@ -149,87 +154,91 @@ class RSAOntology(val ontology: OWLOntology) {
149 * why the ontology might not be RSA. This could help a second 154 * why the ontology might not be RSA. This could help a second
150 * step of approximation of an Horn-ALCHOIQ to RSA 155 * step of approximation of an Horn-ALCHOIQ to RSA
151 */ 156 */
152 lazy val isRSA: Boolean = { 157 lazy val isRSA: Boolean = Logger.timed(
153 158 {
154 val unsafe = this.unsafeRoles 159 val unsafe = this.unsafeRoles
155 160
156 /* DEBUG: print rules in DL syntax and unsafe roles */ 161 // val renderer = new DLSyntaxObjectRenderer()
157 //val renderer = new DLSyntaxObjectRenderer() 162 // println()
158 //println("\nDL rules:") 163 // println("Unsafe roles:")
159 //axioms.foreach(x => println(renderer.render(x))) 164 // println(unsafe)
160 //println("\nUnsafe roles:") 165 // println()
161 //println(unsafe) 166 // println("DL rules:")
162 167 // tbox.foreach(x => println(renderer.render(x)))
163 object RSAConverter extends RDFoxConverter { 168
164 169 object RSAConverter extends RDFoxConverter {
165 override def convert( 170
166 expr: OWLClassExpression, 171 override def convert(
167 term: Term, 172 expr: OWLClassExpression,
168 unsafe: List[OWLObjectPropertyExpression], 173 term: Term,
169 skolem: SkolemStrategy, 174 unsafe: List[OWLObjectPropertyExpression],
170 suffix: RSASuffix 175 skolem: SkolemStrategy,
171 ): Shards = 176 suffix: RSASuffix
172 (expr, skolem) match { 177 ): Shards =
173 178 (expr, skolem) match {
174 case (e: OWLObjectSomeValuesFrom, c: Constant) 179
175 if unsafe contains e.getProperty => { 180 case (e: OWLObjectSomeValuesFrom, c: Constant)
176 val (res, ext) = super.convert(e, term, unsafe, skolem, suffix) 181 if unsafe contains e.getProperty => {
177 (RSA.PE(term, c.iri) :: RSA.U(c.iri) :: res, ext) 182 val (res, ext) = super.convert(e, term, unsafe, skolem, suffix)
183 (RSA.PE(term, c.iri) :: RSA.U(c.iri) :: res, ext)
184 }
185
186 case (e: OWLDataSomeValuesFrom, c: Constant)
187 if unsafe contains e.getProperty => {
188 val (res, ext) = super.convert(e, term, unsafe, skolem, suffix)
189 (RSA.PE(term, c.iri) :: RSA.U(c.iri) :: res, ext)
190 }
191
192 case _ => super.convert(expr, term, unsafe, skolem, suffix)
178 } 193 }
179 194
180 case (e: OWLDataSomeValuesFrom, c: Constant) 195 }
181 if unsafe contains e.getProperty => { 196
182 val (res, ext) = super.convert(e, term, unsafe, skolem, suffix) 197 /* Ontology convertion into LP rules */
183 (RSA.PE(term, c.iri) :: RSA.U(c.iri) :: res, ext) 198 val term = RSAOntology.genFreshVariable()
184 } 199 val datalog = axioms
185 200 .map(a => RSAConverter.convert(a, term, unsafe, new Constant(a), Empty))
186 case _ => super.convert(expr, term, unsafe, skolem, suffix) 201 .unzip
187 } 202 val facts = datalog._1.flatten
188 203 val rules = datalog._2.flatten
189 } 204
190 205 //println("Datalog rules:")
191 /* Ontology convertion into LP rules */ 206 //rules foreach println
192 val term = RSAOntology.genFreshVariable() 207
193 val datalog = axioms 208 // Open connection with RDFox
194 .map(a => RSAConverter.convert(a, term, unsafe, new Constant(a), Empty)) 209 val (server, data) = RDFoxUtil.openConnection("RSACheck")
195 .unzip 210
196 val facts = datalog._1.flatten 211 /* Add built-in rules
197 val rules = datalog._2.flatten 212 * TODO: substitute with RDFoxUtil.addRules
198 213 */
199 /* DEBUG: print datalog rules */ 214 data.importData(
200 //println("\nDatalog rules:") 215 UpdateType.ADDITION,
201 //rules.foreach(println) 216 RSA.Prefixes,
202 217 "rsa:E[?X,?Y] :- rsa:PE[?X,?Y], rsa:U[?X], rsa:U[?Y] ."
203 // Open connection with RDFox 218 )
204 val (server, data) = RDFoxUtil.openConnection("RSACheck")
205
206 /* Add built-in rules
207 * TODO: substitute with RDFoxUtil.addRules
208 */
209 data.importData(
210 UpdateType.ADDITION,
211 RSA.Prefixes,
212 "rsa:E[?X,?Y] :- rsa:PE[?X,?Y], rsa:U[?X], rsa:U[?Y] ."
213 )
214
215 /* Add ontology facts and rules */
216 RDFoxUtil.addFacts(data, facts)
217 RDFoxUtil.addRules(data, rules)
218
219 /* Build graph */
220 val graph = this.rsaGraph(data);
221 //println(graph)
222
223 // Close connection to RDFox
224 RDFoxUtil.closeConnection(server, data)
225 219
226 /* To check if the graph is tree-like we check for acyclicity in a 220 /* Add ontology facts and rules */
227 * undirected graph. 221 RDFoxUtil.addFacts(data, facts)
228 * 222 RDFoxUtil.addRules(data, rules)
229 * TODO: Implement additional checks (taking into account equality) 223
230 */ 224 /* Build graph */
231 graph.isAcyclic 225 val graph = this.rsaGraph(data);
232 } 226 //println("Graph:")
227 //println(graph)
228
229 // Close connection to RDFox
230 RDFoxUtil.closeConnection(server, data)
231
232 /* To check if the graph is tree-like we check for acyclicity in a
233 * undirected graph.
234 *
235 * TODO: Implement additional checks (taking into account equality)
236 */
237 graph.isAcyclic
238 },
239 "RSA check",
240 Logger.DEBUG
241 )
233 242
234 lazy val unsafeRoles: List[OWLObjectPropertyExpression] = { 243 lazy val unsafeRoles: List[OWLObjectPropertyExpression] = {
235 244
@@ -296,9 +305,17 @@ class RSAOntology(val ontology: OWLOntology) {
296 } 305 }
297 306
298 def filteringProgram(query: ConjunctiveQuery): FilteringProgram = 307 def filteringProgram(query: ConjunctiveQuery): FilteringProgram =
299 new FilteringProgram(query, individuals ++ literals) 308 Logger.timed(
309 new FilteringProgram(query, individuals ++ literals),
310 "Generating filtering program",
311 Logger.DEBUG
312 )
300 313
301 lazy val canonicalModel = new CanonicalModel(this) 314 lazy val canonicalModel = Logger.timed(
315 new CanonicalModel(this),
316 "Generating canonical model program",
317 Logger.DEBUG
318 )
302 319
303 // TODO: the following functions needs testing 320 // TODO: the following functions needs testing
304 def confl( 321 def confl(
@@ -329,27 +346,42 @@ class RSAOntology(val ontology: OWLOntology) {
329 * @param query query to execute 346 * @param query query to execute
330 * @return a collection of answers 347 * @return a collection of answers
331 */ 348 */
332 def ask(query: ConjunctiveQuery): ConjunctiveQueryAnswers = { 349 def ask(query: ConjunctiveQuery): ConjunctiveQueryAnswers = Logger.timed(
333 import implicits.JavaCollections._ 350 {
334 val (server, data) = RDFoxUtil.openConnection(RSAOntology.DataStore) 351 import implicits.JavaCollections._
335 val filter = this.filteringProgram(query) 352 val (server, data) = RDFoxUtil.openConnection(RSAOntology.DataStore)
336 RDFoxUtil.addRules(data, this.canonicalModel.rules) 353 val canon = this.canonicalModel
337 RDFoxUtil.addFacts(data, this.canonicalModel.facts) 354 val filter = this.filteringProgram(query)
338 RDFoxUtil.addRules(data, filter.rules) 355
339 RDFoxUtil.addFacts(data, filter.facts) 356 Logger print s"Canonical model: ${canon.rules.length} rules"
340 val answers = RDFoxUtil 357 RDFoxUtil.addRules(data, this.canonicalModel.rules)
341 .submitQuery( 358
342 data, 359 Logger print s"Canonical model: ${canon.facts.length} facts"
343 RDFoxUtil.buildDescriptionQuery("Ans", query.answer.size), 360 RDFoxUtil.addFacts(data, this.canonicalModel.facts)
344 RSA.Prefixes 361
345 ) 362 RDFoxUtil printStatisticsFor data
346 .map( 363
347 new ConjunctiveQueryAnswers(query.bcq, query.variables, _) 364 Logger print s"Filtering program: ${filter.rules.length} rules"
348 ) 365 RDFoxUtil.addRules(data, filter.rules)
349 .get 366
350 RDFoxUtil.closeConnection(server, data) 367 Logger print s"Filtering program: ${filter.facts.length} facts"
351 answers 368 RDFoxUtil.addFacts(data, filter.facts)
352 } 369
370 RDFoxUtil printStatisticsFor data
371
372 val answers = {
373 val ans = RDFoxUtil.buildDescriptionQuery("Ans", query.answer.size)
374 RDFoxUtil
375 .submitQuery(data, ans, RSA.Prefixes)
376 .map(new ConjunctiveQueryAnswers(query.bcq, query.variables, _))
377 .get
378 }
379 RDFoxUtil.closeConnection(server, data)
380 answers
381 },
382 "Answers computation",
383 Logger.DEBUG
384 )
353 385
354 /** Query the RDFox data store used for query answering. 386 /** Query the RDFox data store used for query answering.
355 * 387 *
@@ -377,6 +409,20 @@ class RSAOntology(val ontology: OWLOntology) {
377 answers 409 answers
378 } 410 }
379 411
412 /** Returns set of unfiltered answers.
413 *
414 * This is equivalent to quering just the canonical model.
415 *
416 * @note this method does not load any data to RDFox. The return
417 * value is considered well defined only after
418 * [[uk.ac.ox.cs.rsacomb.RSAOntology.ask RSAOntology.ask]]
419 * for the corresponding query has been called.
420 */
421 def askUnfiltered(cq: ConjunctiveQuery): Option[Seq[Seq[Resource]]] = {
422 val query = RDFoxUtil.buildDescriptionQuery("QM", cq.variables.length)
423 queryDataStore(cq, query, RSA.Prefixes)
424 }
425
380 def self(axiom: OWLSubClassOfAxiom): Set[Term] = { 426 def self(axiom: OWLSubClassOfAxiom): Set[Term] = {
381 // Assuming just one role in the signature of a T5 axiom 427 // Assuming just one role in the signature of a T5 axiom
382 val role = axiom.objectPropertyExpressionsInSignature(0) 428 val role = axiom.objectPropertyExpressionsInSignature(0)
diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/sparql/ConjunctiveQueryAnswers.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/sparql/ConjunctiveQueryAnswers.scala
index 3db5500..667defc 100644
--- a/src/main/scala/uk/ac/ox/cs/rsacomb/sparql/ConjunctiveQueryAnswers.scala
+++ b/src/main/scala/uk/ac/ox/cs/rsacomb/sparql/ConjunctiveQueryAnswers.scala
@@ -22,6 +22,9 @@ class ConjunctiveQueryAnswers(
22 val answers: Seq[Seq[Resource]] 22 val answers: Seq[Seq[Resource]]
23) { 23) {
24 24
25 /** Returns number of answers. */
26 val length: Int = if (bcq) 0 else answers.length
27
25 override def toString(): String = 28 override def toString(): String =
26 if (bcq) { 29 if (bcq) {
27 if (answers.isEmpty) "FALSE" else "TRUE" 30 if (answers.isEmpty) "FALSE" else "TRUE"
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 @@
1package uk.ac.ox.cs.rsacomb.util
2
3import java.util.Calendar
4import java.io.PrintStream
5
6/** Rough implementation of a logger.
7 *
8 * This is a WIP class for debugging and benchmarking.
9 */
10object 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..31cc850 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 @@
1package uk.ac.ox.cs.rsacomb.util 1package uk.ac.ox.cs.rsacomb.util
2 2
3import java.io.File
3import java.io.StringReader 4import java.io.StringReader
4import tech.oxfordsemantic.jrdfox.Prefixes 5import tech.oxfordsemantic.jrdfox.Prefixes
5import tech.oxfordsemantic.jrdfox.client.{ 6import tech.oxfordsemantic.jrdfox.client.{
@@ -20,6 +21,7 @@ import tech.oxfordsemantic.jrdfox.logic.datalog.{
20import tech.oxfordsemantic.jrdfox.logic.expression.{Resource} 21import tech.oxfordsemantic.jrdfox.logic.expression.{Resource}
21import tech.oxfordsemantic.jrdfox.logic.sparql.statement.SelectQuery 22import tech.oxfordsemantic.jrdfox.logic.sparql.statement.SelectQuery
22import uk.ac.ox.cs.rsacomb.suffix.Nth 23import uk.ac.ox.cs.rsacomb.suffix.Nth
24import 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 */
25object RDFoxUtil { 27object 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,29 @@ 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 /** Force materialization in RDFox. */
118 def materialize(data: DataStoreConnection): Unit =
119 Logger.timed(data.updateMaterialization(), "Materialization", Logger.DEBUG)
120
121 /** Load SPARQL query from file. */
122 def loadQueryFromFile(file: File): String = {
123 val source = io.Source.fromFile(file)
124 val query = source.getLines mkString "\n"
125 Logger print s"Loaded query:\n$query"
126 source.close()
127 query
128 }
129
104 /** Parse a SELECT query from a string in SPARQL format. 130 /** Parse a SELECT query from a string in SPARQL format.
105 * 131 *
106 * @param query the string containing the SPARQL query 132 * @param query the string containing the SPARQL query
@@ -136,19 +162,23 @@ object RDFoxUtil {
136 data: DataStoreConnection, 162 data: DataStoreConnection,
137 query: SelectQuery, 163 query: SelectQuery,
138 opts: RDFoxOpts = RDFoxOpts() 164 opts: RDFoxOpts = RDFoxOpts()
139 ): QueryAnswers = { 165 ): QueryAnswers = Logger.timed(
140 val cursor = data.createCursor(query, opts) 166 {
141 var answers = QueryAnswers() 167 val cursor = data.createCursor(query, opts)
142 var mul = cursor.open() 168 var answers = QueryAnswers()
143 while (mul > 0) { 169 var mul = cursor.open()
144 val answer = 170 while (mul > 0) {
145 (0 until cursor.getArity).map(cursor.getResource(_)).toList 171 val answer =
146 answers = answer :: answers 172 (0 until cursor.getArity).map(cursor.getResource(_)).toList
147 mul = cursor.advance() 173 answers = answer :: answers
148 } 174 mul = cursor.advance()
149 cursor.close(); 175 }
150 answers 176 cursor.close();
151 } 177 answers
178 },
179 "Answer query",
180 Logger.DEBUG
181 )
152 182
153 /** Execute a query over a given datastore connection. 183 /** Execute a query over a given datastore connection.
154 * 184 *