View Javadoc

1   package com.atlassian.bonnie.search;
2   
3   import com.atlassian.bonnie.ILuceneConnection;
4   import com.atlassian.bonnie.LuceneException;
5   import com.atlassian.bonnie.LuceneUtils;
6   import com.atlassian.bonnie.Searcher;
7   import com.atlassian.bonnie.analyzer.LuceneAnalyzerFactory;
8   import org.apache.lucene.document.Document;
9   import org.apache.lucene.index.IndexReader;
10  import org.apache.lucene.index.Term;
11  import org.apache.lucene.index.TermEnum;
12  import org.apache.lucene.queryParser.MultiFieldQueryParser;
13  import org.apache.lucene.queryParser.ParseException;
14  import org.apache.lucene.queryParser.QueryParser;
15  import org.apache.lucene.search.*;
16  import org.slf4j.Logger;
17  import org.slf4j.LoggerFactory;
18  
19  import java.io.IOException;
20  import java.util.*;
21  
22  public class LuceneSearcher implements Searcher
23  {
24      private static final Logger log = LoggerFactory.getLogger(LuceneSearcher.class);
25      private ILuceneConnection luceneConnection;
26      private LuceneAnalyzerFactory luceneAnalyzerFactory;
27      private static final String[] HANDLE_ONLY_FIELDS = new String[] { BaseDocumentBuilder.FieldName.HANDLE};
28  
29      public void setLuceneConnection(ILuceneConnection luceneConnection)
30      {
31          this.luceneConnection = luceneConnection;
32      }
33  
34      public void setLuceneAnalyzerFactory(LuceneAnalyzerFactory luceneAnalyzerFactory)
35      {
36          this.luceneAnalyzerFactory = luceneAnalyzerFactory;
37      }
38  
39      public void setBooleanQueryMaxClause(int max)
40      {
41          BooleanQuery.setMaxClauseCount(max);
42      }
43  
44      /**
45       * Different from term query in that the query parameter specified is passed through an analyzer that may
46       * remove certain stop words before constructing a Query. Desirable for full text search fields. Undesirable for keyword
47       * searches (build a Term query instead).
48       *
49       * @param searchFields
50       * @param query
51       */
52      public Query buildStandardQuery(String[] searchFields, String query)
53      {
54          Query myquery;
55  
56          try
57          {
58              QueryParser qp = makeQueryParserForSearchFields(searchFields);
59              qp.setDefaultOperator(QueryParser.Operator.AND);
60              myquery = qp.parse(query);
61          }
62          catch (ParseException e)
63          {
64              throw new LuceneException("Couldn't parse the query successfully:" + e.getMessage());
65          }
66  
67          return myquery;
68      }
69  
70      private QueryParser makeQueryParserForSearchFields(String[] searchFields)
71      {
72          if (searchFields.length == 1)
73              return new QueryParser(searchFields[0], luceneAnalyzerFactory.createAnalyzer());
74          else
75              return new MultiFieldQueryParser(searchFields, luceneAnalyzerFactory.createAnalyzer());
76      }
77  
78      public Query buildStandardQuery(String defaultSearchField, String query)
79      {
80          return buildStandardQuery(new String[]{defaultSearchField}, query);
81      }
82  
83      public List search(final Query myquery)
84      {
85          return search(myquery, null);
86      }
87  
88      public List search(final Query myquery, final Sort sort)
89      {
90          final List result = new LinkedList();
91          luceneConnection.withSearch(new ILuceneConnection.SearcherAction()
92          {
93              public void perform(IndexSearcher searcher) throws IOException
94              {
95                  Hits hits = searcher.search(myquery, sort);
96  
97                  for (int i = 0, x = hits.length(); i < x; i++)
98                  {
99                      Document doc = hits.doc(i);
100                     String handle = doc.get(BaseDocumentBuilder.FieldName.HANDLE);
101                     result.add(handle);
102                 }
103             }
104         });
105         return result;
106     }
107 
108     public Query rewrite(final Query query)
109     {
110         return (Query) luceneConnection.withReader(new ILuceneConnection.ReaderAction()
111         {
112             public Object perform(IndexReader reader) throws IOException
113             {
114                 return query.rewrite(reader);
115             }
116         });
117     }
118 
119     public String explain(final Query myquery, final int docid)
120     {
121         final StringBuffer sb = new StringBuffer();
122         luceneConnection.withSearch(new ILuceneConnection.SearcherAction()
123         {
124             public void perform(IndexSearcher searcher) throws IOException
125             {
126                 Explanation e = searcher.explain(myquery, docid);
127                 sb.append(e.toHtml());
128             }
129         });
130         return sb.toString();
131     }
132 
133     public int searchCount(final Query myquery)
134     {
135         final int[] totalHits = new int[1];
136         luceneConnection.withSearch(new ILuceneConnection.SearcherAction()
137         {
138             public void perform(IndexSearcher searcher) throws IOException
139             {
140                 searcher.search(myquery, new HitCollector()
141                 {
142                     public void collect(int i, float v)
143                     {
144                         totalHits[0]++;
145                     }
146                 });
147             }
148         });
149         return totalHits[0];
150     }
151 
152     public int searchCount(final Query query, final Filter filter)
153     {
154         final int[] totalHits = new int[1];
155         luceneConnection.withSearch(new ILuceneConnection.SearcherAction()
156         {
157             public void perform(IndexSearcher searcher) throws IOException
158             {
159                 searcher.search(query, filter, new HitCollector()
160                 {
161                     public void collect(int i, float v)
162                     {
163                         totalHits[0]++;
164                     }
165                 });
166             }
167         });
168         return totalHits[0];
169     }
170 
171     public List searchForFields(Query myquery, Set fieldsToExtract, int startIndex, int numItems)
172     {
173         return searchForFields(myquery, fieldsToExtract, startIndex, numItems, null, new int[1]);
174     }
175 
176     public List searchForFields(final Query myquery, Set fieldsToExtract, final int startIndex, final int numItems, final Filter filter, final int[] filteredcount)
177     {
178         return searchForFields(myquery, fieldsToExtract, startIndex, numItems, null, null, new int[1]);
179     }
180 
181     public List searchForFields(final Query myquery, Set fieldsToExtract, final int startIndex, final int numItems, final Filter filter, final Sort sort, final int[] filteredcount)
182     {
183         String[] fieldsToExtractArr;
184 
185         if (fieldsToExtract != null && fieldsToExtract.size() > 0)
186             fieldsToExtractArr = (String[]) fieldsToExtract.toArray(new String[fieldsToExtract.size()]);
187         else
188             fieldsToExtractArr = HANDLE_ONLY_FIELDS;
189 
190         final String[] fieldsToExtractArr1 = fieldsToExtractArr;
191 
192         final ArrayList results = new ArrayList();
193         luceneConnection.withSearch(new ILuceneConnection.SearcherAction()
194         {
195             public void perform(IndexSearcher searcher) throws IOException
196             {
197                 Hits hits = searcher.search(myquery, filter, sort);
198 
199                 if (searcher instanceof FilterCountingSearcher)
200                 {
201                     FilterCountingSearcher filterCountingSearcher = ((FilterCountingSearcher) searcher);
202                     for (int i = 0; i < filterCountingSearcher.getFilteredCounts().length; i++)
203                     {
204                         if (i > filteredcount.length - 1)
205                         {
206                             log.error("Array passed in to store filter counts is too small. Actual: " + filteredcount.length 
207                                     + ". Expected: " + filterCountingSearcher.getFilteredCounts().length);
208                             break;
209                         }
210 
211                         filteredcount[i] = filterCountingSearcher.getFilteredCounts()[i];
212                     }
213                 }
214 
215                 results.ensureCapacity(startIndex + numItems);
216                 for (int i = 0, x = hits.length(); i < x; i++)
217                 {
218                     if (i < startIndex || i >= startIndex + numItems)
219                     {
220                         results.add(null);
221                         continue;
222                     }
223                     else
224                     {
225                         Document doc = hits.doc(i);
226                         Map result;
227                         if (fieldsToExtractArr1 == null)
228                         {
229                             result = LuceneUtils.buildMapFromDocument(doc);
230                         }
231                         else
232                         {
233                             result = new HashMap(fieldsToExtractArr1.length);
234                             for (int j = 0; j < fieldsToExtractArr1.length; j++)
235                             {
236                                 String fieldname = fieldsToExtractArr1[j];
237                                 result.put(fieldname, doc.get(fieldname));
238                             }
239                         }
240                         result.put("docid", new Integer(hits.id(i)));   // add docid so explanation is possible
241                         results.add(result);
242                     }
243                 }
244             }
245         });
246 
247         return results;
248     }
249 
250     public List getAllFieldValues(final String fieldName)
251     {
252         return (List) luceneConnection.withReader(new ILuceneConnection.ReaderAction()
253         {
254             public Object perform(IndexReader reader) throws IOException
255             {
256                 List values = new ArrayList();
257                 TermEnum terms = reader.terms(new Term(fieldName, ""));
258 
259                 // Terms starts on the first result, and advances on next(),
260                 // so we have to loop this way. -c
261                 do
262                 {
263                     Term term = terms.term();
264                     if (term == null)
265                         continue;
266 
267                     if (!fieldName.equals(term.field()))
268                         break;
269 
270                     values.add(term.text());
271 
272                 } while (terms.next());
273 
274                 if (terms != null)
275                 {
276                     terms.close();
277                 }
278                 return values;
279             }
280         });
281     }
282 
283 }