Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Annotations_trimmed, zooma fix #595

Merged
merged 11 commits into from
Nov 27, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ public static List<String> mapFieldsList(Collection<String> ols3FieldNames) {
continue;
}

if (legacyFieldName.equals("_json")) {
newFields.add("_json" + suffix);
if (legacyFieldName.equals("annotations_trimmed")) {
newFields.add(prefix + "searchableAnnotationValues" + suffix);
continue;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@
import uk.ac.ebi.spot.ols.repository.transforms.RemoveLiteralDatatypesTransform;
import uk.ac.ebi.spot.ols.repository.v1.JsonHelper;
import uk.ac.ebi.spot.ols.repository.v1.V1OntologyRepository;
import uk.ac.ebi.spot.ols.repository.v1.mappers.AnnotationExtractor;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
* @author Simon Jupp
Expand Down Expand Up @@ -73,7 +75,7 @@ public void search(
if (queryFields == null) {
// if exact just search the supplied fields for exact matches
if (exact) {
String[] fields = {"label_s", "synonym_s", "short_form_s", "obo_id_s", "iri_s", "_json"};
String[] fields = {"label_s", "synonym_s", "short_form_s", "obo_id_s", "iri_s", "annotations_trimmed"};
solrQuery.setQuery(
"((" +
createUnionQuery(query.toLowerCase(), SolrFieldMapper.mapFieldsList(List.of(fields)).toArray(new String[0]), true)
Expand All @@ -85,7 +87,7 @@ public void search(
solrQuery.set("defType", "edismax");
solrQuery.setQuery(query);

String[] fields = {"label^5", "synonym^3", "definition", "short_form^2", "obo_id^2", "iri", "_json"};
String[] fields = {"label^5", "synonym^3", "definition", "short_form^2", "obo_id^2", "iri", "annotations_trimmed"};

solrQuery.set("qf", String.join(" ", SolrFieldMapper.mapFieldsList(List.of(fields))));

Expand All @@ -106,7 +108,10 @@ public void search(
}
}

solrQuery.setFields("_json");
if (fieldList.contains("score"))
solrQuery.setFields("_json","score");
else
solrQuery.setFields("_json");

if (ontologies != null && !ontologies.isEmpty()) {

Expand Down Expand Up @@ -231,11 +236,26 @@ public void search(
if (fieldList.contains("synonym")) outDoc.put("synonym", JsonHelper.getStrings(json, "synonym"));
if (fieldList.contains("ontology_prefix")) outDoc.put("ontology_prefix", JsonHelper.getString(json, "ontologyPreferredPrefix"));
if (fieldList.contains("subset")) outDoc.put("subset", JsonHelper.getStrings(json, "http://www.geneontology.org/formats/oboInOwl#inSubset"));
if (fieldList.contains("ontology_iri")) outDoc.put("ontology_iri", JsonHelper.getStrings(json, "ontologyIri").get(0));
if (fieldList.contains("score")) outDoc.put("score", res.get("score"));

// Include annotations that were specified with <field>_annotation
boolean anyAnnotations = fieldList.stream()
.anyMatch(s -> s.endsWith("_annotation"));
if (anyAnnotations) {
Stream<String> annotationFields = fieldList.stream().filter(s -> s.endsWith("_annotation"));
Map<String, Object> termAnnotations = AnnotationExtractor.extractAnnotations(json);

annotationFields.forEach(annotationName -> {
// Remove _annotation suffix to get plain annotation name
String fieldName = annotationName.replaceFirst("_annotation$", "");
outDoc.put(annotationName, termAnnotations.get(fieldName));
});
}

docs.add(outDoc);
}


Map<String, Object> responseHeader = new HashMap<>();
responseHeader.put("status", 0);
responseHeader.put("QTime", qr.getQTime());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public static void addSearchFieldsToQuery(OlsSolrQuery query, String searchField
query.addSearchField("id", 1, SearchType.WHITESPACE_EDGES);
query.addSearchField("oboId", 1, SearchType.WHITESPACE_EDGES);
query.addSearchField("synonym", 1, SearchType.WHITESPACE_EDGES);
query.addSearchField("searchableAnnotationValues", 1, SearchType.WHITESPACE_EDGES);
} else {
for (ParsedField field : parseFieldsString(searchFields)) {
query.addSearchField(field.property, field.weight, SearchType.CASE_INSENSITIVE_TOKENS);
Expand Down
4 changes: 3 additions & 1 deletion dataload/json2neo/src/main/java/OntologyWriter.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ public class OntologyWriter {

public static final Set<String> PROPERTY_BLACKLIST = Set.of(
// large and doesn't get queried
"appearsIn"
"appearsIn",
// all property values together, this is for solr and not useful in neo4j
"searchableAnnotationValues"
);

public static final Set<String> EDGE_BLACKLIST = Set.of(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ private String urlToFilename(String url) {
long endTime = System.nanoTime();
System.out.println("load ontology: " + ((endTime - startTime) / 1000 / 1000 / 1000));

SearchableAnnotationValuesAnnotator.annotateSearchableAnnotationValues(this);
InverseOfAnnotator.annotateInverseOf(this);
NegativePropertyAssertionAnnotator.annotateNegativePropertyAssertions(this);
OboSynonymTypeNameAnnotator.annotateOboSynonymTypeNames(this); // n.b. this one labels axioms so must run before the ReifiedPropertyAnnotator
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package uk.ac.ebi.rdf2json.annotators;

import uk.ac.ebi.rdf2json.OntologyGraph;
import uk.ac.ebi.rdf2json.OntologyNode;
import uk.ac.ebi.rdf2json.properties.PropertyValue;

import java.util.ArrayList;
import java.util.List;

import static uk.ac.ebi.rdf2json.properties.PropertyValue.Type.LITERAL;

public class SearchableAnnotationValuesAnnotator {

// Roughly equivalent to "annotations_trimmed" in OLS3.
//
// A field that contains a list of just the values (no predicates) of all of the "annotations" (which is not a well
// defined term, so we have to make it up) of an entity.
//
// This field is used for solr searching, so that you can search for the value of any property (regardless of how
// important OLS thinks it is), and still expect a result.
//
public static void annotateSearchableAnnotationValues(OntologyGraph graph) {

long startTime3 = System.nanoTime();
for(String id : graph.nodes.keySet()) {
OntologyNode c = graph.nodes.get(id);
if(c.types.contains(OntologyNode.NodeType.CLASS) ||
c.types.contains(OntologyNode.NodeType.PROPERTY) ||
c.types.contains(OntologyNode.NodeType.INDIVIDUAL) ||
c.types.contains(OntologyNode.NodeType.ONTOLOGY)) {

List<PropertyValue> values = new ArrayList<>();

for(var predicate : c.properties.getPropertyPredicates()) {

// namespaces that are NOT considered annotations for this exercise...
//
if(predicate.startsWith("http://www.w3.org/1999/02/22-rdf-syntax-ns#")
|| predicate.startsWith("http://www.w3.org/2000/01/rdf-schema#")
|| predicate.startsWith("http://www.w3.org/2002/07/owl#")) {

continue;
}

for(var value : c.properties.getPropertyValues(predicate)) {
if(value.getType().equals(LITERAL)) {
values.add(value);
}
}
}

for(var value : values) {
c.properties.addProperty("searchableAnnotationValues", value);
}
}
}

long endTime3 = System.nanoTime();
System.out.println("annotate searchable annotation values: " + ((endTime3 - startTime3) / 1000 / 1000 / 1000));
}
}
2 changes: 1 addition & 1 deletion dataload/solr_config/ols4_entities/conf/managed-schema.xml
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@
<!-- OLS4: we are using dynamic fields for everything so no need to specify much of a schema.
however we do need to specify the fields we DON'T want to be indexed
-->
<field name="_json" type="ols4json" indexed="true" stored="true" multiValued="false" />
<field name="_json" type="ols4json" indexed="false" stored="true" multiValued="false" />

<!-- Type used for data-driven schema, to add a string copy for each text field -->
<dynamicField name="str_*" type="strings" stored="false" docValues="true" indexed="false" useDocValuesAsStored="false"/>
Expand Down
2 changes: 1 addition & 1 deletion frontend/index.html.in
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
content="OLS is a repository for biomedical ontologies that aims to provide a single point of access to the latest ontology versions"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
Expand Down
21 changes: 21 additions & 0 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,14 @@
"multimap": "^1.1.0",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-helmet": "^6.1.0",
"react-redux": "^8.0.2",
"react-router-dom": "^6.8.0",
"react-twitter-widgets": "^1.11.0",
"styled-components": "^5.2.1",
"typescript": "^4.1.2",
"web-vitals": "^1.0.1",
"url-join": "^5.0.0"
"url-join": "^5.0.0",
"web-vitals": "^1.0.1"
},
"scripts": {
"build": "dotenv -e .env.$REACT_APP_ENV -e .env -- node build.mjs",
Expand Down
9 changes: 8 additions & 1 deletion frontend/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect } from "react";
import React, { Fragment, useEffect } from "react";
import {
BrowserRouter,
Navigate,
Expand All @@ -20,10 +20,16 @@ import OntologyPage from "./pages/ontologies/OntologyPage";
import EntityPage from "./pages/ontologies/entities/EntityPage";
import { getEntity } from "./pages/ontologies/ontologiesSlice";
import Search from "./pages/search/Search";
import {Helmet} from "react-helmet";

class App extends React.Component {
render() {
return (
<Fragment>
<Helmet>
<meta charSet="utf-8" />
<title>Ontology Lookup Service (OLS)</title>
</Helmet>
<BrowserRouter basename={process.env.PUBLIC_URL!}>
<Routes>
<Route path={`*`} element={<Error />} />
Expand Down Expand Up @@ -84,6 +90,7 @@ class App extends React.Component {
</Routes>
<Footer />
</BrowserRouter>
</Fragment>
);
}
}
Expand Down
11 changes: 11 additions & 0 deletions frontend/src/components/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Link } from "react-router-dom";
import urlJoin from "url-join";
import { Helmet } from 'react-helmet';

export default function Header({ section }: { section?: string }) {

return (
<header
className="bg-black bg-right bg-cover"
Expand All @@ -12,6 +14,10 @@ export default function Header({ section }: { section?: string }) {
"')",
}}
>
<Helmet>
<meta charSet="utf-8" />
<title>{caps(section)} - Ontology Lookup Service</title>
</Helmet>
<div className="container mx-auto px-4 flex flex-col md:flex-row md:gap-10">
<div className="py-6 self-center">
<a href={urlJoin(process.env.PUBLIC_URL!, "/")}>
Expand Down Expand Up @@ -95,3 +101,8 @@ export default function Header({ section }: { section?: string }) {
</header>
);
}

function caps(str) {
return str[0].toUpperCase() + str.slice(1);
}

11 changes: 10 additions & 1 deletion frontend/src/pages/ontologies/OntologyPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import EntityTree from "./entities/EntityTree";
import MetadataTooltip from "./entities/entityPageSections/MetadataTooltip";
import addLinksToText from "./entities/entityPageSections/addLinksToText";
import { getOntology } from "./ontologiesSlice";
import {Helmet} from 'react-helmet';

export default function OntologyPage() {
const params = useParams();
Expand Down Expand Up @@ -91,10 +92,18 @@ export default function OntologyPage() {
if (errorMessage) navigate("/error", { state: { message: errorMessage } });
}, [errorMessage, navigate]);

document.title = ontology?.getName() || ontologyId;

let pageTitle = ontology?.getName() || ontologyId.toUpperCase();
let pageDesc = ontology?.getDescription();

return (
<div>
<Header section="ontologies" />
<Helmet>
<meta charSet="utf-8" />
{pageTitle && <title>{ontologyId.toUpperCase()} - Ontology Lookup Service</title>}
{pageDesc && <meta name="description" content={ontology?.getDescription()}/>}
</Helmet>
<main className="container mx-auto px-4">
{ontology ? (
<div className="my-8">
Expand Down
10 changes: 9 additions & 1 deletion frontend/src/pages/ontologies/entities/EntityPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import PropertyCharacteristicsSection from "./entityPageSections/PropertyCharact
import PropertyInverseOfSection from "./entityPageSections/PropertyInverseOfSection";
import RangeSection from "./entityPageSections/RangeSection";
import addLinksToText from "./entityPageSections/addLinksToText";
import { Helmet } from 'react-helmet'

export default function EntityPage({
entityType,
Expand Down Expand Up @@ -143,10 +144,17 @@ export default function EntityPage({
if (errorMessage) navigate("/error", { state: { message: errorMessage } });
}, [errorMessage, navigate]);

document.title = entity?.getShortForm() || entity?.getName() || ontologyId;
let pageTitle = entity?.getShortForm() || entity?.getName() || ontologyId;
let pageDesc = entity?.getDescription() || entity?.getName();

return (
<div>
<Header section="ontologies" />
<Helmet>
<meta charSet="utf-8" />
{pageTitle && <title>{pageTitle}</title>}
{pageDesc && <meta name="description" content={pageDesc}/>}
</Helmet>
<main className="container mx-auto px-4">
{ontology && entity ? (
<div className="my-8">
Expand Down
Loading