com.seajas.search.attender.service.search.SolrSearchService.java Source code

Java tutorial

Introduction

Here is the source code for com.seajas.search.attender.service.search.SolrSearchService.java

Source

/**
 * Copyright (C) 2013 Seajas, the Netherlands.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3, as
 * published by the Free Software Foundation.
 *
 * 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 com.seajas.search.attender.service.search;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

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.HttpSolrServer;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrDocument;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import com.seajas.search.attender.model.searchresult.SearchResult;
import com.seajas.search.bridge.profiler.wsdl.taxonomy.TaxonomyNode;
import com.seajas.search.bridge.profiler.wsdl.taxonomy.TaxonomyTranslation;
import com.seajas.search.profiler.wsdl.taxonomy.ITaxonomyListing;
import com.seajas.search.utilities.logging.SearchLogger;

/**
 * Attender service.
 * 
 * @author Jasper van Veghel <jasper@seajas.com>
 */
@Transactional
@Service
public class SolrSearchService implements SearchService {
    /**
     * The logger.
     */
    @Autowired
    private SearchLogger logger;

    /**
     * The Solr server.
     */
    private final SolrServer solrServer;

    /**
     * The shards.
     */
    private final List<String> shards;

    /**
     * The search languages.
     */
    private final List<String> searchLanguages;

    /**
     * Taxonomy listing service.
     */
    @Autowired
    private ITaxonomyListing taxonomyListingService;

