aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFederico Igne <federico.igne@cs.ox.ac.uk>2020-12-09 11:20:37 +0000
committerFederico Igne <federico.igne@cs.ox.ac.uk>2020-12-09 11:20:37 +0000
commit2ec1731da852ee17432c909ed2864f40449c2665 (patch)
treecefb51f4c5f835cf6f19268971af16842f2fe152
parent8af52541d00dc576bac20f9366be979a94d542c1 (diff)
parent43327d8a986e41ba26b6f2b5c911646c7c9a254b (diff)
downloadRSAComb-2ec1731da852ee17432c909ed2864f40449c2665.tar.gz
RSAComb-2ec1731da852ee17432c909ed2864f40449c2665.zip
Merge branch 'logger' into master
-rw-r--r--src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala39
-rw-r--r--src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala257
-rw-r--r--src/main/scala/uk/ac/ox/cs/rsacomb/sparql/ConjunctiveQueryAnswers.scala22
-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.scala78
5 files changed, 298 insertions, 143 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..eaacedc 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,38 @@ 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 Logger.print(
73 s"Number of spurious answers: ${u.length}.",
74 Logger.DEBUG
75 )
76 if (u.length > 0) (1 - answers.length / u.length) * 100 else 0
77 }
78 case None => 0
79 }
80 Logger.print(
81 s"Percentage of spurious answers: $percentage%",
82 Logger.DEBUG
83 )
84 }
85 case None =>
86 throw new RuntimeException("Submitted query is not conjunctive")
87 }
62 88
63 /* Compute answers to query */
64 val answers = ConjunctiveQuery.parse(query).map(ontology ask _)
65 answers map (_.toString) foreach println
66 } 89 }
67} 90}
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..0f1cd5e 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
@@ -289,16 +298,23 @@ class RSAOntology(val ontology: OWLOntology) {
289 ): Graph[Resource, UnDiEdge] = { 298 ): Graph[Resource, UnDiEdge] = {
290 val query = "SELECT ?X ?Y WHERE { ?X rsa:E ?Y }" 299 val query = "SELECT ?X ?Y WHERE { ?X rsa:E ?Y }"
291 val answers = RDFoxUtil.submitQuery(data, query, RSA.Prefixes).get 300 val answers = RDFoxUtil.submitQuery(data, query, RSA.Prefixes).get
292 var edges: Seq[UnDiEdge[Resource]] = answers.map { case Seq(n1, n2) => 301 var edges: Seq[UnDiEdge[Resource]] =
293 UnDiEdge(n1, n2) 302 answers.collect { case (_, Seq(n1, n2)) => UnDiEdge(n1, n2) }
294 }
295 Graph(edges: _*) 303 Graph(edges: _*)
296 } 304 }
297 305
298 def filteringProgram(query: ConjunctiveQuery): FilteringProgram = 306 def filteringProgram(query: ConjunctiveQuery): FilteringProgram =
299 new FilteringProgram(query, individuals ++ literals) 307 Logger.timed(
308 new FilteringProgram(query, individuals ++ literals),
309 "Generating filtering program",
310 Logger.DEBUG
311 )
300 312
301 lazy val canonicalModel = new CanonicalModel(this) 313 lazy val canonicalModel = Logger.timed(
314 new CanonicalModel(this),
315 "Generating canonical model program",
316 Logger.DEBUG
317 )
302 318
303 // TODO: the following functions needs testing 319 // TODO: the following functions needs testing
304 def confl( 320 def confl(
@@ -329,27 +345,42 @@ class RSAOntology(val ontology: OWLOntology) {
329 * @param query query to execute 345 * @param query query to execute
330 * @return a collection of answers 346 * @return a collection of answers
331 */ 347 */
332 def ask(query: ConjunctiveQuery): ConjunctiveQueryAnswers = { 348 def ask(query: ConjunctiveQuery): ConjunctiveQueryAnswers = Logger.timed(
333 import implicits.JavaCollections._ 349 {
334 val (server, data) = RDFoxUtil.openConnection(RSAOntology.DataStore) 350 import implicits.JavaCollections._
335 val filter = this.filteringProgram(query) 351 val (server, data) = RDFoxUtil.openConnection(RSAOntology.DataStore)
336 RDFoxUtil.addRules(data, this.canonicalModel.rules) 352 val canon = this.canonicalModel
337 RDFoxUtil.addFacts(data, this.canonicalModel.facts) 353 val filter = this.filteringProgram(query)
338 RDFoxUtil.addRules(data, filter.rules) 354
339 RDFoxUtil.addFacts(data, filter.facts) 355 Logger print s"Canonical model: ${canon.rules.length} rules"
340 val answers = RDFoxUtil 356 RDFoxUtil.addRules(data, this.canonicalModel.rules)
341 .submitQuery( 357
342 data, 358 Logger print s"Canonical model: ${canon.facts.length} facts"
343 RDFoxUtil.buildDescriptionQuery("Ans", query.answer.size), 359 RDFoxUtil.addFacts(data, this.canonicalModel.facts)
344 RSA.Prefixes 360
345 ) 361 RDFoxUtil printStatisticsFor data
346 .map( 362
347 new ConjunctiveQueryAnswers(query.bcq, query.variables, _) 363 Logger print s"Filtering program: ${filter.rules.length} rules"
348 ) 364 RDFoxUtil.addRules(data, filter.rules)
349 .get 365
350 RDFoxUtil.closeConnection(server, data) 366 Logger print s"Filtering program: ${filter.facts.length} facts"
351 answers 367 RDFoxUtil.addFacts(data, filter.facts)
352 } 368
369 RDFoxUtil printStatisticsFor data
370
371 val answers = {
372 val ans = RDFoxUtil.buildDescriptionQuery("Ans", query.answer.size)
373 RDFoxUtil
374 .submitQuery(data, ans, RSA.Prefixes)
375 .map(new ConjunctiveQueryAnswers(query.bcq, query.variables, _))
376 .get
377 }
378 RDFoxUtil.closeConnection(server, data)
379 answers
380 },
381 "Answers computation",
382 Logger.DEBUG
383 )
353 384
354 /** Query the RDFox data store used for query answering. 385 /** Query the RDFox data store used for query answering.
355 * 386 *
@@ -370,13 +401,29 @@ class RSAOntology(val ontology: OWLOntology) {
370 query: String, 401 query: String,
371 prefixes: Prefixes = new Prefixes(), 402 prefixes: Prefixes = new Prefixes(),
372 opts: ju.Map[String, String] = new ju.HashMap[String, String]() 403 opts: ju.Map[String, String] = new ju.HashMap[String, String]()
373 ): Option[Seq[Seq[Resource]]] = { 404 ): Option[Seq[(Long, Seq[Resource])]] = {
374 val (server, data) = RDFoxUtil.openConnection(RSAOntology.DataStore) 405 val (server, data) = RDFoxUtil.openConnection(RSAOntology.DataStore)
375 val answers = RDFoxUtil.submitQuery(data, query, prefixes, opts) 406 val answers = RDFoxUtil.submitQuery(data, query, prefixes, opts)
376 RDFoxUtil.closeConnection(server, data) 407 RDFoxUtil.closeConnection(server, data)
377 answers 408 answers
378 } 409 }
379 410
411 /** Returns set of unfiltered answers.
412 *
413 * This is equivalent to quering just the canonical model.
414 *
415 * @note this method does not load any data to RDFox. The return
416 * value is considered well defined only after
417 * [[uk.ac.ox.cs.rsacomb.RSAOntology.ask RSAOntology.ask]]
418 * for the corresponding query has been called.
419 */
420 def askUnfiltered(
421 cq: ConjunctiveQuery
422 ): Option[Seq[(Long, Seq[Resource])]] = {
423 val query = RDFoxUtil.buildDescriptionQuery("QM", cq.variables.length)
424 queryDataStore(cq, query, RSA.Prefixes)
425 }
426
380 def self(axiom: OWLSubClassOfAxiom): Set[Term] = { 427 def self(axiom: OWLSubClassOfAxiom): Set[Term] = {
381 // Assuming just one role in the signature of a T5 axiom 428 // Assuming just one role in the signature of a T5 axiom
382 val role = axiom.objectPropertyExpressionsInSignature(0) 429 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..7edc867 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
@@ -19,9 +19,15 @@ import tech.oxfordsemantic.jrdfox.logic.expression.{
19class ConjunctiveQueryAnswers( 19class ConjunctiveQueryAnswers(
20 bcq: Boolean, 20 bcq: Boolean,
21 val variables: Seq[Variable], 21 val variables: Seq[Variable],
22 val answers: Seq[Seq[Resource]] 22 val answers: Seq[(Long, Seq[Resource])]
23) { 23) {
24 24
25 /** Returns number of distinct answers. */
26 val length: Int = if (bcq) 0 else answers.length
27
28 /** Returns number of answers taking into account multiplicity. */
29 val lengthWithMultiplicity: Long = answers.map(_._1).sum
30
25 override def toString(): String = 31 override def toString(): String =
26 if (bcq) { 32 if (bcq) {
27 if (answers.isEmpty) "FALSE" else "TRUE" 33 if (answers.isEmpty) "FALSE" else "TRUE"
@@ -31,11 +37,15 @@ class ConjunctiveQueryAnswers(
31 else { 37 else {
32 val header = variables map (_.getName) mkString "\t" 38 val header = variables map (_.getName) mkString "\t"
33 val body = answers 39 val body = answers
34 .map(_.map { 40 .map(
35 case x: IRI => x.getIRI 41 _._2
36 case x: Literal => x.getLexicalForm 42 .map {
37 case x => x.toString 43 case x: IRI => x.getIRI
38 }.mkString("\t")) 44 case x: Literal => x.getLexicalForm
45 case x => x.toString
46 }
47 .mkString("\t")
48 )
39 .mkString("\n") 49 .mkString("\n")
40 s"$header\n$body" 50 s"$header\n$body"
41 } 51 }
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..76f720c 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 {
@@ -35,8 +37,8 @@ object RDFoxUtil {
35 /** Type alias for a collection of answers to a 37 /** Type alias for a collection of answers to a
36 * [[tech.oxfordsemantic.jrdfox.logic.sparql.statement.Query]]. 38 * [[tech.oxfordsemantic.jrdfox.logic.sparql.statement.Query]].
37 */ 39 */
38 private type QueryAnswers = Seq[Seq[Resource]] 40 private type QueryAnswers = Seq[(Long, Seq[Resource])]
39 private def QueryAnswers() = List.empty[Seq[Resource]] 41 private def QueryAnswers() = List.empty[(Long, Seq[Resource])]
40 42
41 /** Type alias for <option => value> RDFox options. */ 43 /** Type alias for <option => value> RDFox options. */
42 private type RDFoxOpts = java.util.Map[String, String] 44 private type RDFoxOpts = java.util.Map[String, String]
@@ -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 = (mul, 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 *
@@ -188,7 +218,7 @@ object RDFoxUtil {
188 .map(i => s"?S rsa:${pred :: Nth(i)} ?X$i .") 218 .map(i => s"?S rsa:${pred :: Nth(i)} ?X$i .")
189 .mkString("WHERE {\n", "\n", "\n}") 219 .mkString("WHERE {\n", "\n", "\n}")
190 } else { 220 } else {
191 s"ASK { ?X a rsa:$pred }" 221 s"ASK { ?X a rsa:${pred :: Nth(0)} }"
192 } 222 }
193 } 223 }
194 224