Index: lucene/src/test/org/apache/lucene/search/TestNRTManager.java =================================================================== --- lucene/src/test/org/apache/lucene/search/TestNRTManager.java (revision 1210719) +++ lucene/src/test/org/apache/lucene/search/TestNRTManager.java (working copy) @@ -28,6 +28,7 @@ import org.apache.lucene.document.Document; import org.apache.lucene.document.TextField; import org.apache.lucene.index.CorruptIndexException; +import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.IndexWriterConfig; import org.apache.lucene.index.IndexableField; @@ -205,7 +206,14 @@ TestNRTManager.this.warmCalled = true; s.search(new TermQuery(new Term("body", "united")), 10); } - }, false); + }, + new IndexWriter.IndexReaderWarmer() { + @Override + public void warm(IndexReader r) throws IOException { + TestNRTManager.this.warmCalled = true; + } + }, + false); nrtThread = new NRTManagerReopenThread(nrt, maxReopenSec, minReopenSec); nrtThread.setName("NRT Reopen Thread"); @@ -267,7 +275,7 @@ final CountDownLatch signal = new CountDownLatch(1); LatchedIndexWriter writer = new LatchedIndexWriter(d, conf, latch, signal); - final NRTManager manager = new NRTManager(writer, null, null, false); + final NRTManager manager = new NRTManager(writer, null, null, null, false); Document doc = new Document(); doc.add(newField("test","test", TextField.TYPE_STORED)); long gen = manager.addDocument(doc); Index: lucene/src/test/org/apache/lucene/search/TestSearcherManager.java =================================================================== --- lucene/src/test/org/apache/lucene/search/TestSearcherManager.java (revision 1210719) +++ lucene/src/test/org/apache/lucene/search/TestSearcherManager.java (working copy) @@ -29,7 +29,9 @@ import org.apache.lucene.analysis.MockAnalyzer; import org.apache.lucene.document.Document; import org.apache.lucene.index.ConcurrentMergeScheduler; +import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.IndexWriter; +import org.apache.lucene.index.IndexWriter.IndexReaderWarmer; import org.apache.lucene.index.Term; import org.apache.lucene.index.ThreadedIndexingAndSearchingTestCase; import org.apache.lucene.store.AlreadyClosedException; @@ -73,11 +75,17 @@ s.search(new TermQuery(new Term("body", "united")), 10); } }; + final IndexReaderWarmer readerWarmer = new IndexReaderWarmer() { + @Override + public void warm(IndexReader r) throws IOException { + TestSearcherManager.this.warmCalled = true; + } + }; if (random.nextBoolean()) { // TODO: can we randomize the applyAllDeletes? But // somehow for final searcher we must apply // deletes... - mgr = new SearcherManager(writer, true, warmer, es); + mgr = new SearcherManager(writer, true, warmer, readerWarmer, es); isNRT = true; } else { // SearcherManager needs to see empty commit: @@ -204,7 +212,7 @@ } }; final SearcherManager searcherManager = random.nextBoolean() ? new SearcherManager(dir, - warmer, es) : new SearcherManager(writer, random.nextBoolean(), warmer, es); + warmer, es) : new SearcherManager(writer, random.nextBoolean(), warmer, null, es); IndexSearcher searcher = searcherManager.acquire(); try { assertEquals(1, searcher.getIndexReader().numDocs()); Index: lucene/src/java/org/apache/lucene/search/SearcherManager.java =================================================================== --- lucene/src/java/org/apache/lucene/search/SearcherManager.java (revision 1210719) +++ lucene/src/java/org/apache/lucene/search/SearcherManager.java (working copy) @@ -24,6 +24,7 @@ import org.apache.lucene.index.CorruptIndexException; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.IndexWriter; +import org.apache.lucene.index.IndexWriter.IndexReaderWarmer; import org.apache.lucene.search.NRTManager; // javadocs import org.apache.lucene.search.IndexSearcher; // javadocs import org.apache.lucene.store.AlreadyClosedException; @@ -81,10 +82,11 @@ * Applying deletes can be costly, so if your app can tolerate deleted documents * being returned you might gain some performance by passing false. * See {@link IndexReader#openIfChanged(IndexReader, IndexWriter, boolean)}. - * @param warmer An optional {@link SearcherWarmer}. Pass + * @param searcherWarmer An optional {@link SearcherWarmer}. Pass * null if you don't require the searcher to warmed - * before going live. If this is non-null then a - * merged segment warmer is installed on the + * before going live. + * @param mergedSegmentWarmer An optional {@link IndexReaderWarmer}. If this is + * non-null then a merged segment warmer is installed on the * provided IndexWriter's config. * @param es An optional {@link ExecutorService} so different segments can * be searched concurrently (see {@link @@ -94,19 +96,17 @@ * @throws IOException */ public SearcherManager(IndexWriter writer, boolean applyAllDeletes, - final SearcherWarmer warmer, final ExecutorService es) throws IOException { + SearcherWarmer searcherWarmer, IndexReaderWarmer mergedSegmentWarmer, + ExecutorService es) throws IOException { this.es = es; - this.warmer = warmer; + this.warmer = searcherWarmer; currentSearcher = new IndexSearcher(IndexReader.open(writer, applyAllDeletes)); if (warmer != null) { - writer.getConfig().setMergedSegmentWarmer( - new IndexWriter.IndexReaderWarmer() { - @Override - public void warm(IndexReader reader) throws IOException { - warmer.warm(new IndexSearcher(reader, es)); - } - }); + warmer.warm(currentSearcher); } + if (mergedSegmentWarmer != null) { + writer.getConfig().setMergedSegmentWarmer(mergedSegmentWarmer); + } } /** @@ -114,9 +114,7 @@ * @param dir the directory to open the IndexReader on. * @param warmer An optional {@link SearcherWarmer}. Pass * null if you don't require the searcher to warmed - * before going live. If this is non-null then a - * merged segment warmer is installed on the - * provided IndexWriter's config. + * before going live. * @param es And optional {@link ExecutorService} so different segments can * be searched concurrently (see {@link * IndexSearcher#IndexSearcher(IndexReader,ExecutorService)}. Pass null @@ -129,6 +127,9 @@ this.es = es; this.warmer = warmer; currentSearcher = new IndexSearcher(IndexReader.open(dir, true), es); + if (warmer != null) { + warmer.warm(currentSearcher); + } } /** Index: lucene/src/java/org/apache/lucene/search/IndexSearcher.java =================================================================== --- lucene/src/java/org/apache/lucene/search/IndexSearcher.java (revision 1210719) +++ lucene/src/java/org/apache/lucene/search/IndexSearcher.java (working copy) @@ -789,33 +789,65 @@ return "IndexSearcher(" + reader + "; executor=" + executor + ")"; } + /** + * Provides all scoring statistics to queries + * @lucene.experimental + */ + public static abstract class StatsProvider { + /** + * Returns {@link TermStatistics} for a term + */ + public abstract TermStatistics termStatistics(Term term, TermContext context) throws IOException; + /** + * Returns {@link CollectionStatistics} for a field + */ + public abstract CollectionStatistics collectionStatistics(String field) throws IOException; + } + + // default "local" implementation + private StatsProvider statsProvider = new StatsProvider() { + @Override + public TermStatistics termStatistics(Term term, TermContext context) throws IOException { + return new TermStatistics(term.bytes(), context.docFreq(), context.totalTermFreq()); + }; + + @Override + public CollectionStatistics collectionStatistics(String field) throws IOException { + final int docCount; + final long sumTotalTermFreq; + final long sumDocFreq; + + Terms terms = MultiFields.getTerms(reader, field); + if (terms == null) { + docCount = 0; + sumTotalTermFreq = 0; + sumDocFreq = 0; + } else { + docCount = terms.getDocCount(); + sumTotalTermFreq = terms.getSumTotalTermFreq(); + sumDocFreq = terms.getSumDocFreq(); + } + return new CollectionStatistics(field, reader.maxDoc(), docCount, sumTotalTermFreq, sumDocFreq); + } + }; + + public void setStatsProvider(StatsProvider statsProvider) { + this.statsProvider = statsProvider; + } + /** * Returns {@link TermStatistics} for a term * @lucene.experimental */ - public TermStatistics termStatistics(Term term, TermContext context) throws IOException { - return new TermStatistics(term.bytes(), context.docFreq(), context.totalTermFreq()); - }; - + public final TermStatistics termStatistics(Term term, TermContext context) throws IOException { + return statsProvider.termStatistics(term, context); + } + /** * Returns {@link CollectionStatistics} for a field * @lucene.experimental */ - public CollectionStatistics collectionStatistics(String field) throws IOException { - final int docCount; - final long sumTotalTermFreq; - final long sumDocFreq; - - Terms terms = MultiFields.getTerms(reader, field); - if (terms == null) { - docCount = 0; - sumTotalTermFreq = 0; - sumDocFreq = 0; - } else { - docCount = terms.getDocCount(); - sumTotalTermFreq = terms.getSumTotalTermFreq(); - sumDocFreq = terms.getSumDocFreq(); - } - return new CollectionStatistics(field, reader.maxDoc(), docCount, sumTotalTermFreq, sumDocFreq); + public final CollectionStatistics collectionStatistics(String field) throws IOException { + return statsProvider.collectionStatistics(field); } } Index: lucene/src/java/org/apache/lucene/search/NRTManager.java =================================================================== --- lucene/src/java/org/apache/lucene/search/NRTManager.java (revision 1210719) +++ lucene/src/java/org/apache/lucene/search/NRTManager.java (working copy) @@ -31,6 +31,7 @@ import org.apache.lucene.index.CorruptIndexException; import org.apache.lucene.index.IndexReader; // javadocs import org.apache.lucene.index.IndexWriter; +import org.apache.lucene.index.IndexWriter.IndexReaderWarmer; import org.apache.lucene.index.IndexableField; import org.apache.lucene.index.Term; import org.apache.lucene.search.IndexSearcher; // javadocs @@ -67,18 +68,16 @@ * * @param writer IndexWriter to open near-real-time * readers - * @param warmer optional {@link SearcherWarmer}. Pass + * @param searcherWarmer optional {@link SearcherWarmer}. Pass * null if you don't require the searcher to warmed - * before going live. If this is non-null then a - * merged segment warmer is installed on the - * provided IndexWriter's config. - * - *

NOTE: the provided {@link SearcherWarmer} is - * not invoked for the initial searcher; you should - * warm it yourself if necessary. + * before going live. + * @param mergedSegmentWarmer optional {@link IndexReaderWarmer}. + * If this is non-null then a merged segment warmer is + * installed on the provided IndexWriter's config. */ - public NRTManager(IndexWriter writer, SearcherWarmer warmer) throws IOException { - this(writer, null, warmer, true); + public NRTManager(IndexWriter writer, SearcherWarmer searcherWarmer, + IndexReaderWarmer mergedSegmentWarmer) throws IOException { + this(writer, null, searcherWarmer, mergedSegmentWarmer, true); } /** @@ -89,36 +88,37 @@ * @param es optional ExecutorService so different segments can * be searched concurrently (see {@link IndexSearcher#IndexSearcher(IndexReader, ExecutorService)}. * Pass null to search segments sequentially. - * @param warmer optional {@link SearcherWarmer}. Pass + * @param searcherWarmer optional {@link SearcherWarmer}. Pass * null if you don't require the searcher to warmed - * before going live. If this is non-null then a - * merged segment warmer is installed on the + * before going live. + * @param mergedSegmentWarmer optional {@link IndexReaderWarmer}. If this + * is non-null then a merged segment warmer is installed on the * provided IndexWriter's config. - * - *

NOTE: the provided {@link SearcherWarmer} is - * not invoked for the initial searcher; you should - * warm it yourself if necessary. */ public NRTManager(IndexWriter writer, ExecutorService es, - SearcherWarmer warmer) throws IOException { - this(writer, es, warmer, true); + SearcherWarmer searcherWarmer, IndexReaderWarmer mergedSegmentWarmer) throws IOException { + this(writer, es, searcherWarmer, mergedSegmentWarmer, true); } /** * Expert: just like {@link - * #NRTManager(IndexWriter,ExecutorService,SearcherWarmer)}, + * #NRTManager(IndexWriter, ExecutorService, SearcherWarmer, IndexWriter.IndexReaderWarmer)}, * but you can also specify whether every searcher must * apply deletes. This is useful for cases where certain * uses can tolerate seeing some deleted docs, since * reopen time is faster if deletes need not be applied. */ public NRTManager(IndexWriter writer, ExecutorService es, - SearcherWarmer warmer, boolean alwaysApplyDeletes) throws IOException { + SearcherWarmer searcherWarmer, IndexReaderWarmer mergedSegmentWarmer, + boolean alwaysApplyDeletes) throws IOException { this.writer = writer; if (alwaysApplyDeletes) { - withoutDeletes = withDeletes = new SearcherManagerRef(true, 0, new SearcherManager(writer, true, warmer, es)); + withoutDeletes = withDeletes = new SearcherManagerRef(true, 0, + new SearcherManager(writer, true, searcherWarmer, mergedSegmentWarmer, es)); } else { - withDeletes = new SearcherManagerRef(true, 0, new SearcherManager(writer, true, warmer, es)); - withoutDeletes = new SearcherManagerRef(false, 0, new SearcherManager(writer, false, warmer, es)); + withDeletes = new SearcherManagerRef(true, 0, + new SearcherManager(writer, true, searcherWarmer, mergedSegmentWarmer, es)); + withoutDeletes = new SearcherManagerRef(false, 0, + new SearcherManager(writer, false, searcherWarmer, mergedSegmentWarmer, es)); } indexingGen = new AtomicLong(1); }