/** * Searcher.java * Author: Philip Kahle (philip.kahle@uibk.ac.at) * * This file is part of PrestoPRIME Preservation Platform (P4). * * Copyright (C) 2009-2012 University of Innsbruck, Austria * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package eu.prestoprime.search; import java.util.Map; import java.util.Map.Entry; import org.apache.log4j.Logger; import org.apache.solr.client.solrj.SolrQuery; import org.apache.solr.client.solrj.SolrServerException; import org.apache.solr.client.solrj.response.QueryResponse; import org.apache.solr.client.solrj.response.SpellCheckResponse.Suggestion; import eu.prestoprime.model.search.SearchResults; import eu.prestoprime.search.util.ResultProcessor; import eu.prestoprime.search.util.Schema; import eu.prestoprime.search.util.Schema.P4SortField; import eu.prestoprime.search.util.SolrQueryBuilder; import eu.prestoprime.search.util.SolrQueryUtils; import eu.prestoprime.search.util.suggestion.P4Suggestion; import eu.prestoprime.search.util.suggestion.P4Suggestions; public class Searcher { private static final Logger LOGGER = Logger.getLogger(Searcher.class); public Searcher() { LOGGER.info("Instance of Searcher was created."); } /** * Searches all fields that are quicksearch enabled (i.e. fields that are * added to the "text" field in solr's schema.xml via a copyfield * directive). The portion of the results that is shown ranges from result * #from to #(from+resultCount) Furthermore this method uses Solr's dismax * query handler which strips reserved character from the query string. * Thus, specifying fields for the search or conjuction/disjunction are not * allowed. Including and Excluding terms with +/- is possible though. The * user input string can be simply passed through as searchTerm for simple * error-prone search. * * @param searchTerm * @param from * specifies which portion of all results is shown * @param resultCount * specifies how many results are displayed * @param sortField * the field that is used for sorting * @param sortAsc * sortorder, if true -> ascending, else -> descending * @param facets * Map object containing all fields to facet on as key and, if * already selected, the filter value for this field * @return a SearchResults object containing all POJOs from the * QueryResponse */ public SearchResults simpleSearch(final String searchTerm, final int from, final int resultCount, final P4SortField sortField, final boolean sortAsc, Map<Schema.P4FacetField, String> facets) { SolrQuery query = new SolrQuery(searchTerm); // use this for simple error-prone syntax in simple search field: // add dismax requesthandler with deftype=dismax in solrconfig.xml // set default searchfield to catchall field in schema.xml // use this handler for the simple search: query.setQueryType("/dismax"); SolrQueryUtils.enableHighlightAllFields(query); SearchResults results = search(query, from, resultCount, sortField, sortAsc, facets); return results; } /** * Searches all fields that are quicksearch enabled if no fieldnames are * specified (i.e. fields that are added to the "text" field in solr's * schema.xml via a copyfield directive). The portion of the results that is * shown ranges from result #from to #(from+resultCount) The query is * handled by the Solr's standard queryHandler which allows the full-blown * query syntax. * * @param searchTerm * @param from * specifies which portion of all results is shown * @param resultCount * specifies how many results are displayed * @param sortField * the field that is used for sorting * @param sortAsc * sortorder, if true -> ascending, else -> descending * @param facets * Map object containing all fields to facet on as key and, if * already selected, the filter value for this field * @return a SearchResults object containing all POJOs from the * QueryResponse */ public SearchResults advancedSearch(final Map<Schema.searchField, String> fieldMap, final int from, final int resultCount, final P4SortField sortField, final boolean sortAsc, Map<Schema.P4FacetField, String> facets) { SearchResults results = new SearchResults(); String queryString = SolrQueryBuilder.buildQuery(fieldMap); if (!queryString.isEmpty()) { SolrQuery query = new SolrQuery(queryString); SolrQueryUtils.enableHighlight(query, fieldMap); results = search(query, from, resultCount, sortField, sortAsc, facets); } else { results.setErrorMessage("The built query was empty!"); } return results; } /** * Submits the SolrQuery object to SolrServer and handles sorting and * faceting. The portion of the results that is shown ranges from result * #from to #(from+resultCount) * * @param query * SolrQuery object * @param from * specifies which portion of all results is shown * @param resultCount * specifies how many results are displayed * @param sortField * the field that is used for sorting * @param sortAsc * sortorder, if true -> ascending, else -> descending * @param facetFilters * Map object containing all fields to facet on as key and, if * already selected, the filter value for this field * @return a SearchResults object containing all POJOs from the * QueryResponse */ private SearchResults search(SolrQuery query, final int from, final int resultCount, final P4SortField sortField, final boolean sortAsc, Map<Schema.P4FacetField, String> facetFilters) { QueryResponse response = new QueryResponse(); SearchResults results = new SearchResults(); SolrQueryUtils.setResultRange(query, from, resultCount); // TODO deal with sorting on multivalued fields -> copyfield of one // value to a dedicated sortfield is done in P4IndexObject's respective // setters if (sortField == null) { LOGGER.debug("SortField is NULL! setting sortTitle and descending"); SolrQueryUtils.setSortField(query, P4SortField.TITLE, sortAsc); } else { LOGGER.debug("SortField is " + sortField.getFieldName() + ". SortAsc? " + sortAsc); SolrQueryUtils.setSortField(query, sortField, sortAsc); } if (facetFilters == null) { SolrQueryUtils.enableFacets(query); facetFilters = SolrQueryUtils.getDefaultFacetMap(); } else { SolrQueryUtils.setFacets(query, facetFilters); } try { LOGGER.debug("Query = " + query.getQuery()); response = SolrServerConnection.getInstance().getSolrServer().query(query); results = ResultProcessor.extractResults(response, facetFilters); // ResultProcessor.setSelectedFacets(results, facetFilters); // set query params in results object results.getParams().setQuery(query.getQuery()); results.getParams().setFrom(from); results.getParams().setResultCount(resultCount); results.getParams().setSortField(sortField != null ? sortField.getFieldName() : P4SortField.TITLE.getFieldName()); results.getParams().setSortAscending(sortAsc); } catch (SolrServerException e) { LOGGER.fatal(e.getMessage()); LOGGER.fatal("Invalid Query = '" + query.getQuery() + "'."); results.setErrorMessage(e.getMessage()); } return results; } /** * Queries Solr for auto-complete suggestions for an entered term. See * SearchHandler "suggest" in solrConfig.xml for tweaking. * * @param term * @return */ public P4Suggestions getSuggestion(String term) { P4Suggestions suggs = new P4Suggestions(term); QueryResponse response = new QueryResponse(); SolrQuery query = new SolrQuery(term); query.setQueryType("/suggest"); try { response = SolrServerConnection.getInstance().getSolrServer().query(query); if (response.getSpellCheckResponse() != null && !response.getSpellCheckResponse().getSuggestionMap().entrySet().isEmpty()) { Map<String, Suggestion> resultMap = response.getSpellCheckResponse().getSuggestionMap(); for (Entry<String, Suggestion> entry : resultMap.entrySet()) { suggs.suggestions.add(new P4Suggestion(entry.getKey(), entry.getValue().getAlternatives())); } } } catch (SolrServerException e) { LOGGER.fatal(e); LOGGER.fatal("Could not query Solr for suggestions. Query = '" + query.getQuery() + "'."); } return suggs; } }