Skip to content

Commit

Permalink
search implementation/fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
jamesamcl committed Jun 14, 2024
1 parent 4baf710 commit d11dbe0
Show file tree
Hide file tree
Showing 19 changed files with 171 additions and 117 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ graph.db
*.txt
*.swp
.nextflow*
work

20 changes: 13 additions & 7 deletions grebi_api/src/main/java/uk/ac/ebi/grebi/GrebiApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public static void main(String[] args) throws ParseException, org.apache.commons
ctx.result("{}");

var q = new GrebiSolrQuery();
q.addFilter("grebi__nodeId", List.of(ctx.pathParam("nodeId")), SearchType.WHOLE_FIELD);
q.addFilter("grebi:nodeId", List.of(ctx.pathParam("nodeId")), SearchType.WHOLE_FIELD, false);

var res = solr.getFirstNode(q);

Expand All @@ -71,12 +71,15 @@ public static void main(String[] args) throws ParseException, org.apache.commons
q.setSearchText(ctx.queryParam("q"));
q.setExactMatch(false);
q.addSearchField("id", 1000, SearchType.WHOLE_FIELD);
q.addSearchField("grebi__synonym", 900, SearchType.WHOLE_FIELD);
q.addSearchField("grebi:name", 900, SearchType.WHOLE_FIELD);
q.addSearchField("grebi:synonym", 800, SearchType.WHOLE_FIELD);
q.addSearchField("id", 500, SearchType.CASE_INSENSITIVE_TOKENS);
q.addSearchField("grebi__synonym", 450, SearchType.CASE_INSENSITIVE_TOKENS);
q.addSearchField("grebi__description", 400, SearchType.WHOLE_FIELD);
q.addSearchField("grebi__description", 250, SearchType.CASE_INSENSITIVE_TOKENS);
q.addSearchField("grebi:name", 450, SearchType.CASE_INSENSITIVE_TOKENS);
q.addSearchField("grebi:synonym", 420, SearchType.CASE_INSENSITIVE_TOKENS);
q.addSearchField("grebi:description", 400, SearchType.WHOLE_FIELD);
q.addSearchField("grebi:description", 250, SearchType.CASE_INSENSITIVE_TOKENS);
q.addSearchField("_text_", 1, SearchType.CASE_INSENSITIVE_TOKENS);
q.addFilter("ols:isObsolete", Set.of("true"), SearchType.WHOLE_FIELD, true);
for(var param : ctx.queryParamMap().entrySet()) {
if(param.getKey().equals("q") ||
param.getKey().equals("page") ||
Expand All @@ -88,15 +91,18 @@ public static void main(String[] args) throws ParseException, org.apache.commons
) {
continue;
}
q.addFilter(param.getKey(), param.getValue(), SearchType.WHOLE_FIELD);
q.addFilter(param.getKey(), param.getValue(), SearchType.WHOLE_FIELD, false);
}
for(var facetField : ctx.queryParams("facet")) {
q.addFacetField(facetField);
}
var page_num = ctx.queryParam("page");
if(page_num == null) {
page_num = "0";
}
var size = ctx.queryParam("size");
if(size == null) {
size = "100";
size = "10";
}
var page = PageRequest.of(Integer.parseInt(page_num), Integer.parseInt(size));
var res = solr.searchNodesPaginated(q, page);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public GrebiFacetedResultsPage<SolrDocument> searchSolrPaginated(GrebiSolrQuery
valueToCount.put(count.getName(), count.getCount());
}

facetFieldToCounts.put(facetField.getName(), valueToCount);
facetFieldToCounts.put(facetField.getName().replace("__", ":"), valueToCount);
}
}

Expand Down
19 changes: 13 additions & 6 deletions grebi_api/src/main/java/uk/ac/ebi/grebi/db/GrebiSolrQuery.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ public void addFacetField(String propertyName) {
this.facetFields.add(propertyName);
}

