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
46
47
48
49
50
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)));
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
260
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 }