package org.nextprot.api.solr.impl;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrQuery.ORDER;
import org.apache.solr.client.solrj.SolrRequest.METHOD;
import org.apache.solr.client.solrj.SolrServer;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.response.FacetField;
import org.apache.solr.client.solrj.response.FacetField.Count;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.client.solrj.response.SpellCheckResponse;
import org.apache.solr.client.solrj.response.SpellCheckResponse.Collation;
import org.apache.solr.client.solrj.response.SpellCheckResponse.Suggestion;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
import org.nextprot.api.commons.exception.NextProtException;
import org.nextprot.api.commons.exception.SearchConnectionException;
import org.nextprot.api.commons.exception.SearchQueryException;
import org.nextprot.api.commons.utils.Pair;
import org.nextprot.api.solr.*;
import org.nextprot.api.solr.SearchResult.Facet;
import org.nextprot.api.solr.SearchResult.Spellcheck;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.Map.Entry;
@Lazy
@Service
public class SolrServiceImpl implements SolrService {
private static final Log Logger = LogFactory.getLog(SolrServiceImpl.class);
private static final int DEFAULT_ROWS = 50;
@Autowired
private SolrConnectionFactory connFactory;
@Autowired
private SolrConfiguration configuration;
private void logSolrQuery(String context, SolrQuery sq) {
Set<String> params = new TreeSet<String>();
for (String p : sq.getParameterNames()) params.add(p + " : " + sq.get(p));
Logger.debug("SolrQuery ============================================================== in " + context);
for (String p : params) {
Logger.debug("SolrQuery " + p);
}
}
public SearchResult executeQuery(Query query) throws SearchQueryException {
SolrIndex index = query.getIndex();
SolrQuery solrQuery = solrQuerySetup(query);
logSolrQuery("executeQuery",solrQuery);
return executeSolrQuery(index, solrQuery);
}
public SearchResult executeCustomQuery(Query query, String[] fields) throws SearchQueryException {
SolrIndex index = query.getIndex();
SolrQuery solrQuery = solrQuerySetup(query);
solrQuery.setFields(fields);
return executeSolrQuery(index, solrQuery);
}
public SearchResult executeIdQuery(Query query) throws SearchQueryException {
SolrIndex index = query.getIndex();
if (index == null)
index = this.configuration.getIndexByName(query.getIndexName());
String configName = query.getConfigName();
IndexConfiguration indexConfig = configName == null ? index.getDefaultConfig() : index.getConfig(query.getConfigName());
Logger.debug("configName="+indexConfig.getName());
SolrQuery solrQuery = buildSolrIdQuery(query, indexConfig);
logSolrQuery("executeIdQuery", solrQuery);
return executeSolrQuery(index, solrQuery);
}
public boolean checkAvailableIndex(String indexName) {
return this.configuration.hasIndex(indexName);
}
private SolrQuery solrQuerySetup(Query query) throws SearchQueryException {
SolrIndex index = query.getIndex();
if (index == null)
index = this.configuration.getIndexByName(query.getIndexName());
String configName = query.getConfigName();
IndexConfiguration indexConfig = configName == null ? index.getDefaultConfig() : index.getConfig(query.getConfigName());
return buildSolrQuery(query, indexConfig);
}
/*
* references: SearchController.searchIds() -> this.executeIdQuery() -> here
*/
@Override
public SolrQuery buildSolrIdQuery(Query query, IndexConfiguration indexConfig) throws SearchQueryException {
Logger.debug("Query index name:" + query.getIndexName());
Logger.debug("Query config name: "+ query.getConfigName());
String solrReadyQueryString = indexConfig.buildQuery(query);
String filter = query.getFilter();
if (filter != null)
solrReadyQueryString += " AND filters:" + filter;
Logger.debug("Solr-ready query : " + solrReadyQueryString);
SolrQuery solrQuery = new SolrQuery();
solrQuery.setQuery(solrReadyQueryString);
solrQuery.setRows(0);
solrQuery.set("facet", true);
solrQuery.set("facet.field", "id");
solrQuery.set("facet.method", "enum");
solrQuery.set("facet.query", solrReadyQueryString);
solrQuery.set("facet.limit", 30000);
logSolrQuery("buildSolrIdQuery",solrQuery);
return solrQuery;
}
/**
* Builds a SOLR Query according to the specified index configuration
*
* @param query
* @param indexConfig
* @return
*/
private SolrQuery buildSolrQuery(Query query, IndexConfiguration indexConfig) throws SearchQueryException {
SolrQuery solrQuery = new SolrQuery();
String queryString = indexConfig.buildQuery(query);
String filter = query.getFilter();
if (filter != null)
queryString += " AND filters:" + filter;
solrQuery.setQuery(queryString);
solrQuery.setStart(query.getStart());
solrQuery.setRows(query.getRows());
solrQuery.setFields(indexConfig.getParameterQuery(IndexParameter.FL));
solrQuery.set(IndexParameter.FL.name().toLowerCase(), indexConfig.getParameterQuery(IndexParameter.FL));
solrQuery.set(IndexParameter.QF.name().toLowerCase(), indexConfig.getParameterQuery(IndexParameter.QF));
solrQuery.set(IndexParameter.PF.name().toLowerCase(), indexConfig.getParameterQuery(IndexParameter.PF));
solrQuery.set(IndexParameter.FN.name().toLowerCase(), indexConfig.getParameterQuery(IndexParameter.FN));
solrQuery.set(IndexParameter.HI.name().toLowerCase(), indexConfig.getParameterQuery(IndexParameter.HI));
Map<String, String> otherParameters = indexConfig.getOtherParameters();
if (otherParameters != null)
for (Entry<String, String> e : otherParameters.entrySet())
solrQuery.set(e.getKey(), e.getValue());
String sortName = query.getSort();
SortConfig sortConfig = null;
if (sortName != null) {
sortConfig = indexConfig.getSortConfig(sortName);
if (sortConfig == null)
throw new SearchQueryException("sort " + sortName + " does not exist");
} else
sortConfig = indexConfig.getDefaultSortConfiguration();
if (query.getOrder() != null) {
for (Pair<IndexField, ORDER> s : sortConfig.getSorting())
solrQuery.addSort(s.getFirst().getName(), query.getOrder());
} else {
for (Pair<IndexField, ORDER> s : sortConfig.getSorting())
solrQuery.addSort(s.getFirst().getName(), s.getSecond());
}
// function buildBoost(value) { return
// "sum(1.0,product(div(log(informational_score),6.0),div("+ value
// +",100.0)))"; }
if (sortConfig.getBoost() != -1) {
solrQuery.set("boost", "sum(1.0,product(div(log(informational_score),6.0),div(" + sortConfig.getBoost() + ",100.0)))");
}
return solrQuery;
}
/**
* Perform the Solr query and return the results
*
* @param index
* @param solrQuery
* @return
*/
private SearchResult executeSolrQuery(SolrIndex index, SolrQuery solrQuery) {
SearchResult result = new SearchResult();
SolrServer server = this.connFactory.getServer(index.getName());
// Logger.debug("server: " + index.getName() + " >> " +
// ((HttpSolrServer) server).getBaseURL());
// Logger.debug("query: " + solrQuery.toString());
logSolrQuery("executeSolrQuery", solrQuery);
try {
QueryResponse response = server.query(solrQuery, METHOD.POST);
result = buildSearchResult(solrQuery, index.getName(), index.getUrl(), response);
} catch (SolrServerException e) {
throw new SearchConnectionException("Could not connect to Solr server. Please contact support or try again later.");
}
return result;
}
private SearchResult buildSearchResult(SolrQuery query, String indexName, String url, QueryResponse response) {
SearchResult results = new SearchResult(indexName, url);
SolrDocumentList docs = response.getResults();
Logger.debug("Response doc size:" + docs.size());
List<Map<String, Object>> res = new ArrayList<>();
Map<String, Object> item = null;
for (SolrDocument doc : docs) {
item = new HashMap<>();
for (Entry<String, Object> e : doc.entrySet())
item.put(e.getKey(), e.getValue());
res.add(item);
}
results.addAllResults(res);
if (query.getStart() != null)
results.setStart(query.getStart());
results.setRows(query.getRows());
results.setElapsedTime(response.getElapsedTime());
results.setFound(docs.getNumFound());
if (docs.getMaxScore() != null)
results.setScore(docs.getMaxScore());
// Facets
List<FacetField> facetFields = response.getFacetFields();
Logger.debug("Response facet fields:" + facetFields.size());
if (facetFields != null) {
Facet facet = null;
for (FacetField ff : facetFields) {
facet = new Facet(ff.getName());
Logger.debug("Response facet field:" + ff.getName() + " count:" + ff.getValueCount());
for (Count c : ff.getValues())
facet.addFacetField(c.getName(), c.getCount());
results.addSearchResultFacet(facet);
}
}
// Spellcheck
SpellCheckResponse spellcheckResponse = response.getSpellCheckResponse();
if (spellcheckResponse != null) {
Spellcheck spellcheckResult = new Spellcheck();
List<Suggestion> suggestions = spellcheckResponse.getSuggestions();
List<Collation> collations = spellcheckResponse.getCollatedResults();
if (collations != null) {
for (Collation c : collations)
spellcheckResult.addCollation(c.getCollationQueryString(), c.getNumberOfHits());
}
if (suggestions != null)
for (Suggestion s : suggestions)
spellcheckResult.addSuggestions(s.getToken(), s.getAlternatives());
results.setSpellCheck(spellcheckResult);
}
return results;
}
/*
* @Override public SearchResult getUserListSearchResult(UserProteinList
* proteinList) throws SearchQueryException {
*
* Set<String> accessions = proteinList.getAccessionNumbers();
*
* String queryString = "id:" + (accessions.size() > 1 ? "(" +
* Joiner.on(" ").join(accessions) + ")" : accessions.iterator().next());
*
* SolrIndex index = this.configuration.getIndexByName("entry");
* IndexConfiguration indexConfig = index.getConfig("simple");
*
* FieldConfigSet fieldConfigSet =
* indexConfig.getConfigSet(IndexParameter.FL); Set<IndexField> fields =
* fieldConfigSet.getConfigs().keySet(); getClass();
*
* String[] fieldNames = new String[fields.size()]; Iterator<IndexField> it
* = fields.iterator(); int counter = 0; while (it.hasNext()) {
* fieldNames[counter++] = it.next().getName(); }
*
* Query query = new Query(index); query.addQuery(queryString);
* query.rows(50); // Query query = this.queryService.buildQuery(index,
* "simple", // queryString, null, null, null, "0", "50", null, new
* String[0]);
*
* return this.executeByIdQuery(query, fieldNames); }
*/
@Override
public Query buildQueryForAutocomplete(String indexName, String queryString, String quality, String sort, String order, String start, String rows, String filter) {
return buildQuery(indexName, "autocomplete", queryString, quality, sort, order, start, rows, filter);
}
@Override
public Query buildQueryForSearchIndexes(String indexName, String configurationName, QueryRequest request) {
return this.buildQuery(indexName, configurationName, request);
}
@Override
public Query buildQueryForProteinLists(String indexName, String queryString, String quality, String sort, String order, String start, String rows, String filter) {
return buildQuery(indexName, "pl_search", queryString, quality, sort, order, start, rows, filter);
}
private Query buildQuery(String indexName, String configurationName, QueryRequest request) {
Logger.debug("calling buildQuery() with indexName=" + indexName + ", configName=" + configurationName) ;
Logger.debug("\n--------------\nQueryRequest:\n--------------\n"+request.toPrettyString()+"\n--------------");
Query q = buildQuery(indexName, configurationName, request.getQuery(), request.getQuality(), request.getSort(), request.getOrder(), request.getStart(), request.getRows(), request.getFilter());
Logger.debug("\n--------------\nQuery:\n--------------\n" + q.toPrettyString() + "\n--------------");
return q;
}
private Query buildQuery(String indexName, String configuration, String queryString, String quality, String sort, String order, String start, String rows, String filter) {
String actualIndexName = indexName.equals("entry") && quality != null && quality.equals("gold") ? "gold-entry" : indexName;
SolrIndex index = this.configuration.getIndexByName(actualIndexName);
Query q = new Query(index).addQuery(queryString);
q.setConfiguration(configuration);
q.rows((rows != null) ? Integer.parseInt(rows) : DEFAULT_ROWS);
q.start((start != null) ? Integer.parseInt(start) : 0);
if (sort != null && sort.length() > 0)
q.sort(sort);
if (order != null && (order.equals(ORDER.asc.name()) || order.equals(ORDER.desc.name()))) {
q.order(ORDER.valueOf(order));
}
q.setIndex(index);
q.setIndexName(actualIndexName);
if (filter != null && filter.length() > 0)
q.addFilter(filter);
return q;
}
@Override
public List<String> executeQueryAndGetAccessions(Query query) {
List<String> accessions = new ArrayList<>();
try {
SearchResult result = executeQuery(query);
for (Map<String, Object> item : result.getResults()) {
accessions.add((String) item.get("id"));
}
} catch (SearchQueryException e) {
e.printStackTrace();
throw new NextProtException("An exception was thrown while searching");
}
return accessions;
}
}