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