From ba9a3a44114453e029e87c7e7d726e41ba4797c6 Mon Sep 17 00:00:00 2001 From: Jim Balhoff Date: Mon, 20 Jun 2022 16:14:28 -0400 Subject: [PATCH 1/2] Reformat --- src/main/scala/org/phenoscape/kb/Term.scala | 51 +++++++++++---------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/src/main/scala/org/phenoscape/kb/Term.scala b/src/main/scala/org/phenoscape/kb/Term.scala index d1f4ac4f..0c13abe9 100644 --- a/src/main/scala/org/phenoscape/kb/Term.scala +++ b/src/main/scala/org/phenoscape/kb/Term.scala @@ -52,7 +52,7 @@ object Term { limit: Int = 100): Future[Seq[MatchedTerm[DefinedMinimalTerm]]] = { def resultFromQuerySolution(qs: QuerySolution): DefinedMinimalTerm = DefinedMinimalTerm(MinimalTerm.fromQuerySolution(qs), - Option(qs.getResource("ont")).map(o => IRI.create(o.getURI))) + Option(qs.getResource("ont")).map(o => IRI.create(o.getURI))) App .executeSPARQLQueryString( @@ -93,8 +93,8 @@ object Term { def computeLabelForAnonymousTerm(iri: IRI): Future[MinimalTerm] = iri.toString match { case expression - if expression.startsWith(ExpressionUtil.namedExpressionPrefix) || expression.startsWith( - ExpressionUtil.namedSubClassPrefix) => + if expression.startsWith(ExpressionUtil.namedExpressionPrefix) || expression.startsWith( + ExpressionUtil.namedSubClassPrefix) => labelForNamedExpression(iri) case negation if negation.startsWith("http://phenoscape.org/not/") => computedLabel(IRI.create(negation.replaceFirst(Pattern.quote("http://phenoscape.org/not/"), ""))).map { term => @@ -131,8 +131,8 @@ object Term { def withIRI(iri: IRI): Future[Option[Term]] = { def termResult(result: QuerySolution) = (Option(result.getLiteral("label")).map(_.getLexicalForm), - Option(result.getLiteral("definition")).map(_.getLexicalForm).getOrElse(""), - Option(result.getResource("ontology")).map(_.getURI)) + Option(result.getLiteral("definition")).map(_.getLexicalForm).getOrElse(""), + Option(result.getResource("ontology")).map(_.getURI)) val termFuture = App.executeSPARQLQuery(buildTermQuery(iri), termResult).map(_.headOption) val synonymsFuture = termSynonyms(iri) @@ -188,8 +188,8 @@ object Term { def shouldHide(term: MinimalTerm) = { val termID = term.iri.toString termID.startsWith("http://example.org") || - termID == "http://www.w3.org/2002/07/owl#Nothing" || - termID == "http://www.w3.org/2002/07/owl#Thing" + termID == "http://www.w3.org/2002/07/owl#Nothing" || + termID == "http://www.w3.org/2002/07/owl#Thing" } val superclassesFuture = querySuperClasses(iri, source) @@ -202,9 +202,9 @@ object Term { subclasses <- subclassesFuture equivalents <- equivalentsFuture } yield Classification(term, - superclasses.filterNot(shouldHide).toSet, - subclasses.filterNot(shouldHide).toSet, - equivalents.filterNot(shouldHide).toSet) + superclasses.filterNot(shouldHide).toSet, + subclasses.filterNot(shouldHide).toSet, + equivalents.filterNot(shouldHide).toSet) } def querySuperClasses(iri: IRI, source: Option[IRI]): Future[Seq[MinimalTerm]] = { @@ -268,7 +268,7 @@ object Term { val query = select_distinct('term, 'term_label) from "http://kb.phenoscape.org/" where (bgp( (t('term, rdfsLabel, 'term_label) :: definedByTriple): _*), - union) + union) App.executeSPARQLQuery(query, MinimalTerm.fromQuerySolution) } @@ -340,16 +340,16 @@ object Term { val superClassesQuery = select_distinct('super) from "http://kb.phenoscape.org/" where (bgp( (t('super, rdfType, owlClass) :: definedByTriple ++ - iris.map(superClassTriple).toList): _*)) + iris.map(superClassTriple).toList): _*)) val superSuperClassesQuery = select_distinct('supersuper) from "http://kb.phenoscape.org/" where ( bgp( (t('super, rdfType, owlClass) :: t('supersuper, rdfType, owlClass) :: t('super, new P_OneOrMore1(new P_Link(rdfsSubClassOf)), 'supersuper) :: definedByTriple ++ - iris.map(superClassTriple).toList): _* - ) + iris.map(superClassTriple).toList): _* ) + ) val superClassesFuture = App.executeSPARQLQuery(superClassesQuery, _.getResource("super").getURI) val superSuperClassesFuture = App.executeSPARQLQuery(superSuperClassesQuery, _.getResource("supersuper").getURI) for { @@ -369,8 +369,8 @@ object Term { def buildTermQuery(iri: IRI): Query = select_distinct('label, 'definition, 'ontology) from "http://kb.phenoscape.org/" where (optional( bgp(t(iri, rdfsLabel, 'label))), - optional(bgp(t(iri, definition, 'definition))), - optional(bgp(t(iri, rdfsIsDefinedBy, 'ontology)))) + optional(bgp(t(iri, definition, 'definition))), + optional(bgp(t(iri, rdfsIsDefinedBy, 'ontology)))) def termRelationships(iri: IRI): Future[Seq[TermRelationship]] = App.executeSPARQLQuery( @@ -378,7 +378,7 @@ object Term { (result) => TermRelationship( MinimalTerm(IRI.create(result.getResource("relation").getURI), - Some(result.getLiteral("relation_name").getString)), + Some(result.getLiteral("relation_name").getString)), MinimalTerm(IRI.create(result.getResource("filler").getURI), Some(result.getLiteral("filler_name").getString)) ) ) @@ -411,8 +411,8 @@ object Term { t('restriction, owlSomeValuesFrom, 'filler), t('filler, rdfsLabel, 'filler_label) ), - new ElementFilter(new E_IsIRI(new ExprVar('relation))), - new ElementFilter(new E_IsIRI(new ExprVar('filler)))) + new ElementFilter(new E_IsIRI(new ExprVar('relation))), + new ElementFilter(new E_IsIRI(new ExprVar('filler)))) // We need to handle multiple labels in the DB for properties (and possibly classes) query.getProject.add(Var.alloc("filler_name"), query.allocAggregate(new AggMin(new ExprVar('filler_label)))) query.getProject.add(Var.alloc("relation_name"), query.allocAggregate(new AggMin(new ExprVar('relation_label)))) @@ -483,8 +483,8 @@ object Term { t('term, rdfsIsDefinedBy, definedBy), t('term, rdfType, owlClass) ), - new ElementFilter(new E_IsIRI(new ExprVar('term))), - new ElementFilter(new E_NotExists(triplesBlock(bgp(t('term, owlDeprecated, "true" ^^ XSDDatatype.XSDboolean)))))) + new ElementFilter(new E_IsIRI(new ExprVar('term))), + new ElementFilter(new E_NotExists(triplesBlock(bgp(t('term, owlDeprecated, "true" ^^ XSDDatatype.XSDboolean)))))) query.addOrderBy('rank, Query.ORDER_ASCENDING) if (limit > 0) query.setLimit(limit) query @@ -539,7 +539,8 @@ object Term { final case class RelationalTerm(relation: IRI, term: IRI) { def iri: IRI = - IRI.create(s"${RelationalTerm.Prefix}/${URLEncoder.encode(relation.toString, "utf-8")}/${URLEncoder.encode(term.toString, "utf-8")}") + IRI.create( + s"${RelationalTerm.Prefix}/${URLEncoder.encode(relation.toString, "utf-8")}/${URLEncoder.encode(term.toString, "utf-8")}") } @@ -564,7 +565,7 @@ final case class Term(iri: IRI, synonyms: Seq[(IRI, String)], classification: Classification, relationships: Seq[TermRelationship]) - extends LabeledTerm + extends LabeledTerm with JSONResultItem { def toJSON: JsObject = @@ -595,7 +596,7 @@ object MinimalTerm { def fromQuerySolution(result: QuerySolution): MinimalTerm = MinimalTerm(IRI.create(result.getResource("term").getURI), - Option(result.getLiteral("term_label")).map(_.getLexicalForm)) + Option(result.getLiteral("term_label")).map(_.getLexicalForm)) val tsvMarshaller: ToEntityMarshaller[MinimalTerm] = Marshaller.stringMarshaller(MediaTypes.`text/tab-separated-values`).compose(_.toTSV) @@ -672,7 +673,7 @@ final case class Classification(term: MinimalTerm, superclasses: Set[MinimalTerm], subclasses: Set[MinimalTerm], equivalents: Set[MinimalTerm]) - extends JSONResultItem { + extends JSONResultItem { def toJSON: JsObject = JsObject( From cef580e76c1df53615294e20492f3e2126df8231 Mon Sep 17 00:00:00 2001 From: Jim Balhoff Date: Mon, 20 Jun 2022 16:53:40 -0400 Subject: [PATCH 2/2] Discontinue use of redundant relation graph. --- src/main/resources/swaggerDocs/swagger.yaml | 12 -- .../org/phenoscape/kb/AnatomicalEntity.scala | 39 ++++- .../scala/org/phenoscape/kb/EQForGene.scala | 142 ++++++++++------- src/main/scala/org/phenoscape/kb/Graph.scala | 96 +++--------- .../scala/org/phenoscape/kb/KBVocab.scala | 1 - src/main/scala/org/phenoscape/kb/Main.scala | 18 +-- .../scala/org/phenoscape/kb/Phenotype.scala | 143 +++++++++++------- src/main/scala/org/phenoscape/kb/Term.scala | 15 +- 8 files changed, 244 insertions(+), 222 deletions(-) diff --git a/src/main/resources/swaggerDocs/swagger.yaml b/src/main/resources/swaggerDocs/swagger.yaml index bf07d4d8..3cc51a59 100644 --- a/src/main/resources/swaggerDocs/swagger.yaml +++ b/src/main/resources/swaggerDocs/swagger.yaml @@ -419,12 +419,6 @@ paths: required: true type: string format: IRI - - name: direct - in: query - description: Returns only direct neighbors when set `true`. Filters out superclasses and transitively related terms. - required: false - type: boolean - default: false responses: 200: description: subject terms related to this object @@ -451,12 +445,6 @@ paths: required: true type: string format: IRI - - name: direct - in: query - description: Returns only direct neighbors when set `true`. Filters out superclasses and transitively related terms. - required: false - type: boolean - default: false responses: 200: description: object terms related to this subject diff --git a/src/main/scala/org/phenoscape/kb/AnatomicalEntity.scala b/src/main/scala/org/phenoscape/kb/AnatomicalEntity.scala index c752d783..d7859050 100644 --- a/src/main/scala/org/phenoscape/kb/AnatomicalEntity.scala +++ b/src/main/scala/org/phenoscape/kb/AnatomicalEntity.scala @@ -4,11 +4,11 @@ import akka.http.scaladsl.marshalling.{Marshaller, ToEntityMarshaller} import akka.http.scaladsl.model.MediaTypes import org.apache.jena.query.{QueryFactory, QuerySolution} import org.phenoscape.kb.KBVocab.{rdfsSubClassOf, _} +import org.phenoscape.kb.Similarity.rdfsSubClassOf import org.phenoscape.owl.Vocab._ import org.phenoscape.scowl._ import org.phenoscape.sparql.SPARQLInterpolation._ import org.phenoscape.sparql.SPARQLInterpolationOWL._ -import org.phenoscape.kb.util.SPARQLInterpolatorBlazegraph._ import org.phenoscape.owl.{NamedRestrictionGenerator, Vocab} import org.semanticweb.owlapi.model.IRI import spray.json.DefaultJsonProtocol._ @@ -70,7 +70,7 @@ object AnatomicalEntity { } // Output a boolean matrix as CSV - def matrixRendererFromMapOfMaps[A](dependencyMatrix: DependencyMatrix[A]): String = { + def matrixRendererFromMapOfMaps[A](dependencyMatrix: DependencyMatrix[A]) = { val mapOfMaps = dependencyMatrix.map val orderedKeys = dependencyMatrix.orderedKeys @@ -78,7 +78,10 @@ object AnatomicalEntity { val matrix = for (x <- orderedKeys) yield { val row = s"$x" - val values = for (y <- orderedKeys) yield (if (mapOfMaps(x)(y)) 1 else 0) + val values = for (y <- orderedKeys) yield mapOfMaps(x)(y) match { + case true => 1 + case false => 0 + } s"$row,${values.mkString(",")}" } s"$headers\n${matrix.mkString("\n")}\n" @@ -118,16 +121,38 @@ object AnatomicalEntity { """ private def queryImpliesPresenceOfMulti(terms: Iterable[IRI]): QueryText = { + import scalaz._ + import Scalaz._ val valuesList = terms.map(t => sparql" $t ").fold(sparql"")(_ + _) sparql""" + PREFIX hint: SELECT DISTINCT ?x ?y FROM $KBClosureGraph FROM $KBMainGraph - FROM $KBRedundantRelationGraph + WITH { + SELECT ?term ?presence + WHERE { + VALUES ?term { $valuesList } + ?presence ?term . + } + } AS %SUB1 WHERE { - VALUES ?x { $valuesList } - VALUES ?y { $valuesList } - ?x $rdfsSubClassOf | $IMPLIES_PRESENCE_OF ?y . + hint:Query hint:filterExists "VectoredSubPlan" . + { + SELECT (?term AS ?x) (?presence AS ?x_presence) + WHERE { + INCLUDE %SUB1 + } + } + { + SELECT (?term AS ?y) (?presence AS ?y_presence) + WHERE { + INCLUDE %SUB1 + } + } + FILTER EXISTS { + ?x_presence ?y_presence + } FILTER(?x != ?y) } """ diff --git a/src/main/scala/org/phenoscape/kb/EQForGene.scala b/src/main/scala/org/phenoscape/kb/EQForGene.scala index 043ea889..b4d40fe0 100644 --- a/src/main/scala/org/phenoscape/kb/EQForGene.scala +++ b/src/main/scala/org/phenoscape/kb/EQForGene.scala @@ -1,22 +1,36 @@ package org.phenoscape.kb import akka.util.Timeout +import com.google.common.collect.HashMultiset import org.apache.jena.query.Query import org.phenoscape.kb.KBVocab._ -import org.phenoscape.owl.Vocab._ -import org.phenoscape.sparql.SPARQLInterpolation._ +import org.phenoscape.owl.{NamedRestrictionGenerator, Vocab} +import org.phenoscape.owlet.SPARQLComposer._ +import org.phenoscape.scowl._ +import org.semanticweb.owlapi.apibinding.OWLManager import org.semanticweb.owlapi.model.IRI -import org.phenoscape.sparql.SPARQLInterpolationOWL._ import spray.json.DefaultJsonProtocol._ import spray.json._ import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future import scala.concurrent.duration._ +import scala.jdk.CollectionConverters._ import scala.language.postfixOps object EQForGene { + private val factory = OWLManager.getOWLDataFactory + private val rdfType = ObjectProperty(Vocab.rdfType) + private val rdfsSubClassOf = ObjectProperty(Vocab.rdfsSubClassOf) + private val rdfsIsDefinedBy = factory.getRDFSIsDefinedBy + private val UBERON = IRI.create("http://purl.obolibrary.org/obo/uberon.owl") + private val PATO = IRI.create("http://purl.obolibrary.org/obo/pato.owl") + private val has_part_some = NamedRestrictionGenerator.getClassRelationIRI(Vocab.has_part.getIRI) + + private val has_part_inhering_in_some = + NamedRestrictionGenerator.getClassRelationIRI(Vocab.has_part_inhering_in.getIRI) + implicit private val timeout: Timeout = Timeout(10 minutes) def query(geneID: IRI): Future[JsArray] = { @@ -41,72 +55,86 @@ object EQForGene { } def annotationsForGene(geneID: IRI): Future[Iterable[String]] = - App.executeSPARQLQuery(annotationsQuery(geneID), _.getResource("association").getURI) + App.executeSPARQLQuery(annotationsQuery(geneID), _.getResource("annotation").getURI) def annotationsQuery(geneIRI: IRI): Query = - sparql""" - SELECT DISTINCT ?association - FROM $KBMainGraph - WHERE { - ?association $rdfType $association . - ?association $associationHasPredicate $has_phenotype . - ?association $associationHasSubject $geneIRI . - } - """.toQuery - - def qualitiesForAnnotation(annotationID: String): Future[Iterable[String]] = - App.executeSPARQLQuery(annotationSuperQualityQuery(annotationID), _.getResource("quality").getURI) + select_distinct('association) from "http://kb.phenoscape.org/" where bgp( + t('association, rdfType, association), + t('association, associationHasPredicate, has_phenotype), + t('association, associationHasSubject, geneIRI)) + + def qualitiesForAnnotation(annotationID: String): Future[Iterable[String]] = { + val allSuperQualities = + App.executeSPARQLQuery(annotationSuperQualityQuery(annotationID), _.getResource("quality").getURI) + for { + superQualities <- allSuperQualities + superSuperQualities <- superClassesForSuperQualities(superQualities) + } yield { + val superclasses = HashMultiset.create[String] + superSuperQualities.foreach(superclasses.add) + val nearestQualities = superclasses.entrySet.asScala.filter(_.getCount == 1).map(_.getElement) + nearestQualities.toVector + } + } + + def superClassesForSuperQualities(superQualities: Iterable[String]): Future[Iterable[String]] = { + val superclasses = Future.sequence(superQualities.map { superClass => + App.executeSPARQLQuery(qualitySuperQualityQuery(superClass), _.getResource("quality").getURI) + }) + for { result <- superclasses } yield result.flatten + } def annotationSuperQualityQuery(annotationID: String): Query = { val annotationIRI = IRI.create(annotationID) + select_distinct('quality) from "http://kb.phenoscape.org/" from "http://kb.phenoscape.org/closure" where bgp( + t(annotationIRI, rdfType / rdfsSubClassOf, 'has_quality), + t('has_quality, has_part_some, 'quality), + t('quality, rdfsIsDefinedBy, PATO)) + } - val query = - sparql""" - SELECT DISTINCT ?quality - FROM $KBMainGraph - FROM $KBRedundantRelationGraph - WHERE { - $annotationIRI $associationHasObject ?phenotype . - ?phenotype $has_part ?quality . - ?quality $rdfsIsDefinedBy $PATO . - - FILTER NOT EXISTS { - ?phenotype $has_part ?other_quality . - ?other_quality $rdfsIsDefinedBy $PATO . - ?other_quality ${KBVocab.rdfsSubClassOf} ?quality . - FILTER (?other_quality != ?quality) - } - } - """ - query.toQuery + def qualitySuperQualityQuery(termID: String): Query = { + val termIRI = IRI.create(termID) + select_distinct('quality) from "http://kb.phenoscape.org/" from "http://kb.phenoscape.org/closure" where bgp( + t(termIRI, rdfsSubClassOf, 'has_quality), + t('has_quality, has_part_some, 'quality), + t('quality, rdfsIsDefinedBy, PATO)) } - def entitiesForAnnotation(annotationID: String): Future[Iterable[String]] = - App.executeSPARQLQuery(annotationEntityTypesQuery(annotationID), _.getResource("bearer").getURI) + def entitiesForAnnotation(annotationID: String): Future[Iterable[String]] = { + val entityTypes = + App.executeSPARQLQuery(annotationEntityTypesQuery(annotationID), _.getResource("description").getURI) + for { + entityTypesResult <- entityTypes + entitySuperClasses <- superClassesForEntityTypes(entityTypesResult) + } yield { + val superclasses = HashMultiset.create[String] + entitySuperClasses.foreach(superclasses.add) + val nearestEntities = superclasses.entrySet.asScala.filter(_.getCount == 1).map(_.getElement) + nearestEntities.toVector + } + } + + def superClassesForEntityTypes(entityTypes: Iterable[String]): Future[Iterable[String]] = { + val superclasses = Future.sequence(entityTypes.map { entityType => + App.executeSPARQLQuery(entitySuperClassesQuery(entityType), _.getResource("bearer").getURI) + }) + for { result <- superclasses } yield result.flatten + } def annotationEntityTypesQuery(annotationID: String): Query = { val annotationIRI = IRI.create(annotationID) - val query = - sparql""" - SELECT DISTINCT ?bearer - FROM $KBMainGraph - FROM $KBRedundantRelationGraph - WHERE { - $annotationIRI $associationHasObject ?phenotype . - - ?phenotype $has_part_inhering_in ?bearer . - ?bearer $rdfsIsDefinedBy $Uberon . - - FILTER NOT EXISTS { - ?phenotype $has_part_inhering_in ?other_bearer . - ?other_bearer $rdfsIsDefinedBy $Uberon . - ?other_bearer ${KBVocab.rdfsSubClassOf} ?bearer . - FILTER (?other_bearer != ?bearer) - } - } - """ - query.toQuery + select_distinct('description) from "http://kb.phenoscape.org/" from "http://kb.phenoscape.org/closure" where bgp( + t(annotationIRI, rdfType / rdfsSubClassOf, 'description), + t('description, has_part_inhering_in_some, 'bearer), + t('bearer, rdfsIsDefinedBy, UBERON)) + } + def entitySuperClassesQuery(termID: String): Query = { + val termIRI = IRI.create(termID) + select_distinct('bearer) from "http://kb.phenoscape.org/" from "http://kb.phenoscape.org/closure" where bgp( + t(termIRI, rdfsSubClassOf, 'description), + t('description, has_part_inhering_in_some, 'bearer), + t('bearer, rdfsIsDefinedBy, UBERON)) } } diff --git a/src/main/scala/org/phenoscape/kb/Graph.scala b/src/main/scala/org/phenoscape/kb/Graph.scala index af37985c..2706b64b 100644 --- a/src/main/scala/org/phenoscape/kb/Graph.scala +++ b/src/main/scala/org/phenoscape/kb/Graph.scala @@ -11,90 +11,38 @@ import org.phenoscape.kb.Main.system.dispatcher import org.phenoscape.kb.Similarity.PhenotypeCorpus import org.phenoscape.sparql.SPARQLInterpolation._ import org.phenoscape.sparql.SPARQLInterpolationOWL._ +import org.phenoscape.owl.NamedRestrictionGenerator +import org.phenoscape.owlet.SPARQLComposer._ import scala.concurrent.Future import scala.language.postfixOps object Graph { - def propertyNeighborsForObject(term: IRI, property: IRI, direct: Boolean): Future[Seq[MinimalTerm]] = - App.executeSPARQLQuery(buildPropertyNeighborsQueryObject(term, property, direct), MinimalTerm.fromQuerySolution) + def propertyNeighborsForObject(term: IRI, property: IRI): Future[Seq[MinimalTerm]] = + App.executeSPARQLQuery(buildPropertyNeighborsQueryObject(term, property), MinimalTerm.fromQuerySolution) - def propertyNeighborsForSubject(term: IRI, property: IRI, direct: Boolean): Future[Seq[MinimalTerm]] = - App.executeSPARQLQuery(buildPropertyNeighborsQuerySubject(term, property, direct), MinimalTerm.fromQuerySolution) + def propertyNeighborsForSubject(term: IRI, property: IRI): Future[Seq[MinimalTerm]] = + App.executeSPARQLQuery(buildPropertyNeighborsQuerySubject(term, property), MinimalTerm.fromQuerySolution) - private def buildPropertyNeighborsQueryObject(focalTerm: IRI, property: IRI, direct: Boolean): Query = { - - val filterIndirectNeighbors = - sparql""" - FILTER NOT EXISTS { - ?other_term $property $focalTerm . - ?other_term $rdfsSubClassOf ?term . - FILTER(?other_term != ?term) - } - - FILTER NOT EXISTS { - $property $rdfType $transitiveProperty . - ?other_term $property $focalTerm . - ?other_term $property ?term . - FILTER(?other_term != ?term) - } - """ - - val filters = if (direct) filterIndirectNeighbors else sparql"" - - val query = - sparql""" - SELECT DISTINCT ?term ?term_label - FROM $KBMainGraph - FROM $KBClosureGraph - FROM $KBRedundantRelationGraph - WHERE { - ?term $property $focalTerm . - ?term $rdfsLabel ?term_label . - - $filters - } - """ - - query.toQuery + private def buildPropertyNeighborsQueryObject(focalTerm: IRI, property: IRI): Query = { + val classRelation = NamedRestrictionGenerator.getClassRelationIRI(property) + select_distinct('term, 'term_label) from "http://kb.phenoscape.org/" where bgp( + t('existential_node, classRelation, focalTerm), + t('existential_subclass, rdfsSubClassOf, 'existential_node), + t('existential_subclass, classRelation, 'term), + t('term, rdfsLabel, 'term_label) + ) } - private def buildPropertyNeighborsQuerySubject(focalTerm: IRI, property: IRI, direct: Boolean): Query = { - - val filterIndirectNeighbors = - sparql""" - FILTER NOT EXISTS { - $focalTerm $property ?other_term . - ?other_term $rdfsSubClassOf ?term . - FILTER(?other_term != ?term) - } - - FILTER NOT EXISTS { - $property $rdfType $transitiveProperty . - $focalTerm $property ?other_term . - ?other_term $property ?term . - FILTER(?other_term != ?term) - } - """ - - val filters = if (direct) filterIndirectNeighbors else sparql"" - - val query = - sparql""" - SELECT DISTINCT ?term ?term_label - FROM $KBMainGraph - FROM $KBClosureGraph - FROM $KBRedundantRelationGraph - WHERE { - $focalTerm $property ?term . - ?term $rdfsLabel ?term_label . - - $filters - } - """ - - query.toQuery + private def buildPropertyNeighborsQuerySubject(focalTerm: IRI, property: IRI): Query = { + val classRelation = NamedRestrictionGenerator.getClassRelationIRI(property) + select_distinct('term, 'term_label) from "http://kb.phenoscape.org/" where bgp( + t('existential_node, classRelation, focalTerm), + t('existential_node, rdfsSubClassOf, 'existential_superclass), + t('existential_superclass, classRelation, 'term), + t('term, rdfsLabel, 'term_label) + ) } def getTermSubsumerPairs(terms: Set[IRI], corpusOpt: Option[PhenotypeCorpus]): Future[Seq[(IRI, IRI)]] = { diff --git a/src/main/scala/org/phenoscape/kb/KBVocab.scala b/src/main/scala/org/phenoscape/kb/KBVocab.scala index 9f5d86e0..8e478988 100644 --- a/src/main/scala/org/phenoscape/kb/KBVocab.scala +++ b/src/main/scala/org/phenoscape/kb/KBVocab.scala @@ -59,7 +59,6 @@ object KBVocab { val KBMainGraph = IRI.create("http://kb.phenoscape.org/") val KBClosureGraph = IRI.create("http://kb.phenoscape.org/closure") - val KBRedundantRelationGraph = IRI.create("http://kb.phenoscape.org/property_graphs/redundant") val entityRoot = IRI.create("http://purl.obolibrary.org/obo/UBERON_0000061") val qualityRoot = IRI.create("http://purl.obolibrary.org/obo/PATO_0000001") diff --git a/src/main/scala/org/phenoscape/kb/Main.scala b/src/main/scala/org/phenoscape/kb/Main.scala index 7c3179f1..ebf5b535 100644 --- a/src/main/scala/org/phenoscape/kb/Main.scala +++ b/src/main/scala/org/phenoscape/kb/Main.scala @@ -262,19 +262,17 @@ object Main extends HttpApp with App { } ~ pathPrefix("property_neighbors") { path("object") { - parameters("term".as[IRI], "property".as[IRI], "direct".as[Boolean].?(false)) { - (term, property, direct) => - complete { - Graph.propertyNeighborsForObject(term, property, direct) - } + parameters("term".as[IRI], "property".as[IRI]) { (term, property) => + complete { + Graph.propertyNeighborsForObject(term, property) + } } } ~ path("subject") { - parameters("term".as[IRI], "property".as[IRI], "direct".as[Boolean].?(false)) { - (term, property, direct) => - complete { - Graph.propertyNeighborsForSubject(term, property, direct) - } + parameters("term".as[IRI], "property".as[IRI]) { (term, property) => + complete { + Graph.propertyNeighborsForSubject(term, property) + } } } } ~ diff --git a/src/main/scala/org/phenoscape/kb/Phenotype.scala b/src/main/scala/org/phenoscape/kb/Phenotype.scala index 528d5646..d7f754ad 100644 --- a/src/main/scala/org/phenoscape/kb/Phenotype.scala +++ b/src/main/scala/org/phenoscape/kb/Phenotype.scala @@ -1,26 +1,39 @@ package org.phenoscape.kb -import akka.util.Timeout -import org.apache.jena.query.{Query, QuerySolution} +import scala.jdk.CollectionConverters._ +import scala.concurrent.Future +import scala.concurrent.duration._ +import scala.language.postfixOps +import org.apache.jena.query.Query +import org.apache.jena.query.QuerySolution import org.phenoscape.kb.Facets.Facet -import org.phenoscape.kb.KBVocab.{rdfsLabel, rdfsSubClassOf, _} +import org.phenoscape.kb.KBVocab._ import org.phenoscape.kb.Main.system.dispatcher -import org.phenoscape.kb.queries.QueryUtil.{PhenotypicQuality, QualitySpec} import org.phenoscape.kb.queries.TaxonPhenotypes +import org.phenoscape.owl.NamedRestrictionGenerator +import org.phenoscape.owl.Vocab import org.phenoscape.owl.Vocab._ -import org.phenoscape.sparql.SPARQLInterpolation._ -import org.phenoscape.sparql.SPARQLInterpolationOWL._ +import org.phenoscape.kb.KBVocab.rdfsLabel +import org.phenoscape.kb.KBVocab.rdfsSubClassOf +import org.phenoscape.owlet.SPARQLComposer._ import org.semanticweb.owlapi.model.IRI -import spray.json.DefaultJsonProtocol._ +import com.google.common.collect.HashMultiset +import akka.util.Timeout +import org.phenoscape.kb.queries.QueryUtil.{PhenotypicQuality, QualitySpec} import spray.json._ - -import scala.concurrent.Future -import scala.concurrent.duration._ -import scala.language.postfixOps +import spray.json.DefaultJsonProtocol._ +import org.phenoscape.sparql.SPARQLInterpolationOWL._ +import org.phenoscape.sparql.SPARQLInterpolation.{QueryText, _} object Phenotype { - private implicit val timeout = Timeout(10 minutes) + implicit val timeout = Timeout(10 minutes) + + private val has_part_some = NamedRestrictionGenerator.getClassRelationIRI(Vocab.has_part.getIRI) + private val phenotype_of_some: IRI = NamedRestrictionGenerator.getClassRelationIRI(Vocab.phenotype_of.getIRI) + + private val has_part_inhering_in_some = + NamedRestrictionGenerator.getClassRelationIRI(Vocab.has_part_inhering_in.getIRI) def info(phenotype: IRI, annotatedStatesOnly: Boolean): Future[Phenotype] = { val eqsFuture = eqForPhenotype(phenotype) @@ -70,9 +83,11 @@ object Phenotype { } def eqForPhenotype(phenotype: IRI): Future[NearestEQSet] = { - val entitiesFuture = entitiesForPhenotype(phenotype, has_part_inhering_in.getIRI) - val generalEntitiesFuture = entitiesForPhenotype(phenotype, phenotype_of.getIRI) - //FIXME need to change to relatedEntities using has_part_towards_some; this must be added to the KB build + val entitiesFuture = entitiesForPhenotype(phenotype, has_part_inhering_in_some) + val generalEntitiesFuture = + entitiesForPhenotype(phenotype, + phenotype_of_some + ) //FIXME need to change to relatedEntities using has_part_towards_some; this must be added to the KB build //val relatedEntitiesFuture = ??? val qualitiesFuture = qualitiesForPhenotype(phenotype) for { @@ -87,15 +102,25 @@ object Phenotype { def entitiesForPhenotype(phenotype: IRI, relation: IRI): Future[Set[IRI]] = for { - entityTypesResult <- App.executeSPARQLQuery(phenotypeEntitiesQuery(phenotype, relation), - result => IRI.create(result.getResource("entity").getURI)) - } yield entityTypesResult.toSet + entityTypesResult <- App.executeSPARQLQuery(entitySuperClassesQuery(phenotype, relation), + result => IRI.create(result.getResource("description").getURI)) + entitySuperClasses <- superClassesForEntityTypes(entityTypesResult, relation) + } yield { + val superclasses = HashMultiset.create[IRI] + entitySuperClasses.foreach(superclasses.add) + superclasses.entrySet.asScala.filter(_.getCount == 1).map(_.getElement).toSet + } def qualitiesForPhenotype(phenotype: IRI): Future[Set[IRI]] = for { - qualities <- App.executeSPARQLQuery(phenotypeQualitiesQuery(phenotype), - result => IRI.create(result.getResource("quality").getURI)) - } yield qualities.toSet + superQualities <- App.executeSPARQLQuery(qualitySuperClasses(phenotype), + result => IRI.create(result.getResource("description").getURI)) + superSuperQualities <- superClassesForSuperQualities(superQualities) + } yield { + val superclasses = HashMultiset.create[IRI] + superSuperQualities.foreach(superclasses.add) + superclasses.entrySet.asScala.filter(_.getCount == 1).map(_.getElement).toSet + } def queryTaxonPhenotypes(entity: Option[IRI], quality: QualitySpec, @@ -212,41 +237,49 @@ object Phenotype { MinimalTerm(IRI.create(result.getResource("phenotype").getURI), Some(result.getLiteral("phenotype_label").getLexicalForm)) - private def phenotypeEntitiesQuery(phenotype: IRI, relation: IRI): Query = - sparql""" - SELECT DISTINCT ?entity - FROM $KBMainGraph - FROM $KBClosureGraph - FROM $KBRedundantRelationGraph - WHERE { - $phenotype $relation ?entity . - ?entity $rdfsIsDefinedBy $Uberon . - FILTER NOT EXISTS { - $phenotype $relation ?other_entity . - ?other_entity $rdfsSubClassOf ?entity . - ?other_entity $rdfsIsDefinedBy $Uberon . - FILTER(?other_entity != ?entity) - } - } - """.toQuery + private def entitySuperClassesQuery(phenotype: IRI, relation: IRI): Query = + select_distinct('description) from "http://kb.phenoscape.org/" from "http://kb.phenoscape.org/closure" where (bgp( + t(phenotype, rdfsSubClassOf, 'description), + t('description, relation, 'entity), + t('entity, rdfsIsDefinedBy, Uberon))) + + private def entityEntitySuperClassesQuery(phenotype: IRI, relation: IRI): Query = + select_distinct('entity) from "http://kb.phenoscape.org/" from "http://kb.phenoscape.org/closure" where (bgp( + t(phenotype, rdfsSubClassOf, 'description), + t('description, relation, 'entity), + t('entity, rdfsIsDefinedBy, Uberon))) + + private def superClassesForEntityTypes(entityTypes: Iterable[IRI], relation: IRI): Future[Iterable[IRI]] = { + val futureSuperclasses = Future.sequence( + entityTypes.map { entityType => + App.executeSPARQLQuery(entityEntitySuperClassesQuery(entityType, relation), + result => IRI.create(result.getResource("entity").getURI)) + } + ) + futureSuperclasses.map(_.flatten) + } + + private def superClassesForSuperQualities(superQualities: Iterable[IRI]): Future[Iterable[IRI]] = { + val futureSuperclasses = Future.sequence( + superQualities.map { superClass => + App.executeSPARQLQuery(qualityQualitySuperClasses(superClass), + result => IRI.create(result.getResource("quality").getURI)) + } + ) + futureSuperclasses.map(_.flatten) + } + + private def qualitySuperClasses(phenotype: IRI): Query = + select_distinct('description) from "http://kb.phenoscape.org/" from "http://kb.phenoscape.org/closure" where (bgp( + t(phenotype, rdfsSubClassOf, 'description), + t('description, has_part_some, 'quality), + t('quality, rdfsIsDefinedBy, PATO))) - private def phenotypeQualitiesQuery(phenotype: IRI): Query = - sparql""" - SELECT DISTINCT ?quality - FROM $KBMainGraph - FROM $KBClosureGraph - FROM $KBRedundantRelationGraph - WHERE { - $phenotype $has_part ?quality . - ?quality $rdfsIsDefinedBy $PATO . - FILTER NOT EXISTS { - $phenotype $has_part ?other_quality . - ?other_quality $rdfsSubClassOf ?quality . - ?other_quality $rdfsIsDefinedBy $PATO . - FILTER(?other_quality != ?quality) - } - } - """.toQuery + private def qualityQualitySuperClasses(phenotype: IRI): Query = + select_distinct('quality) from "http://kb.phenoscape.org/" from "http://kb.phenoscape.org/closure" where (bgp( + t(phenotype, rdfsSubClassOf, 'description), + t('description, has_part_some, 'quality), + t('quality, rdfsIsDefinedBy, PATO))) } diff --git a/src/main/scala/org/phenoscape/kb/Term.scala b/src/main/scala/org/phenoscape/kb/Term.scala index 0c13abe9..0aebc8a1 100644 --- a/src/main/scala/org/phenoscape/kb/Term.scala +++ b/src/main/scala/org/phenoscape/kb/Term.scala @@ -283,9 +283,10 @@ object Term { """ val partOfs = sparql""" - GRAPH $KBRedundantRelationGraph { - $iri $part_of ?term . - FILTER($iri != ?term) + ?container $PartOfSome ?term . + GRAPH $KBClosureGraph { + $iri $rdfsSubClassOf ?container . + FILTER($iri != ?container) FILTER(?term != $owlThing) } """ @@ -314,9 +315,11 @@ object Term { """ val parts = sparql""" - GRAPH $KBRedundantRelationGraph { - ?term $part_of $iri . - FILTER($iri != ?term) + ?query $PartOfSome $iri . + GRAPH $KBClosureGraph { + ?term $rdfsSubClassOf ?query . + FILTER(?query != ?term) + FILTER(?term != $owlThing) } """ val all = if (includeParts) sparql" { $subclasses } UNION { $parts } " else subclasses