From 8d46102c525105fa29cdd14e1f3ae585a7a50444 Mon Sep 17 00:00:00 2001 From: James McLaughlin Date: Fri, 30 Dec 2022 02:16:53 +0000 Subject: [PATCH] reification refactoring, don't merge axioms together, fixes issues with obo xrefs --- apitester4/pom.xml | 8 +++ .../ac/ebi/ols/apitester/Ols4ApiTester.java | 21 ++----- .../ebi/spot/ols/model/v1/V1OboSynonym.java | 53 +++++++++++++----- .../src/main/java/OntologyScanner.java | 41 ++++++++++---- .../src/main/java/OntologyWriter.java | 28 ++++++---- .../json2solr/src/main/java/JSON2Solr.java | 37 ++++++++---- .../java/uk/ac/ebi/owl2json/OwlGraph.java | 21 ++++++- .../annotators/ReifiedPropertyAnnotator.java | 6 +- .../ebi/owl2json/properties/PropertySet.java | 9 +-- .../owl2json/properties/PropertyValue.java | 7 ++- frontend/src/model/Reified.ts | 56 +++++++++++-------- frontend/src/pages/ontologies/EntityPage.tsx | 42 +++++++------- pom.xml | 1 + 13 files changed, 209 insertions(+), 121 deletions(-) diff --git a/apitester4/pom.xml b/apitester4/pom.xml index 270b56270..4013bff6e 100644 --- a/apitester4/pom.xml +++ b/apitester4/pom.xml @@ -62,6 +62,14 @@ + + org.apache.maven.plugins + maven-compiler-plugin + + 11 + 11 + + diff --git a/apitester4/src/main/java/uk/ac/ebi/ols/apitester/Ols4ApiTester.java b/apitester4/src/main/java/uk/ac/ebi/ols/apitester/Ols4ApiTester.java index d32f83c1a..eb19e17dc 100644 --- a/apitester4/src/main/java/uk/ac/ebi/ols/apitester/Ols4ApiTester.java +++ b/apitester4/src/main/java/uk/ac/ebi/ols/apitester/Ols4ApiTester.java @@ -7,11 +7,7 @@ import java.nio.file.Paths; import java.nio.file.Path; import java.nio.file.Files; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.List; -import java.util.TreeSet; +import java.util.*; import java.util.Map.Entry; import com.google.gson.*; @@ -379,16 +375,11 @@ public JsonElement deepSort(JsonElement element) { elems[i] = deepSort(arr.get(i)); } - Arrays.sort(elems, new Comparator() { - - public int compare(JsonElement a, JsonElement b) { - return gson.toJson(a).compareTo(gson.toJson(b)); - } - }); + Arrays.sort(elems, Comparator.comparing(elem -> gson.toJson(elem))); JsonArray res = new JsonArray(); - for(int i = 0; i < arr.size(); ++ i) { + for(int i = 0; i < elems.length; ++ i) { res.add(elems[i]); } @@ -398,11 +389,7 @@ public int compare(JsonElement a, JsonElement b) { JsonObject obj = element.getAsJsonObject(); - TreeSet sortedKeys = new TreeSet(); - - for(String key : obj.keySet()) { - sortedKeys.add(key); - } + TreeSet sortedKeys = new TreeSet(obj.keySet()); JsonObject res = new JsonObject(); diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/model/v1/V1OboSynonym.java b/backend/src/main/java/uk/ac/ebi/spot/ols/model/v1/V1OboSynonym.java index 0b1e6f2e5..37de12822 100644 --- a/backend/src/main/java/uk/ac/ebi/spot/ols/model/v1/V1OboSynonym.java +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/model/v1/V1OboSynonym.java @@ -3,6 +3,8 @@ import uk.ac.ebi.spot.ols.service.OboDatabaseUrlService; import uk.ac.ebi.spot.ols.service.OntologyEntity; +import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -21,11 +23,13 @@ public static List extractFromEntity(OntologyEntity entity, OboDat List synonyms = exact.stream().map(synonym -> fromSynonymObject(synonym, "hasExactSynonym", oboDbUrls)) + .flatMap(Collection::stream) .filter(synonym -> synonym.type != null || synonym.xrefs != null) .collect(Collectors.toList()); synonyms.addAll( related.stream().map(synonym -> fromSynonymObject(synonym, "hasRelatedSynonym", oboDbUrls)) + .flatMap(Collection::stream) .filter(synonym -> synonym.type != null || synonym.xrefs != null) .collect(Collectors.toList()) ); @@ -33,35 +37,54 @@ public static List extractFromEntity(OntologyEntity entity, OboDat return synonyms.size() > 0 ? synonyms : null; } - private static V1OboSynonym fromSynonymObject(Object synonymObj, String scope, OboDatabaseUrlService oboDbUrls) { + private static List fromSynonymObject(Object synonymObj, String scope, OboDatabaseUrlService oboDbUrls) { if(synonymObj instanceof String) { V1OboSynonym synonym = new V1OboSynonym(); synonym.name = (String)synonymObj; synonym.scope = scope; - return synonym; + return List.of(synonym); } + List synonyms = new ArrayList<>(); + Map synonymMap = (Map) synonymObj; - V1OboSynonym synonym = new V1OboSynonym(); - synonym.name = (String)synonymMap.get("value"); - synonym.scope = scope; - synonym.type = (String)synonymMap.get("oboSynonymTypeName"); + List axioms = (List) synonymMap.get("axioms"); - Object xrefs = synonymMap.get("http://www.geneontology.org/formats/oboInOwl#hasDbXref"); + if(axioms == null) { + throw new RuntimeException("axioms were null"); + } - if(xrefs != null) { - if(! (xrefs instanceof List)) { - xrefs = List.of(xrefs); - } + for(Object axiomObj : axioms) { + + Map axiom = (Map) axiomObj; + + Object xrefs = axiom.get("http://www.geneontology.org/formats/oboInOwl#hasDbXref"); + + if(xrefs != null) { + if(! (xrefs instanceof List)) { + xrefs = List.of(xrefs); + } - synonym.xrefs = - ((List) xrefs).stream().map(xref -> V1OboXref.fromString((String) xref, oboDbUrls)) - .collect(Collectors.toList()); + List xrefObjs = + ((List) xrefs).stream().map(xref -> V1OboXref.fromString((String) xref, oboDbUrls)) + .collect(Collectors.toList()); + + for(V1OboXref xrefObj : xrefObjs) { + V1OboSynonym synonym = new V1OboSynonym(); + + synonym.name = (String)synonymMap.get("value"); + synonym.scope = scope; + synonym.type = (String)axiom.get("oboSynonymTypeName"); + synonym.xrefs = List.of(xrefObj); + + synonyms.add(synonym); + } + } } - return synonym; + return synonyms; } } diff --git a/dataload/json2neo/src/main/java/OntologyScanner.java b/dataload/json2neo/src/main/java/OntologyScanner.java index 4be21e0b5..3bb39ebfe 100644 --- a/dataload/json2neo/src/main/java/OntologyScanner.java +++ b/dataload/json2neo/src/main/java/OntologyScanner.java @@ -129,6 +129,10 @@ public static Result scanOntology(JsonReader reader) throws IOException { private static void visitValue(String predicate, Object value, Set outProps, Set outEdgeProps) { + if(predicate.equals("iriToLabels")) { + return; + } + if(value instanceof String) { } else if(value instanceof List) { @@ -140,14 +144,20 @@ private static void visitValue(String predicate, Object value, Set outPr } else if(value instanceof Map) { - // either reification, or a bnode (anon. class or restriction) + // could be a typed literal, a relatedTo object, reification, or a bnode Map mapValue = new TreeMap((Map) value); - if(mapValue.containsKey("value")) { + List types = (List) mapValue.get("type"); + + if(types == null) { + // bnode (anon. class) + return; + } - // either reification (an owl axiom) OR a langString + if(types.contains("literal")) { + // is this a localization? if(mapValue.containsKey("lang")) { String lang = (String)mapValue.get("lang"); @@ -159,27 +169,34 @@ private static void visitValue(String predicate, Object value, Set outPr if(!lang.equals("en")) { outProps.add(lang + "+" + predicate); } + } + + } else if(types.contains("related")) { + + visitValue(predicate, mapValue.get("value"), outProps, outEdgeProps); - } else { + } else if(types.contains("reification")) { + + List axioms = (List) mapValue.get("axioms"); + + for(Object axiomObj : axioms) { + + Map axiom = (Map) axiomObj; - // reification (owl:Axiom or relatedTo) - // predicates used to describe the edge itself - for(String edgePredicate : mapValue.keySet()) { + for(String edgePredicate : axiom.keySet()) { - if(edgePredicate.equals("value")) + if(edgePredicate.equals("type")) continue; outEdgeProps.add(edgePredicate); } - } + } visitValue(predicate, mapValue.get("value"), outProps, outEdgeProps); } else { - - // bnode (anon. class or restriction) - + throw new RuntimeException("???"); } } diff --git a/dataload/json2neo/src/main/java/OntologyWriter.java b/dataload/json2neo/src/main/java/OntologyWriter.java index c383de793..a16841331 100644 --- a/dataload/json2neo/src/main/java/OntologyWriter.java +++ b/dataload/json2neo/src/main/java/OntologyWriter.java @@ -164,23 +164,32 @@ public void maybeWriteEdges(String subject, String property, Object value) throw for(Object v : values) { if (v instanceof Map) { - // maybe axiom + Map mapValue = (Map) v; - if (mapValue.containsKey("value") && !mapValue.containsKey("lang")) { - // axiom - Object axiomValue = mapValue.get("value"); - assert (axiomValue instanceof String); + List types = (List) mapValue.get("type"); + + if(types != null && types.contains("reification")) { + + // reification + Object reifiedValue = mapValue.get("value"); + assert (reifiedValue instanceof String); + + List> axioms = (List>) mapValue.get("axioms"); + assert(axioms != null); // is the value the URI of something that exists in the ontology? - if (ontologyScannerResult.uriToTypes.containsKey(axiomValue)) { - printEdge(ontologyId, subject, property, axiomValue, mapValue); + if (ontologyScannerResult.uriToTypes.containsKey(reifiedValue)) { + // create one edge for each axiom + for(Map axiom : axioms) { + printEdge(ontologyId, subject, property, reifiedValue, axiom); + } } } } else if (v instanceof String) { // is the value the URI of something that exists in the ontology? if (ontologyScannerResult.uriToTypes.containsKey(v)) { - printEdge(ontologyId, subject, property, v, new TreeMap<>()); + printEdge(ontologyId, subject, property, v, Map.of()); } } else { @@ -209,8 +218,7 @@ private void printEdge(String ontologyId, String aUri, String predicate, Object // In the hacky approach below, we just make multiple edges: in the above example, // one edge would point to the Class and another would point to the Individual. // - // TODO: We should instead look up "predicate" and find out what the semantics - // of the property are. + // TODO: fix // Set aTypes = ontologyScannerResult.uriToTypes.get(aUri); Set bTypes = ontologyScannerResult.uriToTypes.get(bUri); diff --git a/dataload/json2solr/src/main/java/JSON2Solr.java b/dataload/json2solr/src/main/java/JSON2Solr.java index aad62438d..a30bb17ab 100644 --- a/dataload/json2solr/src/main/java/JSON2Solr.java +++ b/dataload/json2solr/src/main/java/JSON2Solr.java @@ -284,19 +284,19 @@ static private void flattenProperties(Map properties, Map properties, Map dict = (Map) obj; - if (dict.containsKey("value")) { + List types = (List) dict.get("type"); + + if(types == null) { + // (2) class expression + return null; + } + + if(types.contains("literal")) { + if(dict.containsKey("lang")) { // (3) localisation String valLang = (String)dict.get("lang"); @@ -320,15 +329,23 @@ public static Object discardMetadata(Object obj, String lang) { return null; } } - // (1) datatyped or (4) reification + + // (1) typed literal return discardMetadata(dict.get("value"), lang); - } else { - // (2) class expression - return null; + + } else if(types.contains("reification") || types.contains("related")) { + + // (4) reification + return discardMetadata(dict.get("value"), lang); + + } else { + throw new RuntimeException("???"); } - } - return obj; + } else { + + return obj; + } } // Gather all of the lang: attributes from an object and all of its descendants diff --git a/dataload/owl2json/src/main/java/uk/ac/ebi/owl2json/OwlGraph.java b/dataload/owl2json/src/main/java/uk/ac/ebi/owl2json/OwlGraph.java index 95c0d6afe..ca5823317 100644 --- a/dataload/owl2json/src/main/java/uk/ac/ebi/owl2json/OwlGraph.java +++ b/dataload/owl2json/src/main/java/uk/ac/ebi/owl2json/OwlGraph.java @@ -440,12 +440,23 @@ private void writeProperties(JsonWriter writer, PropertySet properties, Set types) throws IOException { - if (value.properties != null) { + if (value.axioms.size() > 0) { // reified writer.beginObject(); + writer.name("type"); + writer.beginArray(); + writer.value("reification"); + writer.endArray(); writer.name("value"); writeValue(writer, value); - writeProperties(writer, value.properties, types); + writer.name("axioms"); + writer.beginArray(); + for(PropertySet axiom : value.axioms) { + writer.beginObject(); + writeProperties(writer, axiom, null); + writer.endObject(); + } + writer.endArray(); writer.endObject(); } else { // not reified @@ -455,7 +466,7 @@ public void writePropertyValue(JsonWriter writer, PropertyValue value, Set v2 = c.properties.getPropertyValues(p2); for (PropertyValue prop : v2) { if (!p2.equals("http://www.w3.org/2002/07/owl#annotatedSource") && !p2.equals("http://www.w3.org/2002/07/owl#annotatedProperty") && !p2.equals("http://www.w3.org/2002/07/owl#annotatedTarget")) { - sourceNode.properties.annotateProperty(propertyUri, target, p2, prop, graph); + axiom.addProperty(p2, prop); } } } + + sourceNode.properties.annotatePropertyWithAxiom(propertyUri, target, axiom, graph); } } long endTime3 = System.nanoTime(); diff --git a/dataload/owl2json/src/main/java/uk/ac/ebi/owl2json/properties/PropertySet.java b/dataload/owl2json/src/main/java/uk/ac/ebi/owl2json/properties/PropertySet.java index d47a2d8c9..3816e7bb1 100644 --- a/dataload/owl2json/src/main/java/uk/ac/ebi/owl2json/properties/PropertySet.java +++ b/dataload/owl2json/src/main/java/uk/ac/ebi/owl2json/properties/PropertySet.java @@ -35,8 +35,7 @@ public boolean hasProperty(String predicate) { return properties.containsKey(predicate); } - public void annotateProperty(String predicate, PropertyValue value, String predicate2, PropertyValue value2, - OwlGraph graph) { + public void annotatePropertyWithAxiom(String predicate, PropertyValue value, PropertySet axiom, OwlGraph graph) { List props = properties.get(predicate); @@ -74,11 +73,7 @@ public void annotateProperty(String predicate, PropertyValue value, String predi properties.put(predicate, props); } - if (prop.properties == null) { - prop.properties = new PropertySet(); - } - - prop.properties.addProperty(predicate2, value2); + prop.axioms.add(axiom); } public Set getPropertyPredicates() { diff --git a/dataload/owl2json/src/main/java/uk/ac/ebi/owl2json/properties/PropertyValue.java b/dataload/owl2json/src/main/java/uk/ac/ebi/owl2json/properties/PropertyValue.java index 8b9f4ddd5..88890dec6 100644 --- a/dataload/owl2json/src/main/java/uk/ac/ebi/owl2json/properties/PropertyValue.java +++ b/dataload/owl2json/src/main/java/uk/ac/ebi/owl2json/properties/PropertyValue.java @@ -1,5 +1,8 @@ package uk.ac.ebi.owl2json.properties; +import java.util.ArrayList; +import java.util.List; + import org.apache.jena.graph.Node; import uk.ac.ebi.owl2json.OwlGraph; import uk.ac.ebi.owl2json.OwlNode; @@ -14,8 +17,8 @@ public enum Type { RELATED } - // further properties (for reification) - public PropertySet properties = null; + // reification + public List axioms = new ArrayList<>(); public static PropertyValue fromJenaNode(Node node) { diff --git a/frontend/src/model/Reified.ts b/frontend/src/model/Reified.ts index e32a22794..a9e849d8e 100644 --- a/frontend/src/model/Reified.ts +++ b/frontend/src/model/Reified.ts @@ -1,10 +1,10 @@ export default class Reified { value: T; - metadata: any | null; + axioms: any[]|null - private constructor(value: T, metadata: any) { + private constructor(value: T, axioms: any[]|null) { this.value = value; - this.metadata = metadata; + this.axioms = axioms } public static fromJson(jsonNode: any): Reified[] { @@ -17,26 +17,36 @@ export default class Reified { } return jsonNode.map((value: any) => { - // is this a reification? - if (typeof value === "object" && value.value !== undefined) { - // yes, separate out the metadata from the value - let theValue = value.value; - let metadata: any = {}; - - for (let k of Object.keys(value)) { - if ( - k === "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" || - k === "value" - ) - continue; - metadata[k] = value[k]; - } - - return new Reified(theValue, metadata); - } else { - // no, just return the value - return new Reified(value, null); - } + + if(value.type.contains("reification")) { + return new Reified(value.value, value.axioms) + } else { + return new Reified(value, null) + } + }); } + + getMetadata():any|null { + + if(!this.axioms) { + return null; + } + + let metadata:any = {} + + for(let axiom of this.axioms) { + for(let k of Object.keys(axiom)) { + let v = axiom[k] + let existing:any[]|undefined = metadata[k] + if(existing !== undefined) { + existing.push(v) + } else { + metadata[k] = [v] + } + } + } + + return metadata; + } } diff --git a/frontend/src/pages/ontologies/EntityPage.tsx b/frontend/src/pages/ontologies/EntityPage.tsx index 67de9edd0..03350cc43 100644 --- a/frontend/src/pages/ontologies/EntityPage.tsx +++ b/frontend/src/pages/ontologies/EntityPage.tsx @@ -121,22 +121,22 @@ export default function EntityPage({ .getDescriptionAsArray() .map((definition: Reified) => { const hasMetadata = - definition.metadata?.iriToLabels && - Object.keys(definition.metadata).length > 0 && - Object.keys(definition.metadata.iriToLabels).length > 0; + definition.getMetadata()?.iriToLabels && + Object.keys(definition.getMetadata()).length > 0 && + Object.keys(definition.getMetadata().iriToLabels).length > 0; return ( {definition.value} {hasMetadata ? ( { - if (definition.metadata.iriToLabels[key]) { + if (definition.getMetadata().iriToLabels[key]) { return ( "*" + - definition.metadata[key] + + definition.getMetadata()[key] + " (" + - definition.metadata.iriToLabels[ + definition.getMetadata().iriToLabels[ key ][0].replaceAll("_", " ") + ")" @@ -164,9 +164,9 @@ export default function EntityPage({ .getSynonyms() .map((synonym: Reified) => { const hasMetadata = - synonym.metadata?.iriToLabels && - Object.keys(synonym.metadata).length > 0 && - Object.keys(synonym.metadata.iriToLabels).length > 0; + synonym.getMetadata()?.iriToLabels && + Object.keys(synonym.getMetadata()).length > 0 && + Object.keys(synonym.getMetadata().iriToLabels).length > 0; return (
{ - if (synonym.metadata.iriToLabels[key]) { + if (synonym.getMetadata().iriToLabels[key]) { return ( "*" + - synonym.metadata[key] + + synonym.getMetadata()[key] + " (" + - synonym.metadata.iriToLabels[ + synonym.getMetadata().iriToLabels[ key ][0].replaceAll("_", " ") + ")" @@ -348,9 +348,9 @@ export default function EntityPage({
    {entity.getParents().map((parent: Reified) => { const hasMetadata = - parent.metadata?.iriToLabels && - Object.keys(parent.metadata).length > 0 && - Object.keys(parent.metadata.iriToLabels).length > + parent.getMetadata()?.iriToLabels && + Object.keys(parent.getMetadata()).length > 0 && + Object.keys(parent.getMetadata().iriToLabels).length > 0; return (
  • @@ -360,14 +360,14 @@ export default function EntityPage({ /> {hasMetadata ? ( { - if (parent.metadata.iriToLabels[key]) { + if (parent.getMetadata().iriToLabels[key]) { return ( "*" + - parent.metadata[key] + + parent.getMetadata()[key] + " (" + - parent.metadata.iriToLabels[ + parent.getMetadata().iriToLabels[ key ][0].replaceAll("_", " ") + ")" diff --git a/pom.xml b/pom.xml index 5e81a8a31..24f071ab5 100644 --- a/pom.xml +++ b/pom.xml @@ -10,6 +10,7 @@ dataload backend + apitester4