diff options
author | Federico Igne <federico.igne@cs.ox.ac.uk> | 2020-11-20 13:10:44 +0000 |
---|---|---|
committer | Federico Igne <federico.igne@cs.ox.ac.uk> | 2020-11-20 13:11:14 +0000 |
commit | 0ac35ab057257eadc297d430666d0c1da41756f6 (patch) | |
tree | fbbd64a74675989039c9c9c36e85b9aadd21fb97 /src/main/scala/uk/ac/ox/cs/rsacomb | |
parent | c804109db7bb08cce1246acbca2030c3b979f242 (diff) | |
download | RSAComb-0ac35ab057257eadc297d430666d0c1da41756f6.tar.gz RSAComb-0ac35ab057257eadc297d430666d0c1da41756f6.zip |
Simplify workflow for query execution
Input query is now read from file.
Diffstat (limited to 'src/main/scala/uk/ac/ox/cs/rsacomb')
-rw-r--r-- | src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala | 186 | ||||
-rw-r--r-- | src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala | 55 | ||||
-rw-r--r-- | src/main/scala/uk/ac/ox/cs/rsacomb/util/RDFoxHelpers.scala | 98 |
3 files changed, 121 insertions, 218 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 db52793..678a5fa 100644 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala | |||
@@ -46,9 +46,7 @@ object RSAComb extends App { | |||
46 | sys.exit; | 46 | sys.exit; |
47 | } | 47 | } |
48 | 48 | ||
49 | /* Create RSA object from generic OWLOntology | 49 | /* TODO: It might be required to check if the ontology in input is |
50 | * | ||
51 | * TODO: It might be required to check if the ontology in input is | ||
52 | * Horn-ALCHOIQ. At the moment we are assuming this is always the | 50 | * Horn-ALCHOIQ. At the moment we are assuming this is always the |
53 | * case. | 51 | * case. |
54 | */ | 52 | */ |
@@ -56,183 +54,13 @@ object RSAComb extends App { | |||
56 | val ontology = RSAOntology(ontoPath) | 54 | val ontology = RSAOntology(ontoPath) |
57 | if (ontology.isRSA) { | 55 | if (ontology.isRSA) { |
58 | 56 | ||
59 | /* Load query */ | 57 | /** Read SPARQL query from file */ |
60 | val query = """ | 58 | val source = io.Source.fromFile(queryPath.getAbsoluteFile) |
61 | PREFIX : <http://example.com/rsa_example.owl#> | 59 | val query = source.getLines mkString "\n" |
62 | 60 | source.close() | |
63 | SELECT ?X | ||
64 | WHERE { | ||
65 | ?X a :D ; | ||
66 | :R ?Y . | ||
67 | ?Y :S ?Z . | ||
68 | ?Z a :D . | ||
69 | } | ||
70 | """ | ||
71 | 61 | ||
72 | /* Compute answers to query */ | 62 | /* Compute answers to query */ |
73 | ConjunctiveQuery(query) match { | 63 | val answers = ConjunctiveQuery(query).map(ontology ask _) |
74 | case Some(query) => { | 64 | answers map (_.toString) foreach println |
75 | |||
76 | import implicits.JavaCollections._ | ||
77 | |||
78 | // Open connection to RDFox | ||
79 | val (server, data) = RDFoxHelpers.openConnection("AnswerComputation") | ||
80 | |||
81 | { | ||
82 | println("\nQuery") | ||
83 | println(query) | ||
84 | } | ||
85 | |||
86 | val canon = ontology.canonicalModel | ||
87 | data.addRules(canon.rules) | ||
88 | val filter = ontology.filteringProgram(query) | ||
89 | data.addRules(filter.rules) | ||
90 | |||
91 | { | ||
92 | println("\nCanonical Model rules:") | ||
93 | canon.rules.foreach(println) | ||
94 | println("\nFiltering rules") | ||
95 | filter.rules.foreach(println) | ||
96 | } | ||
97 | |||
98 | // Retrieve answers | ||
99 | println("\nAnswers:") | ||
100 | val ans = | ||
101 | RDFoxHelpers.queryInternalPredicate(data, "Ans", filter.answer.length) | ||
102 | println(ans) | ||
103 | |||
104 | /* DEBUG: adding additional checks | ||
105 | */ | ||
106 | { | ||
107 | import suffix.{Forward, Backward} | ||
108 | |||
109 | val arity = filter.answer.length + filter.bounded.length | ||
110 | |||
111 | println("\nIndividuals:") | ||
112 | ontology.individuals.foreach(println) | ||
113 | |||
114 | println("\nThings:") | ||
115 | val things = RDFoxHelpers.submitQuery( | ||
116 | data, | ||
117 | """ | ||
118 | PREFIX owl: <http://www.w3.org/2002/07/owl#> | ||
119 | |||
120 | SELECT ?X { | ||
121 | ?X a owl:Thing | ||
122 | } | ||
123 | """ | ||
124 | ) | ||
125 | println(things) | ||
126 | |||
127 | println("\nNAMEDs:") | ||
128 | val named = RDFoxHelpers.submitQuery( | ||
129 | data, | ||
130 | """ | ||
131 | SELECT ?X { | ||
132 | ?X a rsa:Named | ||
133 | } | ||
134 | """, | ||
135 | RSA.Prefixes | ||
136 | ) | ||
137 | println(named) | ||
138 | |||
139 | println("\nNIs:") | ||
140 | val nis = RDFoxHelpers.submitQuery( | ||
141 | data, | ||
142 | """ | ||
143 | SELECT ?X { | ||
144 | ?X a rsa:NI | ||
145 | } | ||
146 | """, | ||
147 | RSA.Prefixes | ||
148 | ) | ||
149 | println(nis) | ||
150 | |||
151 | // ID instances | ||
152 | println("\nIDs:") | ||
153 | val ids = RDFoxHelpers.queryInternalPredicate( | ||
154 | data, | ||
155 | "ID", | ||
156 | arity + 2 | ||
157 | ) | ||
158 | println(ids) | ||
159 | |||
160 | println("\nCongruent:") | ||
161 | val equivs = RDFoxHelpers.submitQuery( | ||
162 | data, | ||
163 | """ | ||
164 | SELECT ?X ?Y { | ||
165 | ?X rsa:congruent ?Y | ||
166 | } | ||
167 | """, | ||
168 | RSA.Prefixes | ||
169 | ) | ||
170 | println(equivs) | ||
171 | |||
172 | // Unfiltered answers | ||
173 | println("\nPossible answers:") | ||
174 | val qms = RDFoxHelpers.queryInternalPredicate( | ||
175 | data, | ||
176 | "QM", | ||
177 | arity | ||
178 | ) | ||
179 | println(qms) | ||
180 | |||
181 | // Cycle detected | ||
182 | println("\nCycle detection:") | ||
183 | val aqf = RDFoxHelpers.queryInternalPredicate( | ||
184 | data, | ||
185 | "AQ" :: Forward, | ||
186 | arity + 2 | ||
187 | ) | ||
188 | val aqb = RDFoxHelpers.queryInternalPredicate( | ||
189 | data, | ||
190 | "AQ" :: Backward, | ||
191 | arity + 2 | ||
192 | ) | ||
193 | println(aqf) | ||
194 | println(aqb) | ||
195 | |||
196 | // Forks detected | ||
197 | println("\nForks:") | ||
198 | val fk = RDFoxHelpers.queryInternalPredicate( | ||
199 | data, | ||
200 | "FK", | ||
201 | arity | ||
202 | ) | ||
203 | println(fk) | ||
204 | |||
205 | // Spurious answers | ||
206 | println("\nSpurious answers") | ||
207 | val sp = RDFoxHelpers.queryInternalPredicate( | ||
208 | data, | ||
209 | "SP", | ||
210 | arity | ||
211 | ) | ||
212 | println(sp) | ||
213 | } | ||
214 | |||
215 | // Close connection to RDFox | ||
216 | RDFoxHelpers.closeConnection(server, data) | ||
217 | } | ||
218 | case None => {} | ||
219 | } | ||
220 | } | 65 | } |
221 | } | 66 | } |
222 | |||
223 | /* Notes: | ||
224 | * | ||
225 | * To establish a connection with a local RDFox instance, do the | ||
226 | * following: | ||
227 | * | ||
228 | * ``` | ||
229 | * val serverConnection : ServerConnection = ConnectionFactory.newServerConnection("rdfox:local", "", "") | ||
230 | * serverConnection.createDataStore("test","seq",new HashMap()) | ||
231 | * val dataStoreConnection : DataStoreConnection = serverConnection.newDataStoreConnection("test") | ||
232 | * dataStoreConnection.importData( | ||
233 | * UpdateType.ADDITION, | ||
234 | * Prefixes.s_emptyPrefixes, | ||
235 | * new File("./path/to/file") | ||
236 | * ) | ||
237 | * ``` | ||
238 | */ | ||
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 9640ccc..56fbac3 100644 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala | |||
@@ -1,6 +1,7 @@ | |||
1 | package uk.ac.ox.cs.rsacomb | 1 | package uk.ac.ox.cs.rsacomb |
2 | 2 | ||
3 | /* Java imports */ | 3 | /* Java imports */ |
4 | import java.{util => ju} | ||
4 | import java.util.HashMap | 5 | import java.util.HashMap |
5 | import java.util.stream.{Collectors, Stream} | 6 | import java.util.stream.{Collectors, Stream} |
6 | import java.io.File | 7 | import java.io.File |
@@ -48,8 +49,8 @@ import org.semanticweb.owlapi.model.OWLObjectInverseOf | |||
48 | 49 | ||
49 | import uk.ac.ox.cs.rsacomb.converter.{RDFoxAxiomConverter, SkolemStrategy} | 50 | import uk.ac.ox.cs.rsacomb.converter.{RDFoxAxiomConverter, SkolemStrategy} |
50 | import uk.ac.ox.cs.rsacomb.implicits.RSAAxiom | 51 | import uk.ac.ox.cs.rsacomb.implicits.RSAAxiom |
51 | import uk.ac.ox.cs.rsacomb.suffix.{Empty, Forward, Backward, Inverse} | 52 | import uk.ac.ox.cs.rsacomb.suffix._ |
52 | import uk.ac.ox.cs.rsacomb.sparql.ConjunctiveQuery | 53 | import uk.ac.ox.cs.rsacomb.sparql._ |
53 | import uk.ac.ox.cs.rsacomb.util.{RDFoxHelpers, RSA} | 54 | import uk.ac.ox.cs.rsacomb.util.{RDFoxHelpers, RSA} |
54 | 55 | ||
55 | object RSAOntology { | 56 | object RSAOntology { |
@@ -256,8 +257,8 @@ class RSAOntology(val ontology: OWLOntology) extends RSAAxiom { | |||
256 | ): Graph[Resource, UnDiEdge] = { | 257 | ): Graph[Resource, UnDiEdge] = { |
257 | val query = "SELECT ?X ?Y WHERE { ?X rsa:E ?Y }" | 258 | val query = "SELECT ?X ?Y WHERE { ?X rsa:E ?Y }" |
258 | val answers = RDFoxHelpers.submitQuery(data, query, RSA.Prefixes).get | 259 | val answers = RDFoxHelpers.submitQuery(data, query, RSA.Prefixes).get |
259 | var edges: List[UnDiEdge[Resource]] = answers.map { | 260 | var edges: Seq[UnDiEdge[Resource]] = answers.map { |
260 | case n1 :: n2 :: _ => UnDiEdge(n1, n2) | 261 | case Seq(n1, n2) => UnDiEdge(n1, n2) |
261 | } | 262 | } |
262 | Graph(edges: _*) | 263 | Graph(edges: _*) |
263 | } | 264 | } |
@@ -291,6 +292,52 @@ class RSAOntology(val ontology: OWLOntology) extends RSAAxiom { | |||
291 | .filterNot(_.getInverseProperty.isOWLTopObjectProperty()) | 292 | .filterNot(_.getInverseProperty.isOWLTopObjectProperty()) |
292 | } | 293 | } |
293 | 294 | ||
295 | /** Returns the answers to a query | ||
296 | * | ||
297 | * @param query query to execute | ||
298 | * @return a collection of answers | ||
299 | */ | ||
300 | def ask(query: ConjunctiveQuery): ConjunctiveQueryAnswers = { | ||
301 | import implicits.JavaCollections._ | ||
302 | val (server, data) = RDFoxHelpers.openConnection("AnswerComputation") | ||
303 | data.addRules(this.canonicalModel.rules) | ||
304 | data.addRules(this.filteringProgram(query).rules) | ||
305 | new ConjunctiveQueryAnswers( | ||
306 | query.boolean, | ||
307 | queryInternalPredicate(data, "Ans", query.answer.size) | ||
308 | ) | ||
309 | } | ||
310 | |||
311 | /** Returns instances of a reified predicate | ||
312 | * | ||
313 | * Predicated with arity higher than 2 are internally reified to be | ||
314 | * compatible with RDFox engine. This helper queries for predicate | ||
315 | * instances and returns a set of un-reified answers. | ||
316 | * | ||
317 | * @param data open datastore connection to RDFox | ||
318 | * @param pred name of the predicate | ||
319 | * @param arity arity of the predicate | ||
320 | * @param opts additional options to RDFox | ||
321 | * @return a collection of instances of the given predicate | ||
322 | */ | ||
323 | private def queryInternalPredicate( | ||
324 | data: DataStoreConnection, | ||
325 | pred: String, | ||
326 | arity: Int, | ||
327 | opts: ju.Map[String, String] = new ju.HashMap[String, String]() | ||
328 | ): Seq[Seq[Resource]] = { | ||
329 | val query = | ||
330 | if (arity > 0) { | ||
331 | (0 until arity).mkString("SELECT ?X", " ?X", "\n") + | ||
332 | (0 until arity) | ||
333 | .map(i => s"?S rsa:${pred :: Nth(i)} ?X$i .") | ||
334 | .mkString("WHERE {\n", "\n", "\n}") | ||
335 | } else { | ||
336 | s"ASK { ?X a rsa:$pred }" | ||
337 | } | ||
338 | RDFoxHelpers.submitQuery(data, query, RSA.Prefixes).get | ||
339 | } | ||
340 | |||
294 | def self(axiom: OWLSubClassOfAxiom): Set[Term] = { | 341 | def self(axiom: OWLSubClassOfAxiom): Set[Term] = { |
295 | // Assuming just one role in the signature of a T5 axiom | 342 | // Assuming just one role in the signature of a T5 axiom |
296 | val role = axiom.objectPropertyExpressionsInSignature(0) | 343 | val role = axiom.objectPropertyExpressionsInSignature(0) |
diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/util/RDFoxHelpers.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/util/RDFoxHelpers.scala index 5b85436..43e5d28 100644 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/util/RDFoxHelpers.scala +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/util/RDFoxHelpers.scala | |||
@@ -1,6 +1,5 @@ | |||
1 | package uk.ac.ox.cs.rsacomb.util | 1 | package uk.ac.ox.cs.rsacomb.util |
2 | 2 | ||
3 | import java.util.{Map => JMap, HashMap => JHashMap} | ||
4 | import java.io.StringReader | 3 | import java.io.StringReader |
5 | import tech.oxfordsemantic.jrdfox.Prefixes | 4 | import tech.oxfordsemantic.jrdfox.Prefixes |
6 | import tech.oxfordsemantic.jrdfox.client.{ | 5 | import tech.oxfordsemantic.jrdfox.client.{ |
@@ -12,32 +11,52 @@ import tech.oxfordsemantic.jrdfox.formats.SPARQLParser | |||
12 | import tech.oxfordsemantic.jrdfox.logic.expression.Resource | 11 | import tech.oxfordsemantic.jrdfox.logic.expression.Resource |
13 | import tech.oxfordsemantic.jrdfox.logic.sparql.statement.SelectQuery | 12 | import tech.oxfordsemantic.jrdfox.logic.sparql.statement.SelectQuery |
14 | 13 | ||
15 | import uk.ac.ox.cs.rsacomb.suffix.Nth | 14 | /** A collection of helper methods for RDFox */ |
16 | |||
17 | object RDFoxHelpers { | 15 | object RDFoxHelpers { |
18 | 16 | ||
17 | /** Type alias for a collection of answers to a | ||
18 | * [[tech.oxfordsemantic.jrdfox.logic.sparql.statement.Query]]. | ||
19 | */ | ||
20 | private type QueryAnswers = Seq[Seq[Resource]] | ||
21 | private def QueryAnswers() = List.empty[Seq[Resource]] | ||
22 | |||
23 | /** Type alias for <option => value> RDFox options. */ | ||
24 | private type RDFoxOpts = java.util.Map[String, String] | ||
25 | private def RDFoxOpts() = new java.util.HashMap[String, String]() | ||
26 | |||
27 | /** Setup a new local connection with RDFox. | ||
28 | * | ||
29 | * @param dataStore data store identifier | ||
30 | * @param opts additional options to RDFox | ||
31 | * @return a tuple with the newly opened server and data store | ||
32 | * connections. | ||
33 | * | ||
34 | * @see [[uk.ac.ox.cs.rsacomb.util.RDFoxHelpers.closeConnection | ||
35 | * RDFoxHelpers.closeConnection]] for | ||
36 | * details on how to close an open connection. | ||
37 | */ | ||
19 | def openConnection( | 38 | def openConnection( |
20 | dataStore: String, | 39 | dataStore: String, |
21 | opts: JMap[String, String] = new JHashMap[String, String]() | 40 | opts: RDFoxOpts = RDFoxOpts() |
22 | ): (ServerConnection, DataStoreConnection) = { | 41 | ): (ServerConnection, DataStoreConnection) = { |
23 | /* Create local server connection | ||
24 | */ | ||
25 | val serverUrl = "rdfox:local" | 42 | val serverUrl = "rdfox:local" |
26 | val role = "" | 43 | val role = "" |
27 | val password = "" | 44 | val password = "" |
28 | val server = | 45 | val server = |
29 | ConnectionFactory.newServerConnection(serverUrl, role, password) | 46 | ConnectionFactory.newServerConnection(serverUrl, role, password) |
30 | |||
31 | /* Create datastore connection | ||
32 | */ | ||
33 | // parameters.put("owl-in-rdf-support", "relaxed") | ||
34 | // parameters.put("equality", "noUNA") | ||
35 | server.createDataStore(dataStore, "par-complex-nn", opts) | 47 | server.createDataStore(dataStore, "par-complex-nn", opts) |
36 | val data = server.newDataStoreConnection(dataStore) | 48 | val data = server.newDataStoreConnection(dataStore) |
37 | |||
38 | (server, data) | 49 | (server, data) |
39 | } | 50 | } |
40 | 51 | ||
52 | /** Parse a SELECT query from a string in SPARQL format. | ||
53 | * | ||
54 | * @param query the string containing the SPARQL query | ||
55 | * @param prefixes additional prefixes for the query. It defaults to | ||
56 | * an empty set. | ||
57 | * @return a [[tech.oxfordsemantic.jrdfox.logic.sparql.statement.SelectQuery SelectQuery]] | ||
58 | * if the input string is a SELECT query, none otherwise. | ||
59 | */ | ||
41 | def parseSelectQuery( | 60 | def parseSelectQuery( |
42 | query: String, | 61 | query: String, |
43 | prefixes: Prefixes = new Prefixes() | 62 | prefixes: Prefixes = new Prefixes() |
@@ -52,13 +71,22 @@ object RDFoxHelpers { | |||
52 | } | 71 | } |
53 | } | 72 | } |
54 | 73 | ||
74 | /** Execute a query over a given datastore connection. | ||
75 | * | ||
76 | * @param data RDFox datastore connection. | ||
77 | * @param query a | ||
78 | * [[tech.oxfordsemantic.jrdfox.logic.sparql.statement.SelectQuery SelectQuery]] | ||
79 | * to be executed. | ||
80 | * @param opts additional options to RDFox. | ||
81 | * @returns a collection of answers to the query. | ||
82 | */ | ||
55 | def submitSelectQuery( | 83 | def submitSelectQuery( |
56 | data: DataStoreConnection, | 84 | data: DataStoreConnection, |
57 | query: SelectQuery, | 85 | query: SelectQuery, |
58 | opts: JMap[String, String] = new JHashMap[String, String]() | 86 | opts: RDFoxOpts = RDFoxOpts() |
59 | ): List[List[Resource]] = { | 87 | ): QueryAnswers = { |
60 | val cursor = data.createCursor(query, opts) | 88 | val cursor = data.createCursor(query, opts) |
61 | var answers: List[List[Resource]] = List() | 89 | var answers = QueryAnswers() |
62 | var mul = cursor.open() | 90 | var mul = cursor.open() |
63 | while (mul > 0) { | 91 | while (mul > 0) { |
64 | val answer = | 92 | val answer = |
@@ -70,32 +98,32 @@ object RDFoxHelpers { | |||
70 | answers | 98 | answers |
71 | } | 99 | } |
72 | 100 | ||
101 | /** Execute a query over a given datastore connection. | ||
102 | * | ||
103 | * @param data RDFox datastore connection. | ||
104 | * @param query a string representing a SPARQL query. | ||
105 | * @param prefixes additional prefixes for the query. It defaults to | ||
106 | * an empty set. | ||
107 | * @param opts additional options to RDFox. | ||
108 | * @returns a collection of answers to the query if the input query | ||
109 | * is a SELECT query, none otherwise. | ||
110 | */ | ||
73 | def submitQuery( | 111 | def submitQuery( |
74 | data: DataStoreConnection, | 112 | data: DataStoreConnection, |
75 | query: String, | 113 | query: String, |
76 | prefixes: Prefixes = new Prefixes(), | 114 | prefixes: Prefixes = new Prefixes(), |
77 | opts: JMap[String, String] = new JHashMap[String, String]() | 115 | opts: RDFoxOpts = RDFoxOpts() |
78 | ): Option[List[List[Resource]]] = | 116 | ): Option[QueryAnswers] = |
79 | parseSelectQuery(query, prefixes).map(submitSelectQuery(data, _, opts)) | 117 | parseSelectQuery(query, prefixes).map(submitSelectQuery(data, _, opts)) |
80 | 118 | ||
81 | def queryInternalPredicate( | 119 | /** Close an open connection to RDFox. |
82 | data: DataStoreConnection, | 120 | * |
83 | pred: String, | 121 | * @param server server connection |
84 | arity: Int, | 122 | * @param data data store connections |
85 | opts: JMap[String, String] = new JHashMap[String, String]() | 123 | * |
86 | ): List[List[Resource]] = { | 124 | * @see [[uk.ac.ox.cs.rsacomb.util.RDFoxHelpers.openConnection RDFoxHelpers.openConnection]] |
87 | var query = "SELECT" | 125 | * for details on how to create a new connection with RDFox. |
88 | for (i <- 0 until arity) { | 126 | */ |
89 | query ++= s" ?X$i" | ||
90 | } | ||
91 | query ++= " WHERE {" | ||
92 | for (i <- 0 until arity) { | ||
93 | query ++= s" ?S rsa:${pred :: Nth(i)} ?X$i ." | ||
94 | } | ||
95 | query ++= " }" | ||
96 | submitQuery(data, query, RSA.Prefixes, opts).get | ||
97 | } | ||
98 | |||
99 | def closeConnection( | 127 | def closeConnection( |
100 | server: ServerConnection, | 128 | server: ServerConnection, |
101 | data: DataStoreConnection | 129 | data: DataStoreConnection |