1 | |
package org.apache.maven.index; |
2 | |
|
3 | |
|
4 | |
|
5 | |
|
6 | |
|
7 | |
|
8 | |
|
9 | |
|
10 | |
|
11 | |
|
12 | |
|
13 | |
|
14 | |
|
15 | |
|
16 | |
|
17 | |
|
18 | |
|
19 | |
|
20 | |
|
21 | |
|
22 | |
import java.io.IOException; |
23 | |
import java.util.ArrayList; |
24 | |
import java.util.Arrays; |
25 | |
import java.util.Collection; |
26 | |
import java.util.Comparator; |
27 | |
import java.util.List; |
28 | |
import java.util.Map; |
29 | |
import java.util.Set; |
30 | |
import java.util.TreeMap; |
31 | |
import java.util.TreeSet; |
32 | |
|
33 | |
import org.apache.lucene.document.Document; |
34 | |
import org.apache.lucene.index.IndexReader; |
35 | |
import org.apache.lucene.index.MultiReader; |
36 | |
import org.apache.lucene.search.IndexSearcher; |
37 | |
import org.apache.lucene.search.Query; |
38 | |
import org.apache.lucene.search.ScoreDoc; |
39 | |
import org.apache.lucene.search.TopScoreDocCollector; |
40 | |
import org.apache.maven.index.context.IndexUtils; |
41 | |
import org.apache.maven.index.context.IndexingContext; |
42 | |
import org.apache.maven.index.context.NexusIndexSearcher; |
43 | |
import org.codehaus.plexus.component.annotations.Component; |
44 | |
import org.codehaus.plexus.logging.AbstractLogEnabled; |
45 | |
|
46 | |
|
47 | |
|
48 | |
|
49 | |
|
50 | |
|
51 | |
|
52 | |
@Component( role = SearchEngine.class ) |
53 | 208 | public class DefaultSearchEngine |
54 | |
extends AbstractLogEnabled |
55 | |
implements SearchEngine |
56 | |
{ |
57 | |
@Deprecated |
58 | |
public Set<ArtifactInfo> searchFlat( Comparator<ArtifactInfo> artifactInfoComparator, |
59 | |
IndexingContext indexingContext, Query query ) |
60 | |
throws IOException |
61 | |
{ |
62 | 0 | return searchFlatPaged( new FlatSearchRequest( query, artifactInfoComparator, indexingContext ), |
63 | |
Arrays.asList( indexingContext ), true ).getResults(); |
64 | |
} |
65 | |
|
66 | |
@Deprecated |
67 | |
public Set<ArtifactInfo> searchFlat( Comparator<ArtifactInfo> artifactInfoComparator, |
68 | |
Collection<IndexingContext> indexingContexts, Query query ) |
69 | |
throws IOException |
70 | |
{ |
71 | 0 | return searchFlatPaged( new FlatSearchRequest( query, artifactInfoComparator ), indexingContexts ).getResults(); |
72 | |
} |
73 | |
|
74 | |
public FlatSearchResponse searchFlatPaged( FlatSearchRequest request, Collection<IndexingContext> indexingContexts ) |
75 | |
throws IOException |
76 | |
{ |
77 | 86 | return searchFlatPaged( request, indexingContexts, false ); |
78 | |
} |
79 | |
|
80 | |
public FlatSearchResponse forceSearchFlatPaged( FlatSearchRequest request, |
81 | |
Collection<IndexingContext> indexingContexts ) |
82 | |
throws IOException |
83 | |
{ |
84 | 2359 | return searchFlatPaged( request, indexingContexts, true ); |
85 | |
} |
86 | |
|
87 | |
protected FlatSearchResponse searchFlatPaged( FlatSearchRequest request, |
88 | |
Collection<IndexingContext> indexingContexts, boolean ignoreContext ) |
89 | |
throws IOException |
90 | |
{ |
91 | 2445 | List<IndexingContext> contexts = getParticipatingContexts( indexingContexts, ignoreContext ); |
92 | |
|
93 | |
try |
94 | |
{ |
95 | 2445 | final TreeSet<ArtifactInfo> result = new TreeSet<ArtifactInfo>( request.getArtifactInfoComparator() ); |
96 | |
|
97 | 2445 | for ( IndexingContext ctx : contexts ) |
98 | |
{ |
99 | 2454 | ctx.lock(); |
100 | |
} |
101 | |
|
102 | 2445 | return new FlatSearchResponse( request.getQuery(), searchFlat( request, result, contexts, |
103 | |
request.getQuery() ), result ); |
104 | |
} |
105 | |
finally |
106 | |
{ |
107 | 2445 | for ( IndexingContext ctx : contexts ) |
108 | |
{ |
109 | 2454 | ctx.unlock(); |
110 | |
} |
111 | |
} |
112 | |
} |
113 | |
|
114 | |
|
115 | |
|
116 | |
public GroupedSearchResponse searchGrouped( GroupedSearchRequest request, |
117 | |
Collection<IndexingContext> indexingContexts ) |
118 | |
throws IOException |
119 | |
{ |
120 | 63 | return searchGrouped( request, indexingContexts, false ); |
121 | |
} |
122 | |
|
123 | |
public GroupedSearchResponse forceSearchGrouped( GroupedSearchRequest request, |
124 | |
Collection<IndexingContext> indexingContexts ) |
125 | |
throws IOException |
126 | |
{ |
127 | 0 | return searchGrouped( request, indexingContexts, true ); |
128 | |
} |
129 | |
|
130 | |
protected GroupedSearchResponse searchGrouped( GroupedSearchRequest request, |
131 | |
Collection<IndexingContext> indexingContexts, boolean ignoreContext ) |
132 | |
throws IOException |
133 | |
{ |
134 | 63 | List<IndexingContext> contexts = getParticipatingContexts( indexingContexts, ignoreContext ); |
135 | |
|
136 | |
try |
137 | |
{ |
138 | 63 | final TreeMap<String, ArtifactInfoGroup> result = |
139 | |
new TreeMap<String, ArtifactInfoGroup>( request.getGroupKeyComparator() ); |
140 | |
|
141 | 63 | for ( IndexingContext ctx : contexts ) |
142 | |
{ |
143 | 63 | ctx.lock(); |
144 | |
} |
145 | |
|
146 | 63 | return new GroupedSearchResponse( request.getQuery(), searchGrouped( request, result, |
147 | |
request.getGrouping(), contexts, request.getQuery() ), result ); |
148 | |
|
149 | |
} |
150 | |
finally |
151 | |
{ |
152 | 63 | for ( IndexingContext ctx : contexts ) |
153 | |
{ |
154 | 63 | ctx.unlock(); |
155 | |
} |
156 | |
} |
157 | |
} |
158 | |
|
159 | |
|
160 | |
|
161 | |
protected int searchFlat( FlatSearchRequest req, Collection<ArtifactInfo> result, |
162 | |
List<IndexingContext> participatingContexts, Query query ) |
163 | |
throws IOException |
164 | |
{ |
165 | 2445 | int hitCount = 0; |
166 | |
|
167 | 2445 | for ( IndexingContext context : participatingContexts ) |
168 | |
{ |
169 | 2454 | final TopScoreDocCollector collector = doSearchWithCeiling( req, context.getIndexSearcher(), query ); |
170 | |
|
171 | 2454 | if ( collector.getTotalHits() == 0 ) |
172 | |
{ |
173 | |
|
174 | 64 | continue; |
175 | |
} |
176 | |
|
177 | 2390 | ScoreDoc[] scoreDocs = collector.topDocs().scoreDocs; |
178 | |
|
179 | |
|
180 | |
|
181 | 2390 | hitCount += collector.getTotalHits(); |
182 | |
|
183 | 2390 | int start = 0; |
184 | |
|
185 | |
|
186 | 208027 | for ( int i = start; i < scoreDocs.length; i++ ) |
187 | |
{ |
188 | 205637 | Document doc = context.getIndexSearcher().doc( scoreDocs[i].doc ); |
189 | |
|
190 | 205637 | ArtifactInfo artifactInfo = IndexUtils.constructArtifactInfo( doc, context ); |
191 | |
|
192 | 205637 | if ( artifactInfo != null ) |
193 | |
{ |
194 | 205637 | artifactInfo.repository = context.getRepositoryId(); |
195 | |
|
196 | 205637 | artifactInfo.context = context.getId(); |
197 | |
|
198 | 205637 | result.add( artifactInfo ); |
199 | |
} |
200 | |
} |
201 | 2390 | } |
202 | |
|
203 | 2445 | return hitCount; |
204 | |
} |
205 | |
|
206 | |
protected int searchGrouped( GroupedSearchRequest req, Map<String, ArtifactInfoGroup> result, Grouping grouping, |
207 | |
List<IndexingContext> participatingContexts, Query query ) |
208 | |
throws IOException |
209 | |
{ |
210 | 63 | int hitCount = 0; |
211 | |
|
212 | 63 | for ( IndexingContext context : participatingContexts ) |
213 | |
{ |
214 | 63 | final TopScoreDocCollector collector = doSearchWithCeiling( req, context.getIndexSearcher(), query ); |
215 | |
|
216 | 63 | if ( collector.getTotalHits() > 0 ) |
217 | |
{ |
218 | 63 | ScoreDoc[] scoreDocs = collector.topDocs().scoreDocs; |
219 | |
|
220 | 63 | hitCount += collector.getTotalHits(); |
221 | |
|
222 | 1611 | for ( int i = 0; i < scoreDocs.length; i++ ) |
223 | |
{ |
224 | 1548 | Document doc = context.getIndexSearcher().doc( scoreDocs[i].doc ); |
225 | |
|
226 | 1548 | ArtifactInfo artifactInfo = IndexUtils.constructArtifactInfo( doc, context ); |
227 | |
|
228 | 1548 | if ( artifactInfo != null ) |
229 | |
{ |
230 | 1548 | artifactInfo.repository = context.getRepositoryId(); |
231 | |
|
232 | 1548 | artifactInfo.context = context.getId(); |
233 | |
|
234 | 1548 | if ( !grouping.addArtifactInfo( result, artifactInfo ) ) |
235 | |
{ |
236 | |
|
237 | 0 | hitCount--; |
238 | |
} |
239 | |
} |
240 | |
} |
241 | |
} |
242 | 63 | } |
243 | |
|
244 | 63 | return hitCount; |
245 | |
} |
246 | |
|
247 | |
|
248 | |
|
249 | |
public IteratorSearchResponse searchIteratorPaged( IteratorSearchRequest request, |
250 | |
Collection<IndexingContext> indexingContexts ) |
251 | |
throws IOException |
252 | |
{ |
253 | 28 | return searchIteratorPaged( request, indexingContexts, false ); |
254 | |
} |
255 | |
|
256 | |
public IteratorSearchResponse forceSearchIteratorPaged( IteratorSearchRequest request, |
257 | |
Collection<IndexingContext> indexingContexts ) |
258 | |
throws IOException |
259 | |
{ |
260 | 87 | return searchIteratorPaged( request, indexingContexts, true ); |
261 | |
} |
262 | |
|
263 | |
private IteratorSearchResponse searchIteratorPaged( IteratorSearchRequest request, |
264 | |
Collection<IndexingContext> indexingContexts, |
265 | |
boolean ignoreContext ) |
266 | |
throws IOException |
267 | |
{ |
268 | |
try |
269 | |
{ |
270 | 115 | List<IndexingContext> contexts = getParticipatingContexts( indexingContexts, ignoreContext ); |
271 | |
|
272 | 115 | IndexReader multiReader = getMergedIndexReader( indexingContexts, ignoreContext ); |
273 | |
|
274 | 115 | IndexSearcher indexSearcher = new NexusIndexSearcher( multiReader ); |
275 | |
|
276 | 115 | TopScoreDocCollector hits = doSearchWithCeiling( request, indexSearcher, request.getQuery() ); |
277 | |
|
278 | 115 | return new IteratorSearchResponse( request.getQuery(), hits.getTotalHits(), new DefaultIteratorResultSet( |
279 | |
request, indexSearcher, contexts, hits.topDocs() ) ); |
280 | |
} |
281 | 0 | catch ( Throwable e ) |
282 | |
{ |
283 | |
|
284 | 0 | for ( IndexingContext ctx : indexingContexts ) |
285 | |
{ |
286 | 0 | if ( ignoreContext || ctx.isSearchable() ) |
287 | |
{ |
288 | 0 | ctx.unlock(); |
289 | |
} |
290 | |
} |
291 | |
|
292 | 0 | if ( e instanceof IOException ) |
293 | |
{ |
294 | 0 | throw (IOException) e; |
295 | |
} |
296 | |
else |
297 | |
{ |
298 | |
|
299 | 0 | IOException ex = new IOException( e.getMessage() ); |
300 | 0 | ex.initCause( e ); |
301 | 0 | throw ex; |
302 | |
} |
303 | |
} |
304 | |
} |
305 | |
|
306 | |
|
307 | |
|
308 | |
protected TopScoreDocCollector doSearchWithCeiling( final AbstractSearchRequest request, |
309 | |
final IndexSearcher indexSearcher, final Query query ) |
310 | |
throws IOException |
311 | |
{ |
312 | 2632 | int topHitCount = getTopDocsCollectorHitNum( request, AbstractSearchRequest.UNDEFINED ); |
313 | |
|
314 | 2632 | if ( AbstractSearchRequest.UNDEFINED != topHitCount ) |
315 | |
{ |
316 | |
|
317 | 6 | final TopScoreDocCollector hits = TopScoreDocCollector.create( topHitCount, true ); |
318 | |
|
319 | 6 | indexSearcher.search( query, hits ); |
320 | |
|
321 | 6 | return hits; |
322 | |
} |
323 | |
else |
324 | |
{ |
325 | |
|
326 | 2626 | topHitCount = 1000; |
327 | |
|
328 | |
|
329 | 2626 | TopScoreDocCollector hits = TopScoreDocCollector.create( topHitCount, true ); |
330 | 2626 | indexSearcher.search( query, hits ); |
331 | |
|
332 | |
|
333 | 2626 | if ( topHitCount < hits.getTotalHits() ) |
334 | |
{ |
335 | 4 | topHitCount = hits.getTotalHits(); |
336 | |
|
337 | 4 | if ( getLogger().isDebugEnabled() ) |
338 | |
{ |
339 | |
|
340 | |
|
341 | 4 | getLogger().debug( |
342 | |
"Executing unbounded search, and fitting topHitCounts to " |
343 | |
+ topHitCount |
344 | |
+ ", an OOMEx might follow. To avoid OOM use narrower queries or limit your expectancy with request.setCount() method where appropriate. See MINDEXER-14 for details." ); |
345 | |
} |
346 | |
|
347 | |
|
348 | 4 | hits = TopScoreDocCollector.create( topHitCount, true ); |
349 | 4 | indexSearcher.search( query, hits ); |
350 | |
} |
351 | |
|
352 | 2626 | return hits; |
353 | |
} |
354 | |
} |
355 | |
|
356 | |
|
357 | |
|
358 | |
|
359 | |
protected List<IndexingContext> getParticipatingContexts( final Collection<IndexingContext> indexingContexts, |
360 | |
final boolean ignoreContext ) |
361 | |
{ |
362 | |
|
363 | |
|
364 | 2738 | final ArrayList<IndexingContext> contexts = new ArrayList<IndexingContext>( indexingContexts.size() ); |
365 | |
|
366 | 2738 | for ( IndexingContext ctx : indexingContexts ) |
367 | |
{ |
368 | 2751 | if ( ignoreContext || ctx.isSearchable() ) |
369 | |
{ |
370 | 2751 | contexts.add( ctx ); |
371 | |
} |
372 | |
} |
373 | |
|
374 | 2738 | return contexts; |
375 | |
} |
376 | |
|
377 | |
|
378 | |
|
379 | |
|
380 | |
|
381 | |
|
382 | |
|
383 | |
|
384 | |
|
385 | |
|
386 | |
protected IndexReader getMergedIndexReader( final Collection<IndexingContext> indexingContexts, |
387 | |
final boolean ignoreContext ) |
388 | |
throws IOException |
389 | |
{ |
390 | 115 | final List<IndexingContext> contexts = getParticipatingContexts( indexingContexts, ignoreContext ); |
391 | |
|
392 | |
try |
393 | |
{ |
394 | 115 | final ArrayList<IndexReader> contextsToSearch = new ArrayList<IndexReader>( contexts.size() ); |
395 | |
|
396 | 115 | for ( IndexingContext ctx : contexts ) |
397 | |
{ |
398 | 117 | ctx.lock(); |
399 | |
|
400 | 117 | contextsToSearch.add( ctx.getIndexReader() ); |
401 | |
} |
402 | |
|
403 | 115 | MultiReader multiReader = |
404 | |
new MultiReader( contextsToSearch.toArray( new IndexReader[contextsToSearch.size()] ) ); |
405 | |
|
406 | 115 | return multiReader; |
407 | |
} |
408 | 0 | catch ( Throwable e ) |
409 | |
{ |
410 | |
|
411 | 0 | for ( IndexingContext ctx : contexts ) |
412 | |
{ |
413 | 0 | ctx.unlock(); |
414 | |
} |
415 | |
|
416 | 0 | if ( e instanceof IOException ) |
417 | |
{ |
418 | 0 | throw (IOException) e; |
419 | |
} |
420 | |
else |
421 | |
{ |
422 | |
|
423 | 0 | IOException ex = new IOException( e.getMessage() ); |
424 | 0 | ex.initCause( e ); |
425 | 0 | throw ex; |
426 | |
} |
427 | |
} |
428 | |
} |
429 | |
|
430 | |
protected int getTopDocsCollectorHitNum( final AbstractSearchRequest request, final int ceiling ) |
431 | |
{ |
432 | 2632 | if ( request instanceof AbstractSearchPageableRequest ) |
433 | |
{ |
434 | 115 | final AbstractSearchPageableRequest prequest = (AbstractSearchPageableRequest) request; |
435 | |
|
436 | 115 | if ( AbstractSearchRequest.UNDEFINED != prequest.getCount() ) |
437 | |
{ |
438 | |
|
439 | 0 | return prequest.getCount() + prequest.getStart(); |
440 | |
} |
441 | 115 | } |
442 | |
else |
443 | |
{ |
444 | 2517 | if ( AbstractSearchRequest.UNDEFINED != request.getCount() ) |
445 | |
{ |
446 | |
|
447 | 6 | return request.getCount(); |
448 | |
} |
449 | |
} |
450 | |
|
451 | 2626 | return ceiling; |
452 | |
} |
453 | |
} |