From 0ac35ab057257eadc297d430666d0c1da41756f6 Mon Sep 17 00:00:00 2001 From: Federico Igne Date: Fri, 20 Nov 2020 13:10:44 +0000 Subject: Simplify workflow for query execution Input query is now read from file. --- src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala | 186 +-------------------- .../scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala | 55 +++++- .../uk/ac/ox/cs/rsacomb/util/RDFoxHelpers.scala | 98 +++++++---- 3 files changed, 121 insertions(+), 218 deletions(-) (limited to 'src/main/scala/uk') 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 { sys.exit; } - /* Create RSA object from generic OWLOntology - * - * TODO: It might be required to check if the ontology in input is + /* TODO: It might be required to check if the ontology in input is * Horn-ALCHOIQ. At the moment we are assuming this is always the * case. */ @@ -56,183 +54,13 @@ object RSAComb extends App { val ontology = RSAOntology(ontoPath) if (ontology.isRSA) { - /* Load query */ - val query = """ - PREFIX : - - SELECT ?X - WHERE { - ?X a :D ; - :R ?Y . - ?Y :S ?Z . - ?Z a :D . - } - """ + /** Read SPARQL query from file */ + val source = io.Source.fromFile(queryPath.getAbsoluteFile) + val query = source.getLines mkString "\n" + source.close() /* Compute answers to query */ - ConjunctiveQuery(query) match { - case Some(query) => { - - import implicits.JavaCollections._ - - // Open connection to RDFox - val (server, data) = RDFoxHelpers.openConnection("AnswerComputation") - - { - println("\nQuery") - println(query) - } - - val canon = ontology.canonicalModel - data.addRules(canon.rules) - val filter = ontology.filteringProgram(query) - data.addRules(filter.rules) - - { - println("\nCanonical Model rules:") - canon.rules.foreach(println) - println("\nFiltering rules") - filter.rules.foreach(println) - } - - // Retrieve answers - println("\nAnswers:") - val ans = - RDFoxHelpers.queryInternalPredicate(data, "Ans", filter.answer.length) - println(ans) - - /* DEBUG: adding additional checks - */ - { - import suffix.{Forward, Backward} - - val arity = filter.answer.length + filter.bounded.length - - println("\nIndividuals:") - ontology.individuals.foreach(println) - - println("\nThings:") - val things = RDFoxHelpers.submitQuery( - data, - """ - PREFIX owl: - - SELECT ?X { - ?X a owl:Thing - } - """ - ) - println(things) - - println("\nNAMEDs:") - val named = RDFoxHelpers.submitQuery( - data, - """ - SELECT ?X { - ?X a rsa:Named - } - """, - RSA.Prefixes - ) - println(named) - - println("\nNIs:") - val nis = RDFoxHelpers.submitQuery( - data, - """ - SELECT ?X { - ?X a rsa:NI - } - """, - RSA.Prefixes - ) - println(nis) - - // ID instances - println("\nIDs:") - val ids = RDFoxHelpers.queryInternalPredicate( - data, - "ID", - arity + 2 - ) - println(ids) - - println("\nCongruent:") - val equivs = RDFoxHelpers.submitQuery( - data, - """ - SELECT ?X ?Y { - ?X rsa:congruent ?Y - } - """, - RSA.Prefixes - ) - println(equivs) - - // Unfiltered answers - println("\nPossible answers:") - val qms = RDFoxHelpers.queryInternalPredicate( - data, - "QM", - arity - ) - println(qms) - - // Cycle detected - println("\nCycle detection:") - val aqf = RDFoxHelpers.queryInternalPredicate( - data, - "AQ" :: Forward, - arity + 2 - ) - val aqb = RDFoxHelpers.queryInternalPredicate( - data, - "AQ" :: Backward, - arity + 2 - ) - println(aqf) - println(aqb) - - // Forks detected - println("\nForks:") - val fk = RDFoxHelpers.queryInternalPredicate( - data, - "FK", - arity - ) - println(fk) - - // Spurious answers - println("\nSpurious answers") - val sp = RDFoxHelpers.queryInternalPredicate( - data, - "SP", - arity - ) - println(sp) - } - - // Close connection to RDFox - RDFoxHelpers.closeConnection(server, data) - } - case None => {} - } + val answers = ConjunctiveQuery(query).map(ontology ask _) + answers map (_.toString) foreach println } } - -/* Notes: - * - * To establish a connection with a local RDFox instance, do the - * following: - * - * ``` - * val serverConnection : ServerConnection = ConnectionFactory.newServerConnection("rdfox:local", "", "") - * serverConnection.createDataStore("test","seq",new HashMap()) - * val dataStoreConnection : DataStoreConnection = serverConnection.newDataStoreConnection("test") - * dataStoreConnection.importData( - * UpdateType.ADDITION, - * Prefixes.s_emptyPrefixes, - * new File("./path/to/file") - * ) - * ``` - */ 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 @@ package uk.ac.ox.cs.rsacomb /* Java imports */ +import java.{util => ju} import java.util.HashMap import java.util.stream.{Collectors, Stream} import java.io.File @@ -48,8 +49,8 @@ import org.semanticweb.owlapi.model.OWLObjectInverseOf import uk.ac.ox.cs.rsacomb.converter.{RDFoxAxiomConverter, SkolemStrategy} import uk.ac.ox.cs.rsacomb.implicits.RSAAxiom -import uk.ac.ox.cs.rsacomb.suffix.{Empty, Forward, Backward, Inverse} -import uk.ac.ox.cs.rsacomb.sparql.ConjunctiveQuery +import uk.ac.ox.cs.rsacomb.suffix._ +import uk.ac.ox.cs.rsacomb.sparql._ import uk.ac.ox.cs.rsacomb.util.{RDFoxHelpers, RSA} object RSAOntology { @@ -256,8 +257,8 @@ class RSAOntology(val ontology: OWLOntology) extends RSAAxiom { ): Graph[Resource, UnDiEdge] = { val query = "SELECT ?X ?Y WHERE { ?X rsa:E ?Y }" val answers = RDFoxHelpers.submitQuery(data, query, RSA.Prefixes).get - var edges: List[UnDiEdge[Resource]] = answers.map { - case n1 :: n2 :: _ => UnDiEdge(n1, n2) + var edges: Seq[UnDiEdge[Resource]] = answers.map { + case Seq(n1, n2) => UnDiEdge(n1, n2) } Graph(edges: _*) } @@ -291,6 +292,52 @@ class RSAOntology(val ontology: OWLOntology) extends RSAAxiom { .filterNot(_.getInverseProperty.isOWLTopObjectProperty()) } + /** Returns the answers to a query + * + * @param query query to execute + * @return a collection of answers + */ + def ask(query: ConjunctiveQuery): ConjunctiveQueryAnswers = { + import implicits.JavaCollections._ + val (server, data) = RDFoxHelpers.openConnection("AnswerComputation") + data.addRules(this.canonicalModel.rules) + data.addRules(this.filteringProgram(query).rules) + new ConjunctiveQueryAnswers( + query.boolean, + queryInternalPredicate(data, "Ans", query.answer.size) + ) + } + + /** Returns instances of a reified predicate + * + * Predicated with arity higher than 2 are internally reified to be + * compatible with RDFox engine. This helper queries for predicate + * instances and returns a set of un-reified answers. + * + * @param data open datastore connection to RDFox + * @param pred name of the predicate + * @param arity arity of the predicate + * @param opts additional options to RDFox + * @return a collection of instances of the given predicate + */ + private def queryInternalPredicate( + data: DataStoreConnection, + pred: String, + arity: Int, + opts: ju.Map[String, String] = new ju.HashMap[String, String]() + ): Seq[Seq[Resource]] = { + val query = + if (arity > 0) { + (0 until arity).mkString("SELECT ?X", " ?X", "\n") + + (0 until arity) + .map(i => s"?S rsa:${pred :: Nth(i)} ?X$i .") + .mkString("WHERE {\n", "\n", "\n}") + } else { + s"ASK { ?X a rsa:$pred }" + } + RDFoxHelpers.submitQuery(data, query, RSA.Prefixes).get + } + def self(axiom: OWLSubClassOfAxiom): Set[Term] = { // Assuming just one role in the signature of a T5 axiom 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 @@ package uk.ac.ox.cs.rsacomb.util -import java.util.{Map => JMap, HashMap => JHashMap} import java.io.StringReader import tech.oxfordsemantic.jrdfox.Prefixes import tech.oxfordsemantic.jrdfox.client.{ @@ -12,32 +11,52 @@ import tech.oxfordsemantic.jrdfox.formats.SPARQLParser import tech.oxfordsemantic.jrdfox.logic.expression.Resource import tech.oxfordsemantic.jrdfox.logic.sparql.statement.SelectQuery -import uk.ac.ox.cs.rsacomb.suffix.Nth - +/** A collection of helper methods for RDFox */ object RDFoxHelpers { + /** Type alias for a collection of answers to a + * [[tech.oxfordsemantic.jrdfox.logic.sparql.statement.Query]]. + */ + private type QueryAnswers = Seq[Seq[Resource]] + private def QueryAnswers() = List.empty[Seq[Resource]] + + /** Type alias for