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