no.sesat.search.mode.command.SolrSearchCommand.java Source code

Java tutorial

Introduction

Here is the source code for no.sesat.search.mode.command.SolrSearchCommand.java

Source

/*
 * 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);
            }
        }

    }
}