Java tutorial
/* * Copyright (2008-2012) Schibsted ASA * This file is part of Possom. * * Possom is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Possom 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Possom. If not, see <http://www.gnu.org/licenses/>. */ package no.sesat.search.mode.command; import java.io.Serializable; import java.lang.ref.Reference; import java.net.MalformedURLException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import no.sesat.commons.ioc.BaseContext; import no.sesat.commons.ioc.ContextWrapper; import no.sesat.commons.ref.ReferenceMap; import no.sesat.search.mode.config.SolrCommandConfig; import no.sesat.search.result.BasicResultItem; import no.sesat.search.result.BasicResultList; import no.sesat.search.result.FacetedSearchResult; import no.sesat.search.result.FacetedSearchResultImpl; import no.sesat.search.result.ResultItem; import no.sesat.search.result.ResultList; import no.sesat.search.site.Site; import no.sesat.search.site.config.SiteClassLoaderFactory; import no.sesat.search.site.config.SiteConfiguration; import no.sesat.search.site.config.Spi; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.apache.solr.client.solrj.SolrQuery; import org.apache.solr.client.solrj.SolrServer; import org.apache.solr.client.solrj.SolrServerException; import org.apache.solr.client.solrj.impl.CommonsHttpSolrServer; import org.apache.solr.client.solrj.response.QueryResponse; import org.apache.solr.common.SolrDocument; import org.apache.solr.common.SolrDocumentList; /** Searching against a Solr index using the Solrj client. * see http://wiki.apache.org/solr/Solrj * * The query syntax could be improved * see http://lucene.apache.org/java/docs/queryparsersyntax.html * * @version $Id$ */ public class SolrSearchCommand extends AbstractSearchCommand { // Constants ----------------------------------------------------- private static final Logger LOG = Logger.getLogger(SolrSearchCommand.class); // Attributes ---------------------------------------------------- private SolrServer server; private final FacetToolkit facetToolkit; // Static -------------------------------------------------------- private static final ReferenceMap<String, SolrServer> SERVERS = new ReferenceMap<String, SolrServer>( ReferenceMap.Type.SOFT, new ConcurrentHashMap<String, Reference<SolrServer>>()); // Constructors -------------------------------------------------- public SolrSearchCommand(final Context cxt) { super(cxt); try { final String serverUrlKey = ((SolrCommandConfig) cxt.getSearchConfiguration()).getServerUrl(); final SiteConfiguration siteConf = cxt.getDataModel().getSite().getSiteConfiguration(); final String serverUrl = siteConf.getProperty(serverUrlKey); server = SERVERS.get(serverUrl); if (null == server) { server = new CommonsHttpSolrServer(serverUrl); SERVERS.put(serverUrl, server); } } catch (MalformedURLException ex) { LOG.error(ex.getMessage(), ex); } facetToolkit = createFacetToolkit(); } // Public -------------------------------------------------------- @Override public ResultList<ResultItem> execute() { final ResultList<ResultItem> searchResult = null != facetToolkit ? new FacetedSearchResultImpl<ResultItem>() : new BasicResultList<ResultItem>(); try { // set up query final SolrQuery query = new SolrQuery().setQuery(getTransformedQuery()).setStart(getOffset()) .setRows(getSearchConfiguration().getResultsToReturn()); modifyQuery(query); DUMP.info(query.toString()); // query final QueryResponse response = server.query(query); final SolrDocumentList docs = response.getResults(); searchResult.setHitCount((int) docs.getNumFound()); // iterate through docs for (SolrDocument doc : docs) { searchResult.addResult(createItem(doc)); } collectFacets(response, searchResult); } catch (SolrServerException ex) { LOG.error(ex.getMessage(), ex); } return searchResult; } @Override public SolrCommandConfig getSearchConfiguration() { return (SolrCommandConfig) super.getSearchConfiguration(); } // Package protected --------------------------------------------- // Protected ----------------------------------------------------- /** Override this to set additional parameters in the SolrQuery. * Crucial for any override to call super.modifyQuery(query) **/ protected void modifyQuery(final SolrQuery query) { // @XXX does this ruin solr caching query.set("uniqueId", context.getDataModel().getParameters().getUniqueId()); // add any filtering query if (0 < getSearchConfiguration().getFilteringQuery().length()) { query.setFilterQueries(getSearchConfiguration().getFilteringQuery()); } // also add any filter if (0 < getFilter().length()) { query.addFilterQuery(getFilter()); } // custom query type if (null != getSearchConfiguration().getQueryType() && 0 < getSearchConfiguration().getQueryType().length()) { query.setQueryType(getSearchConfiguration().getQueryType()); } // The request handler may be configured in the index which fields to return in the results if (0 < getSearchConfiguration().getResultFieldMap().size()) { query.setFields(getSearchConfiguration().getResultFieldMap().keySet().toArray(new String[] {})); } createFacets(query); // when the root logger is set to DEBUG do not limit connection times if (Logger.getRootLogger().getLevel().isGreaterOrEqual(Level.INFO)) { query.setTimeAllowed(getSearchConfiguration().getTimeout()); } // sorting if (isUserSortable()) { final String sort = getUserSortBy(); if (null != sort) { final String[] sortSplit = sort.split(" "); query.addSortField(sortSplit[0], SolrQuery.ORDER.valueOf(sortSplit[1])); } } final Map<String, String> sortMap = getSearchConfiguration().getSortMap(); for (Map.Entry<String, String> entry : sortMap.entrySet()) { final SolrQuery.ORDER order = SolrQuery.ORDER.valueOf(entry.getValue()); query.addSortField(entry.getKey(), order); } } protected FacetToolkit createFacetToolkit() { FacetToolkit toolkit = null; final String toolkitName = getSearchConfiguration().getFacetToolkit(); if (null != toolkitName && 0 < toolkitName.length()) { toolkit = FacetToolkitFactory.getInstance(context, toolkitName); } return toolkit; } protected final void createFacets(final SolrQuery query) { if (null != facetToolkit) { facetToolkit.createFacets(context, query); } } protected final void collectFacets(final QueryResponse response, final ResultList<ResultItem> searchResult) { if (null != facetToolkit && searchResult instanceof FacetedSearchResult) { facetToolkit.collectFacets(context, response, (FacetedSearchResult<? extends ResultItem>) searchResult); } } protected BasicResultItem createItem(final SolrDocument doc) { Map<String, String> fieldNames; if (0 < getSearchConfiguration().getResultFieldMap().size()) { fieldNames = getSearchConfiguration().getResultFieldMap(); } else { // The request handler must be configured in the index as to which fields to return in the results fieldNames = new HashMap<String, String>(); for (String fieldName : doc.getFieldNames()) { fieldNames.put(fieldName, fieldName); } } BasicResultItem item = new BasicResultItem(); for (final Map.Entry<String, String> entry : fieldNames.entrySet()) { final Object value = doc.getFieldValue(entry.getKey()); if (value instanceof String) { item = item.addField(entry.getValue(), (String) doc.getFieldValue(entry.getKey())); } else if (value instanceof Serializable) { item = item.addObjectField(entry.getValue(), (Serializable) doc.getFieldValue(entry.getKey())); } else if (null == value) { LOG.debug("Unable to add to ResultItem, field " + entry.getKey() + " does not exist"); } else { LOG.warn("Unable to add to ResultItem this non Serializable object: " + value); } } return item; } @Override protected Collection<String> getReservedWords() { final Collection<String> words = new ArrayList<String>(super.getReservedWords()); // ampersand is treated as parameter separator just like in the restful URLs words.add("&"); return words; } // Private ------------------------------------------------------- // Inner classes ------------------------------------------------- /** * Provider to add facets from request to SolrQuery. */ public interface FacetToolkit { void createFacets(SearchCommand.Context context, SolrQuery query); void collectFacets(SearchCommand.Context context, QueryResponse response, FacetedSearchResult<? extends ResultItem> searchResult); } protected static final class FacetToolkitFactory { // Constructors -------------------------------------------------- /** Not possible to create a new instance of FacetToolkitFactory */ private FacetToolkitFactory() { } // Public -------------------------------------------------------- /** Factory call to instiantate a FacetToolkit. * * @param context context providing Resource * @param name the name of the class implementing FacetToolkit * @return */ public static FacetToolkit getInstance(final Context context, final String name) { try { final Site site = context.getDataModel().getSite().getSite(); final SiteClassLoaderFactory.Context ctlContext = ContextWrapper .wrap(SiteClassLoaderFactory.Context.class, new BaseContext() { public Spi getSpi() { return Spi.SEARCH_COMMAND_CONTROL; } public Site getSite() { return site; } }, context); final ClassLoader ctlLoader = SiteClassLoaderFactory.instanceOf(ctlContext).getClassLoader(); @SuppressWarnings("unchecked") final Class<? extends FacetToolkit> cls = (Class<? extends FacetToolkit>) ctlLoader.loadClass(name); return cls.newInstance(); } catch (ClassNotFoundException ex) { throw new IllegalArgumentException(ex); } catch (InstantiationException ex) { throw new IllegalArgumentException(ex); } catch (IllegalAccessException ex) { throw new IllegalArgumentException(ex); } } } }