    /**
     * Default constructor.
     * 
     * @param url
     * @param shards
     * @param searchLanguages
     */
    @Autowired
    public SolrSearchService(@Value("${attender.project.search.service.solr.url}") final String url,
            @Value("${attender.project.search.service.solr.shards}") final String shards,
            @Value("${attender.project.search.service.languages}") final String searchLanguages) {
        this.solrServer = new HttpSolrServer(url);

        ((HttpSolrServer) solrServer).setFollowRedirects(true);
        ((HttpSolrServer) solrServer).setAllowCompression(true);

        this.shards = Arrays.asList(StringUtils.tokenizeToStringArray(shards, ",", true, true));
        this.searchLanguages = Arrays.asList(StringUtils.tokenizeToStringArray(searchLanguages, ",", true, true));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public List<SearchResult> performSearch(final String query, final Date startDate, final Date endDate,
            final Map<String, String> parameters, final Integer maxResults, final String taxonomyLanguage) {
        List<SearchResult> results = new ArrayList<SearchResult>(maxResults);

        SolrQuery solrQuery = new SolrQuery();
        SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");

        // Combine the shards into a Solr-acceptable list

        String shardList = "";

        for (String shard : shards)
            shardList += (shardList.length() > 0 ? "," : "") + shard;

        solrQuery.setQuery(query);
        solrQuery.setRows(maxResults);
        solrQuery.setParam("shards", shardList);
        solrQuery.addFilterQuery("dcterms_created:[" + dateFormatter.format(startDate) + " TO "
                + dateFormatter.format(endDate) + "]");

        // Construct a field list for highlighting

        String[] fieldList = new String[(searchLanguages.size() * 2) + 2];

        fieldList[0] = "content";
        fieldList[1] = "title";

        for (int i = 2, j = 0; i < fieldList.length; i += 2, j++) {
            fieldList[i] = "content-" + searchLanguages.get(j);
            fieldList[i + 1] = "title-" + searchLanguages.get(j);
        }

        // Enable highlighting for the content (and leaving the title for now)

        solrQuery.setHighlight(true);
        solrQuery.setHighlightSnippets(2);
        solrQuery.setParam("hl.fl", fieldList);

        solrQuery.setParam("f.title.hl.fragsize", "0");
        solrQuery.setParam("f.content.hl.fragsize", "100");

        for (String language : searchLanguages) {
            solrQuery.setParam("f.title-" + language + ".hl.fragsize", "0");
            solrQuery.setParam("f.content-" + language + ".hl.fragsize", "100");
        }

        for (Entry<String, String> parameter : parameters.entrySet()) {
            if (parameter.getKey().equalsIgnoreCase("dcterms_coverage")
                    || parameter.getKey().equalsIgnoreCase("dcterms_format"))
                solrQuery.addFilterQuery(parameter.getKey() + ":(" + parameter.getValue() + ")");
            else if (parameter.getKey().equalsIgnoreCase("dcterms_type"))
                solrQuery.addFilterQuery("-" + parameter.getKey() + ":(" + parameter.getValue() + ")");
            else
                solrQuery.addFilterQuery(parameter.getKey() + ":\"" + parameter.getValue() + "\"");
        }

        try {
            QueryResponse response = solrServer.query(solrQuery);
            Iterator<SolrDocument> iterator = response.getResults().iterator();

            for (int i = 0; i < maxResults && iterator.hasNext(); i++) {
                SolrDocument document = iterator.next();

                // Retrieve the (potentially) highlighted summary

                String id = (String) document.getFieldValue("id");
                String language = (String) document.getFieldValue("dcterms_language");
                String author = (String) document.getFieldValue("author");
                String sourceId = null;

                // Simply take the last source ID

                if (document.getFieldValues("dcterms_coverage") != null
                        && document.getFieldValues("dcterms_coverage").size() > 0)
                    for (Object coverageId : document.getFieldValues("dcterms_coverage"))
                        sourceId = coverageId.toString();

                String contentField = StringUtils.hasText(language) ? "content-" + language : "content";
                String titleField = StringUtils.hasText(language) ? "title-" + language : "title";

                String summary = (String) document.getFieldValue(contentField);

                if (summary.length() > 300)
                    summary = summary.substring(0, 300) + " &hellip;";

                if (response.getHighlighting().get(id) != null) {
                    List<String> highlightSnippets = response.getHighlighting().get(id).get(contentField);

                    if (highlightSnippets != null && highlightSnippets.size() > 0) {
                        String fragmentPrefixOne = highlightSnippets.get(0).length() > 99 ? " .. " : "";

                        summary = fragmentPrefixOne + highlightSnippets.get(0) + fragmentPrefixOne;

                        if (highlightSnippets.size() > 1) {
                            String fragmentSuffixTwo = highlightSnippets.get(1).length() > 99 ? " .. " : "";

                            summary += highlightSnippets.get(1) + fragmentSuffixTwo;
                        }
                    }
                }

                results.add(new SearchResult((String) document.get("url"), (String) document.get(titleField),
                        author, sourceId, summary, (Date) document.get("dcterms_created"),
                        (String) document.get("dcterms_alternative")));
            }
        } catch (SolrServerException e) {
            logger.error("Could not retrieve Solr results for query '" + query + "'", e);
        }

        // Now collect and update the source IDs to their actual sources

        return adjustSourceIds(results, taxonomyLanguage);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public List<SearchResult> adjustSourceIds(final List<SearchResult> results, final String taxonomyLanguage) {
        List<Integer> sourceIds = new ArrayList<Integer>(results.size());

        for (SearchResult result : results) {
            Integer sourceId = convertToSourceId(result.getSource());

            if (sourceId != null && !sourceIds.contains(sourceId))
                sourceIds.add(sourceId);
        }

        List<TaxonomyNode> taxonomyNodes = taxonomyListingService.retrieveNodesById(sourceIds);

        for (SearchResult result : results) {
            Integer sourceId = convertToSourceId(result.getSource());

            if (sourceId != null)
                for (TaxonomyNode taxonomyNode : taxonomyNodes)
                    if (taxonomyNode.getId().equals(sourceId)) {
                        String sourceName = taxonomyNode.getName();

                        for (TaxonomyTranslation translation : taxonomyNode.getTranslations())
                            if (StringUtils.hasText(translation.getName())
                                    && translation.getLanguage().equals(taxonomyLanguage)) {
                                sourceName = translation.getName();

                                break;
                            }

                        result.setSource(sourceName);

                        break;
                    }
        }

        return results;
    }

    /**
     * Convert the given source ID to a valid number, or null.
     * 
     * @param sourceId
     * @return Integer
     */
    private Integer convertToSourceId(final String sourceId) {
        if (StringUtils.hasText(sourceId))
            try {
                return Integer.valueOf(sourceId);
            } catch (NumberFormatException e) {
                logger.error("Source ID '" + sourceId + "' could not be converted to a valid number");
            }

        return null;
    }
}