From 343757e71ff21cf92192147c136a8cc615f273b8 Mon Sep 17 00:00:00 2001 From: Federico Igne Date: Thu, 19 Nov 2020 23:06:05 +0000 Subject: Add wrapper class for (boolean) conjunctive queries --- src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala | 8 +- .../scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala | 2 +- .../ox/cs/rsacomb/implicits/JavaCollections.scala | 1 - .../ac/ox/cs/rsacomb/sparql/ConjunctiveQuery.scala | 124 +++++++++++++++++++++ .../rsacomb/sparql/ConjunctiveQueryAnswers.scala | 29 +++++ .../uk/ac/ox/cs/rsacomb/util/RDFoxHelpers.scala | 15 ++- 6 files changed, 169 insertions(+), 10 deletions(-) create mode 100644 src/main/scala/uk/ac/ox/cs/rsacomb/sparql/ConjunctiveQuery.scala create mode 100644 src/main/scala/uk/ac/ox/cs/rsacomb/sparql/ConjunctiveQueryAnswers.scala (limited to 'src/main/scala') 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 a8107b5..28196be 100644 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala @@ -113,7 +113,7 @@ object RSAComb extends App { ontology.individuals.foreach(println) println("\nThings:") - val things = RDFoxHelpers.submitSelectQuery( + val things = RDFoxHelpers.submitQuery( data, """ PREFIX owl: @@ -126,7 +126,7 @@ object RSAComb extends App { println(things) println("\nNAMEDs:") - val named = RDFoxHelpers.submitSelectQuery( + val named = RDFoxHelpers.submitQuery( data, """ SELECT ?X { @@ -138,7 +138,7 @@ object RSAComb extends App { println(named) println("\nNIs:") - val nis = RDFoxHelpers.submitSelectQuery( + val nis = RDFoxHelpers.submitQuery( data, """ SELECT ?X { @@ -159,7 +159,7 @@ object RSAComb extends App { println(ids) println("\nCongruent:") - val equivs = RDFoxHelpers.submitSelectQuery( + val equivs = RDFoxHelpers.submitQuery( data, """ SELECT ?X ?Y { 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 93fd5cd..c4d4184 100644 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala @@ -254,7 +254,7 @@ class RSAOntology(val ontology: OWLOntology) extends RSAAxiom { data: DataStoreConnection ): Graph[Resource, UnDiEdge] = { val query = "SELECT ?X ?Y WHERE { ?X rsa:E ?Y }" - val answers = RDFoxHelpers.submitSelectQuery(data, query, RSA.Prefixes) + val answers = RDFoxHelpers.submitQuery(data, query, RSA.Prefixes).get var edges: List[UnDiEdge[Resource]] = answers.map { case n1 :: n2 :: _ => UnDiEdge(n1, n2) } diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/implicits/JavaCollections.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/implicits/JavaCollections.scala index 3b621f4..9d205b5 100644 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/implicits/JavaCollections.scala +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/implicits/JavaCollections.scala @@ -9,5 +9,4 @@ object JavaCollections { implicit def scalaToJavaList[A](list: List[A]): java.util.List[A] = list.asJava - } diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/sparql/ConjunctiveQuery.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/sparql/ConjunctiveQuery.scala new file mode 100644 index 0000000..b523938 --- /dev/null +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/sparql/ConjunctiveQuery.scala @@ -0,0 +1,124 @@ +package uk.ac.ox.cs.rsacomb.sparql + +import java.util.{Map => JMap, HashMap => JHashMap} +import tech.oxfordsemantic.jrdfox.Prefixes +import tech.oxfordsemantic.jrdfox.client.DataStoreConnection +import tech.oxfordsemantic.jrdfox.logic.expression.Variable +import tech.oxfordsemantic.jrdfox.logic.sparql.pattern.{ + ConjunctionPattern, + QueryPattern, + TriplePattern +} +import tech.oxfordsemantic.jrdfox.logic.sparql.statement.SelectQuery +import uk.ac.ox.cs.rsacomb.util.RDFoxHelpers + +/** Factory for [[uk.ac.ox.cs.rsacomb.sparql.ConjunctiveQuery]]. */ +object ConjunctiveQuery { + + /** Creates a new ConjunctiveQuery instance. + * + * @param query `SelectQuery` instance representing the actual query + */ + def apply(query: SelectQuery): ConjunctiveQuery = + new ConjunctiveQuery(query) + + /** Creates a new ConjunctiveQuery from a query string + * + * @param query a string representing the query in SPARQL format + * @param prefixes additional prefixes used in the query. Defaults to + * an empty set of prefixes. + * @return an [[scala.Option]] containing a ConjunctiveQuery if the + * input query represents one, None is returned otherwise. + */ + def apply( + query: String, + prefixes: Prefixes = new Prefixes() + ): Option[ConjunctiveQuery] = + RDFoxHelpers.parseSelectQuery(query, prefixes).map(ConjunctiveQuery(_)) + +} + +/** A conjunctive query + * + * A thin layer around + * [[tech.oxfordsemantics.jrdfox.logic.sparql.statement.SelectQuery]]. + * + * Instances should be created using the companion object. + * + * @todo additional checks need to be performed in order for a + * `SelectQuery` to be considered a conjunctive query. + */ +class ConjunctiveQuery( + query: SelectQuery, + val prefixes: Prefixes = new Prefixes() +) { + + import uk.ac.ox.cs.rsacomb.implicits.JavaCollections._ + + /** SELECT section of the SPARQL query. + * + * Simply exposes the underlying `getSelection` method in + * [[tech.oxfordsemantics.jrdfox.logic.sparql.statement.SelectQuery]]. + */ + val select = query.getSelection + + /** WHERE section of the SPARQL query. + * + * Simply exposes the underlying `getWherePattern` method in + * [[tech.oxfordsemantics.jrdfox.logic.sparql.statement.QueryBody]]. + */ + val where = query.getQueryBody.getWherePattern + + /** Returns true if it is a boolean CQ. + * + * @note checking for `select` being empty is not enough. When a + * query selects '''all''' variables with `*`, `select` is empty as + * well. + */ + val boolean: Boolean = select.isEmpty && !query.getAllPossibleVariables + + /** Returns the full set of variables involved in the query. */ + val variables: Set[Variable] = + where match { + case b: ConjunctionPattern => { + b.getConjuncts.toSet.flatMap { conj: QueryPattern => + conj match { + case c: TriplePattern => + Set(c.getSubject, c.getPredicate, c.getObject).collect { + case v: Variable => v + } + case _ => Set() + } + } + } + case _ => Set() + } + + /** Returns the collection of answer variables in the query. */ + val answer: Set[Variable] = + if (query.getAllPossibleVariables) + variables + else + select.map(_.getVariable).toSet + + /** Returns the collection of bounded (existential) variables in the query. */ + val bounded: Set[Variable] = variables &~ answer + + /** Returns the answers to a query + * + * @param data data store against which the query is executed + * @param opts additional options passed to RDFox + * @return a new [[ConjunctiveQueryAnswers]] instance containing the + * collection of answers. + */ + def answers( + data: DataStoreConnection, + opts: JMap[String, String] = new JHashMap[String, String]() + ): ConjunctiveQueryAnswers = + new ConjunctiveQueryAnswers( + boolean, + RDFoxHelpers.submitSelectQuery(data, query, opts) + ) + + override def toString(): String = query.toString +} 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 new file mode 100644 index 0000000..223e121 --- /dev/null +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/sparql/ConjunctiveQueryAnswers.scala @@ -0,0 +1,29 @@ +package uk.ac.ox.cs.rsacomb.sparql + +import tech.oxfordsemantic.jrdfox.logic.expression.Resource + +/** A collections of answers to a query + * + * Will take the query type (CQ or BCQ) into consideration when + * serialised. + * + * @param boolean whether the answers are to a BCQ + * @param answers collection of answers to a query. When dealing with + * BCQs, and empty collection represents a ''false'', + * ''true'' otherwise. + */ +class ConjunctiveQueryAnswers( + boolean: Boolean, + val answers: Seq[Seq[Resource]] +) { + + override def toString(): String = + if (boolean) { + if (answers.isEmpty) "FALSE" else "TRUE" + } else { + if (answers.isEmpty) + "NO ANSWER" + else + answers.map(_.mkString("(", ", ", ")")).mkString("\n") + } +} 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 fd9e1c5..5b85436 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 @@ -54,11 +54,10 @@ object RDFoxHelpers { def submitSelectQuery( data: DataStoreConnection, - query: String, - prefixes: Prefixes = new Prefixes(), + query: SelectQuery, opts: JMap[String, String] = new JHashMap[String, String]() ): List[List[Resource]] = { - val cursor = data.createCursor(prefixes, query, opts) + val cursor = data.createCursor(query, opts) var answers: List[List[Resource]] = List() var mul = cursor.open() while (mul > 0) { @@ -71,6 +70,14 @@ object RDFoxHelpers { answers } + def submitQuery( + data: DataStoreConnection, + query: String, + prefixes: Prefixes = new Prefixes(), + opts: JMap[String, String] = new JHashMap[String, String]() + ): Option[List[List[Resource]]] = + parseSelectQuery(query, prefixes).map(submitSelectQuery(data, _, opts)) + def queryInternalPredicate( data: DataStoreConnection, pred: String, @@ -86,7 +93,7 @@ object RDFoxHelpers { query ++= s" ?S rsa:${pred :: Nth(i)} ?X$i ." } query ++= " }" - submitSelectQuery(data, query, RSA.Prefixes, opts) + submitQuery(data, query, RSA.Prefixes, opts).get } def closeConnection( -- cgit v1.2.3