--- /dev/null
+/**\r
+ * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.\r
+ *\r
+ * This library is free software; you can redistribute it and/or modify it under\r
+ * the terms of the GNU Lesser General Public License as published by the Free\r
+ * Software Foundation; either version 2.1 of the License, or (at your option)\r
+ * any later version.\r
+ *\r
+ * This library is distributed in the hope that it will be useful, but WITHOUT\r
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\r
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\r
+ * details.\r
+ */\r
+\r
+package com.liferay.portal.search.solr;\r
+\r
+import com.liferay.portal.kernel.dao.orm.QueryUtil;\r
+import com.liferay.portal.kernel.json.JSONArray;\r
+import com.liferay.portal.kernel.json.JSONObject;\r
+import com.liferay.portal.kernel.log.Log;\r
+import com.liferay.portal.kernel.log.LogFactoryUtil;\r
+import com.liferay.portal.kernel.search.Document;\r
+import com.liferay.portal.kernel.search.DocumentImpl;\r
+import com.liferay.portal.kernel.search.Field;\r
+import com.liferay.portal.kernel.search.Hits;\r
+import com.liferay.portal.kernel.search.HitsImpl;\r
+import com.liferay.portal.kernel.search.IndexSearcher;\r
+import com.liferay.portal.kernel.search.Query;\r
+import com.liferay.portal.kernel.search.QueryConfig;\r
+import com.liferay.portal.kernel.search.QueryTranslatorUtil;\r
+import com.liferay.portal.kernel.search.SearchContext;\r
+import com.liferay.portal.kernel.search.SearchException;\r
+import com.liferay.portal.kernel.search.Sort;\r
+import com.liferay.portal.kernel.search.facet.Facet;\r
+import com.liferay.portal.kernel.search.facet.RangeFacet;\r
+import com.liferay.portal.kernel.search.facet.collector.FacetCollector;\r
+import com.liferay.portal.kernel.search.facet.config.FacetConfiguration;\r
+import com.liferay.portal.kernel.util.ArrayUtil;\r
+import com.liferay.portal.kernel.util.GetterUtil;\r
+import com.liferay.portal.kernel.util.LocaleUtil;\r
+import com.liferay.portal.kernel.util.StringBundler;\r
+import com.liferay.portal.kernel.util.StringPool;\r
+import com.liferay.portal.kernel.util.StringUtil;\r
+import com.liferay.portal.kernel.util.Time;\r
+import com.liferay.portal.kernel.util.Validator;\r
+import com.liferay.portal.search.solr.facet.SolrFacetFieldCollector;\r
+import com.liferay.portal.search.solr.facet.SolrFacetQueryCollector;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Collection;\r
+import java.util.HashSet;\r
+import java.util.List;\r
+import java.util.Map;\r
+import java.util.Set;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+import org.apache.solr.client.solrj.SolrQuery.ORDER;\r
+import org.apache.solr.client.solrj.SolrQuery;\r
+import org.apache.solr.client.solrj.SolrRequest.METHOD;\r
+import org.apache.solr.client.solrj.SolrServer;\r
+import org.apache.solr.client.solrj.response.FacetField;\r
+import org.apache.solr.client.solrj.response.QueryResponse;\r
+import org.apache.solr.common.SolrDocument;\r
+import org.apache.solr.common.SolrDocumentList;\r
+import org.apache.solr.common.params.FacetParams;\r
+\r
+/**\r
+ * @author Bruno Farache\r
+ * @author Zsolt Berentey\r
+ * @author Raymond Augé\r
+ */\r
+public class SolrIndexSearcherImpl implements IndexSearcher {\r
+\r
+ public Hits search(SearchContext searchContext, Query query)\r
+ throws SearchException {\r
+\r
+ try {\r
+ return doSearch(searchContext, query);\r
+ }\r
+ catch (Exception e) {\r
+ _log.error(e, e);\r
+\r
+ throw new SearchException(e.getMessage());\r
+ }\r
+ }\r
+ \r
+ public Hits search(\r
+ String searchEngineId, long companyId, Query query, Sort[] sorts,\r
+ int start, int end)\r
+ throws SearchException {\r
+ \r
+ return search(searchEngineId, companyId, query, sorts, new ArrayList<String>(), start, end);\r
+ }\r
+\r
+ public Hits search(\r
+ String searchEngineId, long companyId, Query query, Sort[] sorts, List<String> filters,\r
+ int start, int end)\r
+ throws SearchException {\r
+\r
+ try {\r
+ SolrQuery solrQuery = translateQuery(\r
+ companyId, query, sorts, filters, start, end);\r
+ _log.debug("Search query "+solrQuery.toString());\r
+ QueryResponse queryResponse = _solrServer.query(\r
+ solrQuery, METHOD.POST);\r
+\r
+ boolean allResults = false;\r
+\r
+ if (solrQuery.getRows() == 0) {\r
+ allResults = true;\r
+ }\r
+\r
+ return subset(\r
+ solrQuery, query, query.getQueryConfig(), queryResponse,\r
+ allResults);\r
+ }\r
+ catch (Exception e) {\r
+ _log.error(e, e);\r
+\r
+ if (!_swallowException) {\r
+ throw new SearchException(e.getMessage());\r
+ }\r
+\r
+ return new HitsImpl();\r
+ }\r
+ }\r
+\r
+ public void setSolrServer(SolrServer solrServer) {\r
+ _solrServer = solrServer;\r
+ }\r
+\r
+ public void setSwallowException(boolean swallowException) {\r
+ _swallowException = swallowException;\r
+ }\r
+\r
+ protected Hits doSearch(SearchContext searchContext, Query query)\r
+ throws Exception {\r
+\r
+ SolrQuery solrQuery = translateQuery(\r
+ searchContext.getCompanyId(), query, searchContext.getSorts(), new ArrayList<String>(),\r
+ searchContext.getStart(), searchContext.getEnd());\r
+\r
+ Map<String, Facet> facets = searchContext.getFacets();\r
+\r
+ for (Facet facet : facets.values()) {\r
+ if (facet.isStatic()) {\r
+ continue;\r
+ }\r
+\r
+ FacetConfiguration facetConfiguration =\r
+ facet.getFacetConfiguration();\r
+\r
+ if (facet instanceof RangeFacet) {\r
+ solrQuery.addFacetField(facetConfiguration.getFieldName());\r
+\r
+ JSONObject dataJSONObject = facetConfiguration.getData();\r
+\r
+ JSONArray rangesJSONArray = dataJSONObject.getJSONArray(\r
+ "ranges");\r
+\r
+ if (rangesJSONArray == null) {\r
+ continue;\r
+ }\r
+\r
+ for (int i = 0; i < rangesJSONArray.length(); i++) {\r
+ JSONObject rangeJSONObject = rangesJSONArray.getJSONObject(\r
+ i);\r
+\r
+ String range = rangeJSONObject.getString("range");\r
+\r
+ String facetQuery =\r
+ facetConfiguration.getFieldName() +\r
+ StringPool.COLON + range;\r
+\r
+ solrQuery.addFacetQuery(facetQuery);\r
+ }\r
+ }\r
+ else {\r
+ solrQuery.addFacetField(facetConfiguration.getFieldName());\r
+ }\r
+\r
+ String facetSort = FacetParams.FACET_SORT_COUNT;\r
+\r
+ String order = facetConfiguration.getOrder();\r
+\r
+ if (order.equals("OrderValueAsc")) {\r
+ facetSort = FacetParams.FACET_SORT_INDEX;\r
+ }\r
+\r
+ solrQuery.add(\r
+ "f." + facetConfiguration.getFieldName() + ".facet.sort",\r
+ facetSort);\r
+ }\r
+\r
+ solrQuery.setFacetLimit(-1);\r
+\r
+ QueryResponse queryResponse = _solrServer.query(solrQuery, METHOD.POST);\r
+\r
+ boolean allResults = false;\r
+\r
+ if (solrQuery.getRows() == 0) {\r
+ allResults = true;\r
+ }\r
+\r
+ List<FacetField> facetFields = queryResponse.getFacetFields();\r
+\r
+ if (facetFields != null) {\r
+ for (FacetField facetField : facetFields) {\r
+ Facet facet = facets.get(facetField.getName());\r
+\r
+ FacetCollector facetCollector = null;\r
+\r
+ if (facet instanceof RangeFacet) {\r
+ facetCollector = new SolrFacetQueryCollector(\r
+ facetField.getName(), queryResponse.getFacetQuery());\r
+ }\r
+ else {\r
+ facetCollector = new SolrFacetFieldCollector(\r
+ facetField.getName(), facetField);\r
+ }\r
+\r
+ facet.setFacetCollector(facetCollector);\r
+ }\r
+ }\r
+\r
+ return subset(\r
+ solrQuery, query, query.getQueryConfig(), queryResponse,\r
+ allResults);\r
+ }\r
+\r
+ protected String getSnippet(\r
+ SolrDocument solrDocument, QueryConfig queryConfig,\r
+ Set<String> queryTerms,\r
+ Map<String, Map<String, List<String>>> highlights, String field) {\r
+\r
+ if (highlights == null) {\r
+ return StringPool.BLANK;\r
+ }\r
+\r
+ String key = (String)solrDocument.getFieldValue(Field.UID);\r
+\r
+ Map<String, List<String>> uidHighlights = highlights.get(key);\r
+\r
+ boolean localizedSearch = true;\r
+\r
+ String defaultLanguageId = LocaleUtil.toLanguageId(\r
+ LocaleUtil.getDefault());\r
+ String queryLanguageId = LocaleUtil.toLanguageId(\r
+ queryConfig.getLocale());\r
+\r
+ if (defaultLanguageId.equals(queryLanguageId)) {\r
+ localizedSearch = false;\r
+ }\r
+\r
+ if (localizedSearch) {\r
+ String localizedName = DocumentImpl.getLocalizedName(\r
+ queryConfig.getLocale(), field);\r
+\r
+ if (solrDocument.containsKey(localizedName)) {\r
+ field = localizedName;\r
+ }\r
+ }\r
+\r
+ List<String> snippets = uidHighlights.get(field);\r
+\r
+ String snippet = StringUtil.merge(snippets, "...");\r
+\r
+ if (Validator.isNotNull(snippet)) {\r
+ snippet = snippet + "...";\r
+ }\r
+ else {\r
+ snippet = StringPool.BLANK;\r
+ }\r
+\r
+ Pattern pattern = Pattern.compile("<em>(.*?)</em>");\r
+\r
+ Matcher matcher = pattern.matcher(snippet);\r
+\r
+ while (matcher.find()) {\r
+ queryTerms.add(matcher.group(1));\r
+ }\r
+\r
+ snippet = StringUtil.replace(snippet, "<em>", "");\r
+ snippet = StringUtil.replace(snippet, "</em>", "");\r
+\r
+ return snippet;\r
+ }\r
+\r
+ protected Hits subset(\r
+ SolrQuery solrQuery, Query query, QueryConfig queryConfig,\r
+ QueryResponse queryResponse, boolean allResults)\r
+ throws Exception {\r
+\r
+ long startTime = System.currentTimeMillis();\r
+\r
+ Hits hits = new HitsImpl();\r
+\r
+ SolrDocumentList solrDocumentList = queryResponse.getResults();\r
+\r
+ long total = solrDocumentList.getNumFound();\r
+\r
+ if (allResults && (total > 0)) {\r
+ solrQuery.setRows((int)total);\r
+\r
+ queryResponse = _solrServer.query(solrQuery);\r
+\r
+ return subset(solrQuery, query, queryConfig, queryResponse, false);\r
+ }\r
+\r
+ List<Document> documents = new ArrayList<Document>();\r
+ List<Float> scores = new ArrayList<Float>();\r
+ List<String> snippets = new ArrayList<String>();\r
+\r
+ float maxScore = -1;\r
+ Set<String> queryTerms = new HashSet<String>();\r
+ int subsetTotal = 0;\r
+\r
+ for (SolrDocument solrDocument : solrDocumentList) {\r
+ Document document = new DocumentImpl();\r
+\r
+ Collection<String> names = solrDocument.getFieldNames();\r
+\r
+ for (String name : names) {\r
+ Collection<Object> fieldValues = solrDocument.getFieldValues(\r
+ name);\r
+\r
+ Field field = new Field(\r
+ name,\r
+ ArrayUtil.toStringArray(\r
+ fieldValues.toArray(new Object[fieldValues.size()])));\r
+\r
+ document.add(field);\r
+ }\r
+\r
+ documents.add(document);\r
+\r
+ String snippet = StringPool.BLANK;\r
+\r
+ if (queryConfig.isHighlightEnabled()) {\r
+ snippet = getSnippet(\r
+ solrDocument, queryConfig, queryTerms,\r
+ queryResponse.getHighlighting(), Field.CONTENT);\r
+\r
+ if (Validator.isNull(snippet)) {\r
+ snippet = getSnippet(\r
+ solrDocument, queryConfig, queryTerms,\r
+ queryResponse.getHighlighting(), Field.TITLE);\r
+ }\r
+\r
+ if (Validator.isNotNull(snippet)) {\r
+ snippets.add(snippet);\r
+ }\r
+ }\r
+\r
+ if (queryConfig.isScoreEnabled()) {\r
+ float score = GetterUtil.getFloat(\r
+ String.valueOf(solrDocument.getFieldValue("score")));\r
+\r
+ if (score > maxScore) {\r
+ maxScore = score;\r
+ }\r
+\r
+ scores.add(score);\r
+ }\r
+ else {\r
+ scores.add(maxScore);\r
+ }\r
+\r
+ subsetTotal++;\r
+ }\r
+\r
+ hits.setDocs(documents.toArray(new Document[subsetTotal]));\r
+ hits.setLength((int)total);\r
+ hits.setQuery(query);\r
+ hits.setQueryTerms(queryTerms.toArray(new String[queryTerms.size()]));\r
+\r
+ Float[] scoresArray = scores.toArray(new Float[subsetTotal]);\r
+\r
+ if (queryConfig.isScoreEnabled() && (subsetTotal > 0) &&\r
+ (maxScore > 0)) {\r
+\r
+ for (int i = 0; i < scoresArray.length; i++) {\r
+ scoresArray[i] = scoresArray[i] / maxScore;\r
+ }\r
+ }\r
+\r
+ hits.setScores(scoresArray);\r
+\r
+ float searchTime =\r
+ (float)(System.currentTimeMillis() - startTime) / Time.SECOND;\r
+\r
+ hits.setSearchTime(searchTime);\r
+ hits.setSnippets(snippets.toArray(new String[subsetTotal]));\r
+ hits.setStart(startTime);\r
+\r
+ return hits;\r
+ }\r
+\r
+ protected SolrQuery translateQuery(\r
+ long companyId, Query query, Sort[] sorts, List<String> filters, int start, int end)\r
+ throws Exception {\r
+\r
+ QueryConfig queryConfig = query.getQueryConfig();\r
+\r
+ SolrQuery solrQuery = new SolrQuery();\r
+\r
+ if (queryConfig.isHighlightEnabled()) {\r
+ solrQuery.setHighlight(true);\r
+ solrQuery.setHighlightFragsize(\r
+ queryConfig.getHighlightFragmentSize());\r
+ solrQuery.setHighlightSnippets(\r
+ queryConfig.getHighlightSnippetSize());\r
+\r
+ String localizedContentName = DocumentImpl.getLocalizedName(\r
+ queryConfig.getLocale(), Field.CONTENT);\r
+\r
+ String localizedTitleName = DocumentImpl.getLocalizedName(\r
+ queryConfig.getLocale(), Field.TITLE);\r
+ \r
+ solrQuery.setParam(\r
+ "hl.fl", Field.CONTENT, localizedContentName, Field.TITLE,\r
+ localizedTitleName);\r
+ \r
+ for (String filterQuery : filters) {\r
+ solrQuery.addFilterQuery(filterQuery);\r
+ }\r
+ }\r
+\r
+ solrQuery.setIncludeScore(queryConfig.isScoreEnabled());\r
+\r
+ QueryTranslatorUtil.translateForSolr(query);\r
+\r
+ String queryString = query.toString();\r
+\r
+ StringBundler sb = new StringBundler(6);\r
+\r
+ sb.append(queryString);\r
+ sb.append(StringPool.SPACE);\r
+ sb.append(StringPool.PLUS);\r
+ sb.append(Field.COMPANY_ID);\r
+ sb.append(StringPool.COLON);\r
+ sb.append(companyId);\r
+\r
+ solrQuery.setQuery(sb.toString());\r
+\r
+ if ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS)) {\r
+ solrQuery.setRows(0);\r
+ }\r
+ else {\r
+ solrQuery.setRows(end - start);\r
+ solrQuery.setStart(start);\r
+ }\r
+\r
+ if (sorts != null) {\r
+ for (Sort sort : sorts) {\r
+ if (sort == null) {\r
+ continue;\r
+ }\r
+\r
+ String sortFieldName = sort.getFieldName();\r
+\r
+ if (DocumentImpl.isSortableTextField(sortFieldName)) {\r
+ sortFieldName = DocumentImpl.getSortableFieldName(\r
+ sortFieldName);\r
+ }\r
+\r
+ ORDER order = ORDER.asc;\r
+\r
+ if (Validator.isNull(sortFieldName) ||\r
+ !sortFieldName.endsWith("sortable")) {\r
+\r
+ sortFieldName = "score";\r
+\r
+ order = ORDER.desc;\r
+ }\r
+\r
+ if (sort.isReverse()) {\r
+ order = ORDER.desc;\r
+ }\r
+\r
+ solrQuery.addSortField(sortFieldName, order);\r
+ }\r
+ }\r
+\r
+ return solrQuery;\r
+ }\r
+\r
+ private static Log _log = LogFactoryUtil.getLog(\r
+ SolrIndexSearcherImpl.class);\r
+\r
+ private SolrServer _solrServer;\r
+ private boolean _swallowException;\r
+\r
+}
\ No newline at end of file