Closed jamesamcl closed 1 year ago
In OLS3 this seems to be based on "related from" relations.
String relatedGraphQuery = "MATCH path = (n:Class)-[r:SUBCLASSOF|Related]-(parent)\n"+
"WHERE n.ontology_name = {0} AND n.iri = {1}\n"+
"UNWIND nodes(path) as p\n" +
"UNWIND rels(path) as r1\n" +
"RETURN {nodes: collect( distinct {iri: p.iri, label: p.label})[0..200], " +
"edges: collect (distinct {source: startNode(r1).iri, target: endNode(r1).iri, label: r1.label, uri: r1.uri} )[0..200]} as result";
String relatedFromQuery = "MATCH (x)-[r:Related]->(n:Class) WHERE n.ontology_name = {0} AND n.iri = {1} RETURN r.label as relation, collect( {iri: x.iri, label: x.label})[0..99] as terms limit 100";
String usageQuery = "MATCH (n:Resource)<-[r:REFERSTO]-(x) WHERE n.iri = {0} RETURN distinct ({name: x.ontology_name, prefix: x.ontology_prefix}) as usage";
private Collection<Individual> instances;
public Object getGraphJson(String ontologyName, String iri, int distance) {
Map<String, Object> paramt = new HashMap<>();
paramt.put("0", ontologyName);
paramt.put("1", iri);
Result res = graphDatabaseService.execute(relatedGraphQuery, paramt);
return res.next().get("result");
}
public Map<String, Collection<Map<String, String>>> getRelatedFrom(String ontologyId, String iri) {
Map<String, Object> paramt = new HashMap<>();
paramt.put("0", ontologyId);
paramt.put("1", iri);
Result res = graphDatabaseService.execute(relatedFromQuery, paramt);
Map<String, Collection<Map<String, String>>> relatedFromMap = new HashMap<>();
while (res.hasNext()) {
Map<String, Object> r = res.next();
String relationLabel = r.get("relation").toString();
relatedFromMap.put(relationLabel, (Collection<Map<String, String>>) r.get("terms"));
}
return relatedFromMap;
}
public Collection<Map<String, String>> getOntologyUsage (String iri) {
Map<String, Object> paramt = new HashMap<>();
paramt.put("0", iri);
Result res = graphDatabaseService.execute(usageQuery,paramt);
Collection<Map<String, String>> usageInfo = new HashSet<>();
while (res.hasNext()) {
Map<String, Object> r = res.next();
usageInfo.add((Map<String, String>) r.get("usage"));
}
return usageInfo;
}
Everything else just uses the termRepository methods which should already be implemented.
Here is what Related relations look like in OLS3:
@RelationshipEntity (type = "Related")
public class Related {
@GraphId
Long id;
String uri;
String label;
@GraphProperty(propertyName="ontology_name")
@JsonProperty(value = "ontology_name")
String ontologyName;
@StartNode
private Term relatedFrom;
@EndNode
private Term relatedTo;
Seems to be populated like this in AbstractOWLOntologyLoader:
// map of related parent terms for hierarchy views
Map<IRI, Collection<IRI>> relatedParentTerms = new HashMap<>();
// find direct related terms
Map<IRI, Collection<IRI>> relatedTerms = new HashMap<>();
Map<IRI, Collection<IRI>> relatedIndividualsToClasses = new HashMap<>();
Set<String> relatedDescriptions = new HashSet<>();
EntitySearcher.getSuperClasses(owlClass, getManager().ontologies()).forEach(expression -> {
// only want existential with named class as filler
if (expression.isAnonymous()) {
if (expression instanceof OWLObjectSomeValuesFrom) {
OWLObjectSomeValuesFrom someValuesFrom = (OWLObjectSomeValuesFrom) expression;
if (!someValuesFrom.getFiller().isAnonymous() && !someValuesFrom.getProperty().isAnonymous()) {
IRI propertyIRI = someValuesFrom.getProperty().asOWLObjectProperty().getIRI();
IRI relatedTerm = someValuesFrom.getFiller().asOWLClass().getIRI();
// skip terms that are related to themselves as this can cause nasty cycles
if (!relatedTerms.containsKey(propertyIRI)) {
relatedTerms.put(propertyIRI, new HashSet<>());
}
relatedTerms.get(propertyIRI).add(relatedTerm);
// check if hierarchical
if (hierarchicalRels.contains(propertyIRI) || isPartOf(propertyIRI)) {
if (owlClass.getIRI().equals(relatedTerm)) {
getLogger().warn("Ignoring Iri that is related to itself: " + owlClass.getIRI());
} else {
if (!relatedParentTerms.containsKey(propertyIRI)) {
relatedParentTerms.put(propertyIRI, new HashSet<>());
}
relatedParentTerms.get(propertyIRI).add(relatedTerm);
addRelatedChildTerm(relatedTerm, owlClass.getIRI());
}
}
} else if (someValuesFrom.getFiller().isAnonymous() && !someValuesFrom.getProperty().isAnonymous()) {
indexTermToIndividualRelations(someValuesFrom, relatedIndividualsToClasses);
}
} else if (expression instanceof OWLObjectHasValue) {
OWLObjectSomeValuesFrom someValuesFrom = (OWLObjectSomeValuesFrom) ((OWLObjectHasValue) expression).asSomeValuesFrom();
indexTermToIndividualRelations(someValuesFrom, relatedIndividualsToClasses);
}
// store stringified form of class description
relatedDescriptions.add(renderHtml(expression));
}
});
if (!relatedTerms.isEmpty()) {
setRelatedTerms(owlClass.getIRI(), relatedTerms);
}
if (!relatedIndividualsToClasses.isEmpty()) {
setRelatedIndividualsToClasses(owlClass.getIRI(), relatedIndividualsToClasses);
}
setRelatedParentTerms(owlClass.getIRI(), relatedParentTerms);
if (!relatedDescriptions.isEmpty()) {
setSuperClassDescriptions(owlClass.getIRI(), relatedDescriptions);
}
// todo find transitive closure of related terms
The distance
parameter is not implemented at all in OLS3. Instead it seems to return the first 200 nodes and edges. Excerpt from above:
String relatedGraphQuery = "MATCH path = (n:Class)-[r:SUBCLASSOF|Related]-(parent)\n"+
"WHERE n.ontology_name = {0} AND n.iri = {1}\n"+
"UNWIND nodes(path) as p\n" +
"UNWIND rels(path) as r1\n" +
"RETURN {nodes: collect( distinct {iri: p.iri, label: p.label})[0..200], " +
"edges: collect (distinct {source: startNode(r1).iri, target: endNode(r1).iri, label: r1.label, uri: r1.uri} )[0..200]} as result";
I suppose there are two separate issues here:
1. Implementing the graph functionality in the backwards compatible OLS3 API. I will do this with just subClassOf to begin with. This is now implemented for directParent
.
For legacy plugins
https://www.ebi.ac.uk/ols/api/ontologies/efo/terms/http%253A%252F%252Fwww.ebi.ac.uk%252Fefo%252FEFO_0000400/graph