public void addFilter(String propertyName, Collection<String> propertyValues, SearchType searchType) {
this.filters.add(new Filter(propertyName, propertyValues, searchType));
public void addFilter(String propertyName, Collection<String> propertyValues, SearchType searchType, boolean negate) {
this.filters.add(new Filter(propertyName, propertyValues, searchType, negate));
}

public SolrQuery constructQuery() {
Expand Down Expand Up @@ -107,6 +107,10 @@ public SolrQuery constructQuery() {
fq.append("{!tag=grebifacet}");
}

if(f.negate) {
fq.append("-");
}

fq.append( ClientUtils.escapeQueryChars(getSolrPropertyName(f.propertyName, f.searchType)) );
fq.append(":(");

Expand All @@ -127,8 +131,9 @@ public SolrQuery constructQuery() {

if(facetFields.size() > 0) {
for(String facetField : facetFields) {
query.addFacetField("{!ex=grebifacet}" + facetField);
query.addFacetField("{!ex=grebifacet}" + facetField.replace(":", "__"));
}
query.setFacetMinCount(1);
}

return query;
Expand All @@ -139,11 +144,13 @@ private class Filter {
String propertyName;
Collection<String> propertyValues; // all values to search for ("OR")
SearchType searchType;
boolean negate;

public Filter(String propertyName, Collection<String> propertyValues, SearchType searchType) {
public Filter(String propertyName, Collection<String> propertyValues, SearchType searchType, boolean negate) {
this.propertyName = propertyName;
this.propertyValues = propertyValues;
this.searchType = searchType;
this.negate = negate;
}
}

Expand Down Expand Up @@ -180,9 +187,9 @@ public BoostField(String propertyName, String propertyValue, int weight, SearchT
private String getSolrPropertyName(String propertyName, SearchType searchType) {
switch(searchType) {
case CASE_INSENSITIVE_TOKENS:
return propertyName;
return propertyName.replace(":", "__");
case WHOLE_FIELD:
return "str_" + propertyName;
return "str_" + propertyName.replace(":", "__");
default:
throw new RuntimeException("unknown filter accuracy");
}
Expand Down
12 changes: 11 additions & 1 deletion grebi_api/src/main/java/uk/ac/ebi/grebi/db/ResolverClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@
import com.google.gson.JsonElement;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.google.gson.JsonElement;
import com.google.gson.Gson;
import com.google.gson.internal.LinkedTreeMap;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
Expand All @@ -31,7 +34,7 @@ public static String getResolverHost() {
return "http://localhost:8080/";
}

public Map<String, Map<String,JsonElement>> resolve(Collection<String> ids) {
public Map<String, Map<String,JsonElement>> resolveToMap(Collection<String> ids) {

Stopwatch timer = Stopwatch.createStarted();

Expand Down Expand Up @@ -65,4 +68,11 @@ public Map<String, Map<String,JsonElement>> resolve(Collection<String> ids) {

return null;
}

public List<Map<String, JsonElement>> resolveToList(Collection<String> ids) {

var resolved = resolveToMap(ids);

return ids.stream().map(id -> resolved.get(id)).collect(Collectors.toList());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public List<EdgeAndNode> getIncomingEdges(String nodeId) {
.withParameters(Map.of("nodeId", nodeId))
.withConfig(QueryConfig.builder().withDatabase("neo4j").build()).execute();

var resolved = resolver.resolve(
var resolved = resolver.resolveToMap(
res.records().stream().flatMap(record -> {
var props = record.asMap();
return List.of((String) props.get("otherId"), (String) props.get("edgeId")).stream();
Expand Down
17 changes: 4 additions & 13 deletions grebi_api/src/main/java/uk/ac/ebi/grebi/repo/GrebiSolrRepo.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,38 +38,29 @@ private GrebiFacetedResultsPage<Map<String,JsonElement>> resolveNodeIds(GrebiFac

List<String> ids = solrDocs.map(doc -> doc.getFieldValue("grebi__nodeId").toString()).toList();

Map<String,Map<String,JsonElement>> idToEntity = resolver.resolve(ids);

List<Map<String,JsonElement>> vals = idToEntity.values().stream().collect(Collectors.toList());
List<Map<String,JsonElement>> vals = resolver.resolveToList(ids);
assert(vals.size() == solrDocs.getSize());

return new GrebiFacetedResultsPage<>(vals, solrDocs.facetFieldToCounts, solrDocs.getPageable(), solrDocs.getTotalElements());
}

private Map<String,JsonElement> resolveNodeId(SolrDocument solrDoc) {

Map<String,Map<String,JsonElement>> idToEntity = resolver.resolve(List.of(solrDoc.getFieldValue("grebi__nodeId").toString()));

return idToEntity.values().iterator().next();
return resolver.resolveToList(List.of(solrDoc.getFieldValue("grebi__nodeId").toString())).iterator().next();
}

private GrebiFacetedResultsPage<Map<String,JsonElement>> resolveEdgeIds(GrebiFacetedResultsPage<SolrDocument> solrDocs) {

List<String> ids = solrDocs.map(doc -> doc.getFieldValue("grebi__edgeId").toString()).toList();

Map<String, Map<String,JsonElement>> idToEntity = resolver.resolve(ids);

List<Map<String,JsonElement>> vals = idToEntity.values().stream().collect(Collectors.toList());
List<Map<String,JsonElement>> vals = resolver.resolveToList(ids);
assert(vals.size() == solrDocs.getSize());

return new GrebiFacetedResultsPage<>(vals, solrDocs.facetFieldToCounts, solrDocs.getPageable(), solrDocs.getTotalElements());
}

private Map<String,JsonElement> resolveEdgeId(SolrDocument solrDoc) {

Map<String,Map<String,JsonElement>> idToEntity = resolver.resolve(List.of(solrDoc.getFieldValue("grebi__edgeId").toString()));

return idToEntity.values().iterator().next();
return resolver.resolveToList(List.of(solrDoc.getFieldValue("grebi__edgeId").toString())).iterator().next();
}

}
18 changes: 18 additions & 0 deletions grebi_ui/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,26 @@ import Search from "./pages/search/Search";
import NodePage from "./pages/node/NodePage";
import DownloadsPage from "./pages/downloads/Downloads";


import MuiThemeProvider from '@mui/styles/ThemeProvider'
import createTheme from '@mui/material/styles/createTheme'

const theme = createTheme({
palette: {
primary: {
main: '#ff0000',
},
secondary: {
main: '#ff0000',
}
}
});


class App extends React.Component {
render() {
return (
<MuiThemeProvider theme={theme}>
<Fragment>
<Helmet>
<meta charSet="utf-8" />
Expand All @@ -36,6 +53,7 @@ class App extends React.Component {
{/* <Footer /> */}
</BrowserRouter>
</Fragment>
</MuiThemeProvider>
);
}
}
Expand Down
19 changes: 17 additions & 2 deletions grebi_ui/src/app/api.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@

type ReqParams = {[k:string]:string}|undefined
type ReqParams = {[k:string]:(string|string[])}|undefined

function buildSearchParams(reqParams:ReqParams):string {
let params = new URLSearchParams()
for(let key in reqParams) {
let val = reqParams[key]
if(Array.isArray(val)) {
for(let v of val) {
params.append(key, v)
}
} else {
params.append(key, val)
}
}
return params.toString()
}

export async function request(
path: string,
Expand All @@ -9,7 +24,7 @@ export async function request(
): Promise<any> {
const url = (apiUrl || process.env.REACT_APP_APIURL) + path;
//const res = await fetch(url.replace(/([^:]\/)\/+/g, "$1"), {
const res = await fetch(url + (reqParams ? ('?' + new URLSearchParams(Object.entries(reqParams)).toString()) : ''), {
const res = await fetch(url + (reqParams ? ('?' + buildSearchParams(reqParams)) : ''), {
...(init ? init : {}),
//headers: { ...(init?.headers || {}), ...getAuthHeaders() }
});
Expand Down
5 changes: 2 additions & 3 deletions grebi_ui/src/components/SearchBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,8 @@ export default function SearchBox({
.map((entry: GraphNode, i: number): SearchBoxEntry => {
let name = entry.getName();
let type = entry.extractType()?.short
const linkUrl = `/nodes/${encodeNodeId(entry.getNodeId())}`;
return {
linkUrl,
linkUrl: entry.getLinkUrl(),
li: (
<li
key={randomString()}
Expand All @@ -164,7 +163,7 @@ export default function SearchBox({
onClick={() => {
setQuery("");
}}
to={linkUrl}
to={entry.getLinkUrl()}
>
<div className="flex justify-between">

Expand Down
49 changes: 41 additions & 8 deletions grebi_ui/src/model/GraphNode.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { pickBestDisplayName } from "../app/util";
import encodeNodeId from "../encodeNodeId";
import PropVal from "./PropVal";
import Refs from "./Refs";

Expand All @@ -14,22 +15,30 @@ export default class GraphNode {
return PropVal.arrFrom(this.props['grebi:name'] || []);
}

getSynonyms():PropVal[] {
return PropVal.arrFrom(this.props['grebi:synonym'] || []);
}

getName():string {
return pickBestDisplayName(this.getNames().map(n => n.value)) || this.getId().value
}

getDescriptions():PropVal[] {
return PropVal.arrFrom(this.props['rdfs:comment'])
return PropVal.arrFrom(this.props['grebi:description'])
}

getDescription():PropVal|undefined {
return PropVal.arrFrom(this.getDescriptions())[0]
getDescription():string|undefined {
return PropVal.arrFrom(this.getDescriptions())[0]?.value
}

getNodeId():string {
return this.props['grebi:nodeId']
}

getLinkUrl():string {
return `/nodes/${encodeNodeId(this.getNodeId())}`;
}

getId():PropVal {
if(this.props['ols:curie']){
return PropVal.arrFrom(this.props['ols:curie'])[0]
Expand Down Expand Up @@ -62,6 +71,9 @@ export default class GraphNode {
}
if(types.indexOf('ols:Class') !== -1) {
let ancestors:any[] = PropVal.arrFrom(this.props['ols:directAncestor']).map(a => a.value)
if(ancestors.indexOf("chebi:36080") !== -1) {
return {long:'Protein',short:'Protein'}
}
if(ancestors.indexOf("chebi:24431") !== -1) {
return {long:'Chemical',short:'Chemical'}
}
Expand All @@ -83,8 +95,13 @@ export default class GraphNode {
return this.props['grebi:datasources'] || []
}

isBold() { // idk yet
return false
isBoldForQuery(q:string) {
let allIdentifiers = [
...this.getNames().map(p => p.value),
...this.getSynonyms().map(p => p.value),
...this.getIds().map(p => p.value),
];
return allIdentifiers.indexOf(q) !== -1
}

isDeprecated() {
Expand All @@ -97,12 +114,28 @@ export default class GraphNode {

getProps():{[key:string]:PropVal[]} {
let res_props = {}
for(let k of Object.keys(this.props)) {
if( (k.startsWith('grebi:') || k === '_refs') && k !== 'grebi:type') {
let keys = Object.keys(this.props)

let sortOrder = [
'grebi:name',
'grebi:synonym',
'grebi:description',
'grebi:type'
].filter(k => {
if(keys.indexOf(k) !== -1) {
keys.splice(keys.indexOf(k), 1)
return true
}
})
keys = sortOrder.concat(keys)
for(let k of keys) {
//if( (k.startsWith('grebi:') || k === '_refs') && k !== 'grebi:type') {
if(k === '_refs') {
continue;
}
res_props[k] = PropVal.arrFrom(this.props[k])
}
return res_props
}
}
}

Loading

0 comments on commit d11dbe0

Please sign in to comment.