VIVO-742 Base and Solr Implementations of SearchEngine
This commit is contained in:
parent
6329343465
commit
5d653ebc9c
9 changed files with 923 additions and 0 deletions
|
@ -0,0 +1,55 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vitro.webapp.searchengine.base;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchFacetField;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A foundation class for implementing SearchFacetField.
|
||||||
|
*/
|
||||||
|
public class BaseSearchFacetField implements SearchFacetField {
|
||||||
|
private final String name;
|
||||||
|
private final List<Count> values;
|
||||||
|
|
||||||
|
public BaseSearchFacetField(String name, List<? extends Count> values) {
|
||||||
|
this.name = name;
|
||||||
|
this.values = new ArrayList<>(values);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Count> getValues() {
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A foundation class for implementing SearchFacetField.Count.
|
||||||
|
*/
|
||||||
|
public static class BaseCount implements SearchFacetField.Count {
|
||||||
|
private final String name;
|
||||||
|
private final long count;
|
||||||
|
|
||||||
|
public BaseCount(String name, long count) {
|
||||||
|
this.name = name;
|
||||||
|
this.count = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getCount() {
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vitro.webapp.searchengine.base;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchInputDocument;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchInputField;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A foundation class for implementing SearchInputDocument.
|
||||||
|
*/
|
||||||
|
public class BaseSearchInputDocument implements SearchInputDocument {
|
||||||
|
private final Map<String, SearchInputField> fieldMap = new HashMap<>();
|
||||||
|
private float documentBoost = 1.0F;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addField(SearchInputField field) {
|
||||||
|
fieldMap.put(field.getName(), field);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addField(String name, Object... values) {
|
||||||
|
addField(name, 1.0F, Arrays.asList(values));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addField(String name, Collection<Object> values) {
|
||||||
|
addField(name, 1.0F, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addField(String name, float boost, Object... values) {
|
||||||
|
addField(name, boost, Arrays.asList(values));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addField(String name, float boost, Collection<Object> values) {
|
||||||
|
BaseSearchInputField field = new BaseSearchInputField(name);
|
||||||
|
field.setBoost(boost);
|
||||||
|
field.addValues(values);
|
||||||
|
fieldMap.put(name, field);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setDocumentBoost(float searchBoost) {
|
||||||
|
this.documentBoost = searchBoost;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getDocumentBoost() {
|
||||||
|
return this.documentBoost;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SearchInputField getField(String name) {
|
||||||
|
return fieldMap.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, SearchInputField> getFieldMap() {
|
||||||
|
return new HashMap<>(fieldMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sub-classes should override this if the field requires special
|
||||||
|
* functionality.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public SearchInputField createField(String name) {
|
||||||
|
return new BaseSearchInputField(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vitro.webapp.searchengine.base;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchInputField;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A foundation class for implementing SearchInputField.
|
||||||
|
*/
|
||||||
|
public class BaseSearchInputField implements SearchInputField {
|
||||||
|
private final String name;
|
||||||
|
private final List<Object> valueList = new ArrayList<>();
|
||||||
|
|
||||||
|
private float boost = 1.0F;
|
||||||
|
|
||||||
|
public BaseSearchInputField(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addValues(Object... values) {
|
||||||
|
addValues(Arrays.asList(values));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addValues(Collection<? extends Object> values) {
|
||||||
|
valueList.addAll(values);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setBoost(float boost) {
|
||||||
|
this.boost = boost;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getBoost() {
|
||||||
|
return boost;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Object> getValues() {
|
||||||
|
return new ArrayList<Object>(valueList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getFirstValue() {
|
||||||
|
if (valueList.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return valueList.get(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,179 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vitro.webapp.searchengine.base;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchQuery;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A foundation class for implementing SearchQuery.
|
||||||
|
*/
|
||||||
|
public class BaseSearchQuery implements SearchQuery {
|
||||||
|
private String queryText;
|
||||||
|
private int start = 0;
|
||||||
|
private int rows = -1;
|
||||||
|
|
||||||
|
private final Set<String> fieldsToReturn = new HashSet<>();
|
||||||
|
private final Map<String, SearchQuery.Order> sortFields = new HashMap<>();
|
||||||
|
private final Set<String> filters = new HashSet<>();
|
||||||
|
|
||||||
|
private boolean faceting;
|
||||||
|
private final Set<String> facetFields = new HashSet<>();
|
||||||
|
private final Set<String> facetQueries = new HashSet<>();
|
||||||
|
private int facetMinCount = -1;
|
||||||
|
|
||||||
|
private final Map<String, List<String>> parameterMap = new HashMap<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SearchQuery setQuery(String query) {
|
||||||
|
this.queryText = query;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SearchQuery setStart(int start) {
|
||||||
|
this.start = start;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SearchQuery setRows(int rows) {
|
||||||
|
this.rows = rows;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SearchQuery addFields(String... names) {
|
||||||
|
return addFields(Arrays.asList(names));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SearchQuery addFields(Collection<String> names) {
|
||||||
|
this.fieldsToReturn.addAll(names);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SearchQuery addSortField(String name, Order order) {
|
||||||
|
sortFields.put(name, order);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SearchQuery addFilterQuery(String filterQuery) {
|
||||||
|
filters.add(filterQuery);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SearchQuery addFilterQueries(String... filterQueries) {
|
||||||
|
this.filters.addAll(Arrays.asList(filterQueries));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SearchQuery setFaceting(boolean b) {
|
||||||
|
this.faceting = b;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SearchQuery addFacetFields(String... fields) {
|
||||||
|
facetFields.addAll(Arrays.asList(fields));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SearchQuery addFacetQueries(String... queries) {
|
||||||
|
facetQueries.addAll(Arrays.asList(queries));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SearchQuery setFacetMinCount(int cnt) {
|
||||||
|
facetMinCount = cnt;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SearchQuery addParameter(String name, String... values) {
|
||||||
|
parameterMap.put(name, Collections.unmodifiableList(new ArrayList<>(
|
||||||
|
Arrays.asList(values))));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getQuery() {
|
||||||
|
return queryText;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getStart() {
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getRows() {
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<String> getFieldsToReturn() {
|
||||||
|
return Collections.unmodifiableSet(fieldsToReturn);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, SearchQuery.Order> getSortFields() {
|
||||||
|
return Collections.unmodifiableMap(sortFields);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<String> getFilters() {
|
||||||
|
return Collections.unmodifiableSet(filters);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isFaceting() {
|
||||||
|
return faceting;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<String> getFacetFields() {
|
||||||
|
return Collections.unmodifiableSet(facetFields);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<String> getFacetQueries() {
|
||||||
|
return Collections.unmodifiableSet(facetQueries);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getFacetMinCount() {
|
||||||
|
return facetMinCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, List<String>> getParameterMap() {
|
||||||
|
return Collections.unmodifiableMap(parameterMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "BaseSearchQuery [queryText=" + queryText + ", start=" + start
|
||||||
|
+ ", rows=" + rows + ", fieldsToReturn=" + fieldsToReturn
|
||||||
|
+ ", sortFields=" + sortFields + ", filters=" + filters
|
||||||
|
+ ", faceting=" + faceting + ", facetFields=" + facetFields
|
||||||
|
+ ", facetQueries=" + facetQueries + ", facetMinCount="
|
||||||
|
+ facetMinCount + ", parameterMap=" + parameterMap + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vitro.webapp.searchengine.base;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchFacetField;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchResponse;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchResultDocumentList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A foundation class for implementing SearchResponse;
|
||||||
|
*/
|
||||||
|
public class BaseSearchResponse implements SearchResponse {
|
||||||
|
private final Map<String, Map<String, List<String>>> highlighting;
|
||||||
|
private final Map<String, SearchFacetField> facetFields;
|
||||||
|
private final SearchResultDocumentList results;
|
||||||
|
|
||||||
|
public BaseSearchResponse(
|
||||||
|
Map<String, Map<String, List<String>>> highlighting,
|
||||||
|
Map<String, SearchFacetField> facetFields,
|
||||||
|
SearchResultDocumentList results) {
|
||||||
|
this.highlighting = highlighting;
|
||||||
|
this.facetFields = facetFields;
|
||||||
|
this.results = results;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SearchResultDocumentList getResults() {
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Map<String, List<String>>> getHighlighting() {
|
||||||
|
return Collections.unmodifiableMap(highlighting);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SearchFacetField getFacetField(String name) {
|
||||||
|
return facetFields.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<SearchFacetField> getFacetFields() {
|
||||||
|
return new ArrayList<>(facetFields.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vitro.webapp.searchengine.base;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchResultDocument;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A foundation class for implementing SearchResultDocument.
|
||||||
|
*/
|
||||||
|
public class BaseSearchResultDocument implements SearchResultDocument {
|
||||||
|
private final String uniqueId;
|
||||||
|
private final Map<String, Collection<Object>> fieldValuesMap;
|
||||||
|
|
||||||
|
public BaseSearchResultDocument(String uniqueId, Map<String, Collection<Object>> fieldValuesMap) {
|
||||||
|
this.uniqueId = uniqueId;
|
||||||
|
|
||||||
|
Map<String, Collection<Object>> map = new HashMap<>();
|
||||||
|
for (String name : fieldValuesMap.keySet()) {
|
||||||
|
map.put(name, Collections.unmodifiableList(new ArrayList<>(
|
||||||
|
fieldValuesMap.get(name))));
|
||||||
|
}
|
||||||
|
this.fieldValuesMap = Collections.unmodifiableMap(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUniqueId() {
|
||||||
|
return uniqueId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<String> getFieldNames() {
|
||||||
|
return fieldValuesMap.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getFirstValue(String name) {
|
||||||
|
Collection<Object> values = fieldValuesMap.get(name);
|
||||||
|
if (values == null || values.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return values.iterator().next();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getStringValue(String name) {
|
||||||
|
Object o = getFirstValue(name);
|
||||||
|
if (o == null) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return String.valueOf(o);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Object> getFieldValues(String name) {
|
||||||
|
Collection<Object> values = fieldValuesMap.get(name);
|
||||||
|
if (values == null) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
} else {
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Collection<Object>> getFieldValuesMap() {
|
||||||
|
return fieldValuesMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,173 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vitro.webapp.searchengine.solr;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.solr.client.solrj.SolrQuery;
|
||||||
|
import org.apache.solr.client.solrj.SolrQuery.ORDER;
|
||||||
|
import org.apache.solr.client.solrj.response.FacetField;
|
||||||
|
import org.apache.solr.client.solrj.response.QueryResponse;
|
||||||
|
import org.apache.solr.common.SolrInputDocument;
|
||||||
|
import org.apache.solr.common.SolrInputField;
|
||||||
|
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchFacetField;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchInputDocument;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchInputField;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchQuery;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchQuery.Order;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchResponse;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.searchengine.base.BaseSearchFacetField;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.searchengine.base.BaseSearchFacetField.BaseCount;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.searchengine.base.BaseSearchResponse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility method for converting from Solr-specific instances to Search-generic
|
||||||
|
* instances, and back.
|
||||||
|
*/
|
||||||
|
public class SolrConversionUtils {
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
// Convert input documents to Solr-specific.
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
static List<SolrInputDocument> convertToSolrInputDocuments(
|
||||||
|
Collection<SearchInputDocument> docs) {
|
||||||
|
List<SolrInputDocument> solrDocs = new ArrayList<>();
|
||||||
|
for (SearchInputDocument doc : docs) {
|
||||||
|
solrDocs.add(convertToSolrInputDocument(doc));
|
||||||
|
}
|
||||||
|
return solrDocs;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SolrInputDocument convertToSolrInputDocument(
|
||||||
|
SearchInputDocument doc) {
|
||||||
|
SolrInputDocument solrDoc = new SolrInputDocument(
|
||||||
|
convertToSolrInputFieldMap(doc.getFieldMap()));
|
||||||
|
solrDoc.setDocumentBoost(doc.getDocumentBoost());
|
||||||
|
return solrDoc;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<String, SolrInputField> convertToSolrInputFieldMap(
|
||||||
|
Map<String, SearchInputField> fieldMap) {
|
||||||
|
Map<String, SolrInputField> solrFieldMap = new HashMap<>();
|
||||||
|
for (String fieldName : fieldMap.keySet()) {
|
||||||
|
solrFieldMap.put(fieldName,
|
||||||
|
convertToSolrInputField(fieldMap.get(fieldName)));
|
||||||
|
}
|
||||||
|
return solrFieldMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SolrInputField convertToSolrInputField(
|
||||||
|
SearchInputField searchInputField) {
|
||||||
|
SolrInputField solrField = new SolrInputField(
|
||||||
|
searchInputField.getName());
|
||||||
|
solrField.addValue(searchInputField.getValues(),
|
||||||
|
searchInputField.getBoost());
|
||||||
|
return solrField;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
// Convert queries to Solr-specific.
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert from a SearchQuery to a SolrQuery, so the Solr server may execute
|
||||||
|
* it.
|
||||||
|
*/
|
||||||
|
static SolrQuery convertToSolrQuery(SearchQuery query) {
|
||||||
|
SolrQuery solrQuery = new SolrQuery(query.getQuery());
|
||||||
|
solrQuery.setStart(query.getStart());
|
||||||
|
|
||||||
|
int rows = query.getRows();
|
||||||
|
if (rows >= 0) {
|
||||||
|
solrQuery.setRows(rows);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String fieldToReturn : query.getFieldsToReturn()) {
|
||||||
|
solrQuery.addField(fieldToReturn);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Order> sortFields = query.getSortFields();
|
||||||
|
for (String sortField : sortFields.keySet()) {
|
||||||
|
solrQuery.addSortField(sortField,
|
||||||
|
convertToSolrOrder(sortFields.get(sortField)));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String filter : query.getFilters()) {
|
||||||
|
solrQuery.addFilterQuery(filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
solrQuery.setFacet(query.isFaceting());
|
||||||
|
|
||||||
|
for (String facetField : query.getFacetFields()) {
|
||||||
|
solrQuery.addFacetField(facetField);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String facetQuery : query.getFacetQueries()) {
|
||||||
|
solrQuery.addFacetQuery(facetQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
int minCount = query.getFacetMinCount();
|
||||||
|
if (minCount >= 0) {
|
||||||
|
solrQuery.setFacetMinCount(minCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, List<String>> parameterMap = query.getParameterMap();
|
||||||
|
for (String parameterName : parameterMap.keySet()) {
|
||||||
|
String[] values = parameterMap.get(parameterName).toArray(
|
||||||
|
new String[0]);
|
||||||
|
solrQuery.add(parameterName, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
return solrQuery;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ORDER convertToSolrOrder(Order order) {
|
||||||
|
if (order == Order.DESC) {
|
||||||
|
return ORDER.desc;
|
||||||
|
} else {
|
||||||
|
return ORDER.asc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
// Convert responses to Search-generic
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
static SearchResponse convertToSearchResponse(QueryResponse response) {
|
||||||
|
return new BaseSearchResponse(response.getHighlighting(),
|
||||||
|
convertToSearchFacetFieldMap(response.getFacetFields()),
|
||||||
|
new SolrSearchResultDocumentList(response.getResults()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<String, SearchFacetField> convertToSearchFacetFieldMap(
|
||||||
|
List<FacetField> facetFields) {
|
||||||
|
Map<String, SearchFacetField> map = new HashMap<>();
|
||||||
|
for (FacetField facetField : facetFields) {
|
||||||
|
map.put(facetField.getName(), convertToSearchFacetField(facetField));
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SearchFacetField convertToSearchFacetField(
|
||||||
|
FacetField facetField) {
|
||||||
|
return new BaseSearchFacetField(facetField.getName(),
|
||||||
|
convertToSearchFacetFieldCounts(facetField.getValues()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<BaseCount> convertToSearchFacetFieldCounts(
|
||||||
|
List<FacetField.Count> solrCounts) {
|
||||||
|
List<BaseCount> searchCounts = new ArrayList<>();
|
||||||
|
for (FacetField.Count solrCount : solrCounts) {
|
||||||
|
searchCounts.add(new BaseCount(solrCount.getName(), solrCount
|
||||||
|
.getCount()));
|
||||||
|
}
|
||||||
|
return searchCounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,170 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vitro.webapp.searchengine.solr;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import javax.servlet.ServletContext;
|
||||||
|
|
||||||
|
import org.apache.solr.client.solrj.SolrQuery;
|
||||||
|
import org.apache.solr.client.solrj.SolrServerException;
|
||||||
|
import org.apache.solr.client.solrj.impl.HttpSolrServer;
|
||||||
|
import org.apache.solr.client.solrj.response.QueryResponse;
|
||||||
|
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.Application;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.ComponentStartupStatus;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchEngine;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchEngineException;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchInputDocument;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchQuery;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchResponse;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.searchengine.base.BaseSearchInputDocument;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.searchengine.base.BaseSearchQuery;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Solr-based implementation of SearchEngine.
|
||||||
|
*/
|
||||||
|
public class SolrSearchEngine implements SearchEngine {
|
||||||
|
private HttpSolrServer server;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set up the http connection with the solr server
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void startup(Application application, ComponentStartupStatus css) {
|
||||||
|
ServletContext ctx = application.getServletContext();
|
||||||
|
String solrServerUrlString = ConfigurationProperties.getBean(ctx)
|
||||||
|
.getProperty("vitro.local.solr.url");
|
||||||
|
if (solrServerUrlString == null) {
|
||||||
|
css.fatal("Could not find vitro.local.solr.url in "
|
||||||
|
+ "runtime.properties. Vitro application needs the URL of "
|
||||||
|
+ "a solr server that it can use to index its data. It "
|
||||||
|
+ "should be something like http://localhost:${port}"
|
||||||
|
+ ctx.getContextPath() + "solr");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
server = new HttpSolrServer(solrServerUrlString);
|
||||||
|
server.setSoTimeout(10000); // socket read timeout
|
||||||
|
server.setConnectionTimeout(10000);
|
||||||
|
server.setDefaultMaxConnectionsPerHost(100);
|
||||||
|
server.setMaxTotalConnections(100);
|
||||||
|
server.setMaxRetries(1);
|
||||||
|
css.info("Set up the Solr search engine; URL = '"
|
||||||
|
+ solrServerUrlString + "'.");
|
||||||
|
} catch (Exception e) {
|
||||||
|
css.fatal("Could not set up the Solr search engine", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void shutdown(Application application) {
|
||||||
|
server.shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void ping() throws SearchEngineException {
|
||||||
|
try {
|
||||||
|
server.ping();
|
||||||
|
} catch (SolrServerException | IOException e) {
|
||||||
|
throw new SearchEngineException(
|
||||||
|
"Solr server did not respont to ping.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SearchInputDocument createInputDocument() {
|
||||||
|
return new BaseSearchInputDocument();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(SearchInputDocument... docs) throws SearchEngineException {
|
||||||
|
add(Arrays.asList(docs));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(Collection<SearchInputDocument> docs)
|
||||||
|
throws SearchEngineException {
|
||||||
|
try {
|
||||||
|
server.add(SolrConversionUtils.convertToSolrInputDocuments(docs));
|
||||||
|
} catch (SolrServerException | IOException e) {
|
||||||
|
throw new SearchEngineException(
|
||||||
|
"Solr server failed to add documents " + docs, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void commit() throws SearchEngineException {
|
||||||
|
try {
|
||||||
|
server.commit();
|
||||||
|
} catch (SolrServerException | IOException e) {
|
||||||
|
throw new SearchEngineException("Failed to commit to Solr server.",
|
||||||
|
e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void commit(boolean wait) throws SearchEngineException {
|
||||||
|
try {
|
||||||
|
server.commit(wait, wait);
|
||||||
|
} catch (SolrServerException | IOException e) {
|
||||||
|
throw new SearchEngineException("Failed to commit to Solr server.",
|
||||||
|
e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteById(String... ids) throws SearchEngineException {
|
||||||
|
deleteById(Arrays.asList(ids));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteById(Collection<String> ids) throws SearchEngineException {
|
||||||
|
try {
|
||||||
|
server.deleteById(new ArrayList<>(ids));
|
||||||
|
} catch (SolrServerException | IOException e) {
|
||||||
|
throw new SearchEngineException(
|
||||||
|
"Solr server failed to delete documents: " + ids, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteByQuery(String query) throws SearchEngineException {
|
||||||
|
try {
|
||||||
|
server.deleteByQuery(query);
|
||||||
|
} catch (SolrServerException | IOException e) {
|
||||||
|
throw new SearchEngineException(
|
||||||
|
"Solr server failed to delete documents: " + query, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SearchQuery createQuery() {
|
||||||
|
return new BaseSearchQuery();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SearchQuery createQuery(String queryText) {
|
||||||
|
BaseSearchQuery query = new BaseSearchQuery();
|
||||||
|
query.setQuery(queryText);
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SearchResponse query(SearchQuery query) throws SearchEngineException {
|
||||||
|
try {
|
||||||
|
SolrQuery solrQuery = SolrConversionUtils.convertToSolrQuery(query);
|
||||||
|
QueryResponse response = server.query(solrQuery);
|
||||||
|
return SolrConversionUtils.convertToSearchResponse(response);
|
||||||
|
} catch (SolrServerException e) {
|
||||||
|
throw new SearchEngineException(
|
||||||
|
"Solr server failed to execute the query" + query, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,79 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vitro.webapp.searchengine.solr;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import org.apache.solr.common.SolrDocument;
|
||||||
|
import org.apache.solr.common.SolrDocumentList;
|
||||||
|
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchResultDocument;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchResultDocumentList;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.searchengine.base.BaseSearchResultDocument;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Solr-based implementation of SearchResultDocumentList.
|
||||||
|
*
|
||||||
|
* It's necessary to use this instead of the base version, so the iterator can
|
||||||
|
* convert each document as it is requested.
|
||||||
|
*/
|
||||||
|
public class SolrSearchResultDocumentList implements SearchResultDocumentList {
|
||||||
|
private SolrDocumentList solrDocs;
|
||||||
|
|
||||||
|
public SolrSearchResultDocumentList(SolrDocumentList solrDocs) {
|
||||||
|
this.solrDocs = solrDocs;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<SearchResultDocument> iterator() {
|
||||||
|
return new SearchResultDocumentIterator(solrDocs.iterator());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getNumFound() {
|
||||||
|
return solrDocs.getNumFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return solrDocs.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SearchResultDocument get(int i) {
|
||||||
|
return convertToSearchResultDocument(solrDocs.get(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SearchResultDocumentIterator implements
|
||||||
|
Iterator<SearchResultDocument> {
|
||||||
|
private final Iterator<SolrDocument> solrIterator;
|
||||||
|
|
||||||
|
public SearchResultDocumentIterator(Iterator<SolrDocument> solrIterator) {
|
||||||
|
this.solrIterator = solrIterator;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return solrIterator.hasNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SearchResultDocument next() {
|
||||||
|
return convertToSearchResultDocument(solrIterator.next());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SearchResultDocument convertToSearchResultDocument(
|
||||||
|
SolrDocument solrDoc) {
|
||||||
|
return new BaseSearchResultDocument(
|
||||||
|
(String) solrDoc.getFieldValue("DocId"),
|
||||||
|
solrDoc.getFieldValuesMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue