diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/searchengine/base/BaseSearchFacetField.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/searchengine/base/BaseSearchFacetField.java new file mode 100644 index 000000000..55caed27e --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/searchengine/base/BaseSearchFacetField.java @@ -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 values; + + public BaseSearchFacetField(String name, List values) { + this.name = name; + this.values = new ArrayList<>(values); + } + + @Override + public String getName() { + return name; + } + + @Override + public List 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; + } + + } +} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/searchengine/base/BaseSearchInputDocument.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/searchengine/base/BaseSearchInputDocument.java new file mode 100644 index 000000000..e2603dd8e --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/searchengine/base/BaseSearchInputDocument.java @@ -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 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 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 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 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); + } + +} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/searchengine/base/BaseSearchInputField.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/searchengine/base/BaseSearchInputField.java new file mode 100644 index 000000000..69dd3857e --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/searchengine/base/BaseSearchInputField.java @@ -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 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 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 getValues() { + return new ArrayList(valueList); + } + + @Override + public Object getFirstValue() { + if (valueList.isEmpty()) { + return null; + } else { + return valueList.get(0); + } + } + +} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/searchengine/base/BaseSearchQuery.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/searchengine/base/BaseSearchQuery.java new file mode 100644 index 000000000..eccc6b243 --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/searchengine/base/BaseSearchQuery.java @@ -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 fieldsToReturn = new HashSet<>(); + private final Map sortFields = new HashMap<>(); + private final Set filters = new HashSet<>(); + + private boolean faceting; + private final Set facetFields = new HashSet<>(); + private final Set facetQueries = new HashSet<>(); + private int facetMinCount = -1; + + private final Map> 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 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 getFieldsToReturn() { + return Collections.unmodifiableSet(fieldsToReturn); + } + + @Override + public Map getSortFields() { + return Collections.unmodifiableMap(sortFields); + } + + @Override + public Set getFilters() { + return Collections.unmodifiableSet(filters); + } + + @Override + public boolean isFaceting() { + return faceting; + } + + @Override + public Set getFacetFields() { + return Collections.unmodifiableSet(facetFields); + } + + @Override + public Set getFacetQueries() { + return Collections.unmodifiableSet(facetQueries); + } + + @Override + public int getFacetMinCount() { + return facetMinCount; + } + + @Override + public Map> 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 + "]"; + } + +} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/searchengine/base/BaseSearchResponse.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/searchengine/base/BaseSearchResponse.java new file mode 100644 index 000000000..955b1f20a --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/searchengine/base/BaseSearchResponse.java @@ -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>> highlighting; + private final Map facetFields; + private final SearchResultDocumentList results; + + public BaseSearchResponse( + Map>> highlighting, + Map facetFields, + SearchResultDocumentList results) { + this.highlighting = highlighting; + this.facetFields = facetFields; + this.results = results; + } + + @Override + public SearchResultDocumentList getResults() { + return results; + } + + @Override + public Map>> getHighlighting() { + return Collections.unmodifiableMap(highlighting); + } + + @Override + public SearchFacetField getFacetField(String name) { + return facetFields.get(name); + } + + @Override + public List getFacetFields() { + return new ArrayList<>(facetFields.values()); + } + +} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/searchengine/base/BaseSearchResultDocument.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/searchengine/base/BaseSearchResultDocument.java new file mode 100644 index 000000000..6c33823b6 --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/searchengine/base/BaseSearchResultDocument.java @@ -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> fieldValuesMap; + + public BaseSearchResultDocument(String uniqueId, Map> fieldValuesMap) { + this.uniqueId = uniqueId; + + Map> 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 getFieldNames() { + return fieldValuesMap.keySet(); + } + + @Override + public Object getFirstValue(String name) { + Collection 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 getFieldValues(String name) { + Collection values = fieldValuesMap.get(name); + if (values == null) { + return Collections.emptyList(); + } else { + return values; + } + } + + @Override + public Map> getFieldValuesMap() { + return fieldValuesMap; + } + +} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/searchengine/solr/SolrConversionUtils.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/searchengine/solr/SolrConversionUtils.java new file mode 100644 index 000000000..752c81b94 --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/searchengine/solr/SolrConversionUtils.java @@ -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 convertToSolrInputDocuments( + Collection docs) { + List 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 convertToSolrInputFieldMap( + Map fieldMap) { + Map 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 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> 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 convertToSearchFacetFieldMap( + List facetFields) { + Map 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 convertToSearchFacetFieldCounts( + List solrCounts) { + List searchCounts = new ArrayList<>(); + for (FacetField.Count solrCount : solrCounts) { + searchCounts.add(new BaseCount(solrCount.getName(), solrCount + .getCount())); + } + return searchCounts; + } + +} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/searchengine/solr/SolrSearchEngine.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/searchengine/solr/SolrSearchEngine.java new file mode 100644 index 000000000..7a3dbf993 --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/searchengine/solr/SolrSearchEngine.java @@ -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 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 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); + } + } + +} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/searchengine/solr/SolrSearchResultDocumentList.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/searchengine/solr/SolrSearchResultDocumentList.java new file mode 100644 index 000000000..196b87217 --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/searchengine/solr/SolrSearchResultDocumentList.java @@ -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 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 { + private final Iterator solrIterator; + + public SearchResultDocumentIterator(Iterator 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()); + } + +}