Java tutorial
/** * 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) + " …"; 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; } }