1   /**
2    * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
3    *
4    * The contents of this file are subject to the terms of the Liferay Enterprise
5    * Subscription License ("License"). You may not use this file except in
6    * compliance with the License. You can obtain a copy of the License by
7    * contacting Liferay, Inc. See the License for the specific language governing
8    * permissions and limitations under the License, including but not limited to
9    * distribution rights of the Software.
10   *
11   *
12   *
13   */
14  
15  package com.liferay.portal.search.lucene;
16  
17  import com.liferay.portal.kernel.dao.orm.QueryUtil;
18  import com.liferay.portal.kernel.log.Log;
19  import com.liferay.portal.kernel.log.LogFactoryUtil;
20  import com.liferay.portal.kernel.search.Document;
21  import com.liferay.portal.kernel.search.DocumentImpl;
22  import com.liferay.portal.kernel.search.Field;
23  import com.liferay.portal.kernel.search.Hits;
24  import com.liferay.portal.kernel.search.HitsImpl;
25  import com.liferay.portal.kernel.search.IndexSearcher;
26  import com.liferay.portal.kernel.search.Query;
27  import com.liferay.portal.kernel.search.SearchException;
28  import com.liferay.portal.kernel.search.Sort;
29  import com.liferay.portal.kernel.util.StringUtil;
30  import com.liferay.portal.kernel.util.Time;
31  import com.liferay.portal.kernel.util.Validator;
32  import com.liferay.portal.util.PropsValues;
33  
34  import java.io.IOException;
35  
36  import java.util.List;
37  
38  import org.apache.lucene.queryParser.ParseException;
39  import org.apache.lucene.search.BooleanQuery;
40  import org.apache.lucene.search.SortField;
41  import org.apache.lucene.search.TopFieldDocs;
42  
43  /**
44   * <a href="LuceneIndexSearcherImpl.java.html"><b><i>View Source</i></b></a>
45   *
46   * @author Bruno Farache
47   */
48  public class LuceneIndexSearcherImpl implements IndexSearcher {
49  
50      public Hits search(
51              long companyId, Query query, Sort[] sorts, int start, int end)
52          throws SearchException {
53  
54          if (_log.isDebugEnabled()) {
55              _log.debug("Query " + query);
56          }
57  
58          Hits hits = null;
59  
60          org.apache.lucene.search.IndexSearcher indexSearcher = null;
61          org.apache.lucene.search.Sort luceneSort = null;
62  
63          try {
64              indexSearcher = LuceneHelperUtil.getSearcher(companyId, true);
65  
66              if (sorts != null) {
67                  indexSearcher.setDefaultFieldSortScoring(true, true);
68  
69                  SortField[] sortFields = new SortField[sorts.length];
70  
71                  for (int i = 0; i < sorts.length; i++) {
72                      Sort sort = sorts[i];
73  
74                      sortFields[i] = new SortField(
75                          sort.getFieldName(), sort.getType(), sort.isReverse());
76                  }
77  
78                  luceneSort = new org.apache.lucene.search.Sort(sortFields);
79              }
80              else {
81                  luceneSort = new org.apache.lucene.search.Sort();
82              }
83  
84              long startTime = System.currentTimeMillis();
85  
86              TopFieldDocs topFieldDocs = indexSearcher.search(
87                  QueryTranslator.translate(query), null,
88                  PropsValues.INDEX_SEARCH_LIMIT, luceneSort);
89  
90              long endTime = System.currentTimeMillis();
91  
92              float searchTime = (float)(endTime - startTime) / Time.SECOND;
93  
94              hits = toHits(
95                  indexSearcher, topFieldDocs, query, startTime, searchTime,
96                  start, end);
97          }
98          catch (BooleanQuery.TooManyClauses tmc) {
99              int maxClauseCount = BooleanQuery.getMaxClauseCount();
100 
101             BooleanQuery.setMaxClauseCount(Integer.MAX_VALUE);
102 
103             try {
104                 long startTime = System.currentTimeMillis();
105 
106                 TopFieldDocs topFieldDocs = indexSearcher.search(
107                     QueryTranslator.translate(query), null,
108                     PropsValues.INDEX_SEARCH_LIMIT, luceneSort);
109 
110                 long endTime = System.currentTimeMillis();
111 
112                 float searchTime = (float)(endTime - startTime) / Time.SECOND;
113 
114                 hits = toHits(
115                     indexSearcher, topFieldDocs, query, startTime, searchTime,
116                     start, end);
117             }
118             catch (Exception e) {
119                 throw new SearchException(e);
120             }
121             finally {
122                 BooleanQuery.setMaxClauseCount(maxClauseCount);
123             }
124         }
125         catch (ParseException pe) {
126             _log.error("Query: " + query, pe);
127 
128             return new HitsImpl();
129         }
130         catch (Exception e) {
131             throw new SearchException(e);
132         }
133         finally {
134             try {
135                 if (indexSearcher != null) {
136                     indexSearcher.close();
137                 }
138             }
139             catch (IOException ioe) {
140                 throw new SearchException(ioe);
141             }
142         }
143 
144         if (_log.isDebugEnabled()) {
145             _log.debug(
146                 "Search found " + hits.getLength() + " results in " +
147                     hits.getSearchTime() + "ms");
148         }
149 
150         return hits;
151     }
152 
153     protected DocumentImpl getDocument(
154         org.apache.lucene.document.Document oldDoc) {
155 
156         DocumentImpl newDoc = new DocumentImpl();
157 
158         List<org.apache.lucene.document.Field> oldFields = oldDoc.getFields();
159 
160         for (org.apache.lucene.document.Field oldField : oldFields) {
161             String[] values = oldDoc.getValues(oldField.name());
162 
163             if ((values != null) && (values.length > 1)) {
164                 Field newField = new Field(
165                     oldField.name(), values, oldField.isTokenized());
166 
167                 newDoc.add(newField);
168             }
169             else {
170                 Field newField = new Field(
171                     oldField.name(), oldField.stringValue(),
172                     oldField.isTokenized());
173 
174                 newDoc.add(newField);
175             }
176         }
177 
178         return newDoc;
179     }
180 
181     protected String[] getQueryTerms(Query query) {
182         String[] queryTerms = new String[0];
183 
184         try {
185             queryTerms = LuceneHelperUtil.getQueryTerms(
186                 QueryTranslator.translate(query));
187         }
188         catch (ParseException pe) {
189             _log.error("Query: " + query, pe);
190         }
191 
192         return queryTerms;
193     }
194 
195     protected String getSnippet(
196             org.apache.lucene.document.Document doc, Query query, String field)
197         throws IOException {
198 
199         String[] values = doc.getValues(field);
200 
201         String snippet = null;
202 
203         if (Validator.isNull(values)) {
204             return snippet;
205         }
206 
207         String s = StringUtil.merge(values);
208 
209         try {
210             snippet = LuceneHelperUtil.getSnippet(
211                 QueryTranslator.translate(query), field, s);
212         }
213         catch (ParseException pe) {
214             _log.error("Query: " + query, pe);
215         }
216 
217         return snippet;
218     }
219 
220     protected Hits toHits(
221             org.apache.lucene.search.IndexSearcher indexSearcher,
222             TopFieldDocs topFieldDocs, Query query, long startTime,
223             float searchTime, int start, int end)
224         throws IOException {
225 
226         int length = topFieldDocs.totalHits;
227 
228         if ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS)) {
229             start = 0;
230             end = length;
231         }
232 
233         String[] queryTerms = getQueryTerms(query);
234 
235         Hits hits = new HitsImpl();
236 
237         if ((start > -1) && (start <= end)) {
238             if (end > length) {
239                 end = length;
240             }
241 
242             int subsetTotal = end - start;
243 
244             Document[] subsetDocs = new DocumentImpl[subsetTotal];
245             String[] subsetSnippets = new String[subsetTotal];
246             float[] subsetScores = new float[subsetTotal];
247 
248             int j = 0;
249 
250             for (int i = start; i < end; i++, j++) {
251                 org.apache.lucene.document.Document document =
252                     indexSearcher.doc(topFieldDocs.scoreDocs[i].doc);
253 
254                 subsetDocs[j] = getDocument(document);
255                 subsetSnippets[j] = getSnippet(document, query, Field.CONTENT);
256                 subsetScores[j] = topFieldDocs.scoreDocs[i].score;
257             }
258 
259             hits.setStart(startTime);
260             hits.setSearchTime(searchTime);
261             hits.setQueryTerms(queryTerms);
262             hits.setDocs(subsetDocs);
263             hits.setLength(length);
264             hits.setSnippets(subsetSnippets);
265             hits.setScores(subsetScores);
266         }
267 
268         return hits;
269     }
270 
271     private static Log _log = LogFactoryUtil.getLog(
272         LuceneIndexSearcherImpl.class);
273 
274 }