LUCENE-3465: pass correct docBase to Collector when using ExecutorService with IndexSearcher

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/branches/branch_3x@1176095 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt
index 98a0dac..d0892e0 100644
--- a/lucene/CHANGES.txt
+++ b/lucene/CHANGES.txt
@@ -51,6 +51,10 @@
 * LUCENE-3215: SloppyPhraseScorer sometimes computed Infinite freq
   (Robert Muir, Doron Cohen)  
 
+* LUCENE-3465: IndexSearcher with ExecutorService was always passing 0
+  for docBase to Collector.setNextReader.  (Robert Muir, Mike
+  McCandless)
+
 New Features
 
 * LUCENE-3448: Added FixedBitSet.and(other/DISI), andNot(other/DISI).
diff --git a/lucene/src/java/org/apache/lucene/search/IndexSearcher.java b/lucene/src/java/org/apache/lucene/search/IndexSearcher.java
index 0704c85..232b843 100644
--- a/lucene/src/java/org/apache/lucene/search/IndexSearcher.java
+++ b/lucene/src/java/org/apache/lucene/search/IndexSearcher.java
@@ -79,6 +79,8 @@
   private final ExecutorService executor;
   protected final IndexSearcher[] subSearchers;
 
+  private final int docBase;
+
   /** Creates a searcher searching the index in the named
    *  directory, with readOnly=true
    * @param path directory where IndexReader will be opened
@@ -129,14 +131,22 @@
    * 
    * @lucene.experimental */
   public IndexSearcher(IndexReader reader, IndexReader[] subReaders, int[] docStarts) {
-    this.reader = reader;
-    this.subReaders = subReaders;
-    this.docStarts = docStarts;
-    closeReader = false;
-    executor = null;
-    subSearchers = null;
+    this(reader, subReaders, docStarts, null);
   }
   
+  // Used only when we are an atomic sub-searcher in a parent
+  // IndexSearcher that has an ExecutorService, to record
+  // our docBase in the parent IndexSearcher:
+  private IndexSearcher(IndexReader r, int docBase) {
+    reader = r;
+    this.executor = null;
+    closeReader = false;
+    this.docBase = docBase;
+    subReaders = new IndexReader[] {r};
+    docStarts = new int[] {0};
+    subSearchers = null;
+  }
+
   /** Expert: directly specify the reader, subReaders and
    *  their docID starts, and an ExecutorService.  In this
    *  case, each segment will be separately searched using the
@@ -159,11 +169,12 @@
     } else {
       subSearchers = new IndexSearcher[subReaders.length];
       for(int i=0;i<subReaders.length;i++) {
-        subSearchers[i] = new IndexSearcher(subReaders[i]);
+        subSearchers[i] = new IndexSearcher(subReaders[i], docStarts[i]);
       }
     }
     closeReader = false;
     this.executor = executor;
+    docBase = 0;
   }
 
   private IndexSearcher(IndexReader r, boolean closeReader, ExecutorService executor) {
@@ -185,9 +196,10 @@
     } else {
       subSearchers = new IndexSearcher[subReaders.length];
       for (int i = 0; i < subReaders.length; i++) {
-        subSearchers[i] = new IndexSearcher(subReaders[i]);
+        subSearchers[i] = new IndexSearcher(subReaders[i], docStarts[i]);
       }
     }
+    docBase = 0;
   }
 
   protected void gatherSubReaders(List<IndexReader> allSubReaders, IndexReader r) {
@@ -418,7 +430,6 @@
    * @throws BooleanQuery.TooManyClauses
    */
   protected TopDocs search(Weight weight, Filter filter, ScoreDoc after, int nDocs) throws IOException {
-
     if (executor == null) {
       // single thread
       int limit = reader.maxDoc();
@@ -436,7 +447,7 @@
     
       for (int i = 0; i < subReaders.length; i++) { // search each sub
         runner.submit(
-                      new MultiSearcherCallableNoSort(lock, subSearchers[i], weight, filter, after, nDocs, hq, docStarts[i]));
+                      new MultiSearcherCallableNoSort(lock, subSearchers[i], weight, filter, after, nDocs, hq));
       }
 
       int totalHits = 0;
@@ -512,7 +523,7 @@
       final ExecutionHelper<TopFieldDocs> runner = new ExecutionHelper<TopFieldDocs>(executor);
       for (int i = 0; i < subReaders.length; i++) { // search each sub
         runner.submit(
-                      new MultiSearcherCallableWithSort(lock, subSearchers[i], weight, filter, nDocs, topCollector, sort, docStarts[i]));
+                      new MultiSearcherCallableWithSort(lock, subSearchers[i], weight, filter, nDocs, topCollector, sort));
       }
       int totalHits = 0;
       float maxScore = Float.NEGATIVE_INFINITY;
@@ -559,7 +570,7 @@
     // always use single thread:
     if (filter == null) {
       for (int i = 0; i < subReaders.length; i++) { // search each subreader
-        collector.setNextReader(subReaders[i], docStarts[i]);
+        collector.setNextReader(subReaders[i], docBase + docStarts[i]);
         Scorer scorer = weight.scorer(subReaders[i], !collector.acceptsDocsOutOfOrder(), true);
         if (scorer != null) {
           scorer.score(collector);
@@ -567,7 +578,7 @@
       }
     } else {
       for (int i = 0; i < subReaders.length; i++) { // search each subreader
-        collector.setNextReader(subReaders[i], docStarts[i]);
+        collector.setNextReader(subReaders[i], docBase + docStarts[i]);
         searchWithFilter(subReaders[i], weight, filter, collector);
       }
     }
@@ -712,10 +723,9 @@
     private final ScoreDoc after;
     private final int nDocs;
     private final HitQueue hq;
-    private final int docBase;
 
     public MultiSearcherCallableNoSort(Lock lock, IndexSearcher searchable, Weight weight,
-        Filter filter, ScoreDoc after, int nDocs, HitQueue hq, int docBase) {
+        Filter filter, ScoreDoc after, int nDocs, HitQueue hq) {
       this.lock = lock;
       this.searchable = searchable;
       this.weight = weight;
@@ -723,7 +733,6 @@
       this.after = after;
       this.nDocs = nDocs;
       this.hq = hq;
-      this.docBase = docBase;
     }
 
     public TopDocs call() throws IOException {
@@ -736,17 +745,17 @@
         docs = searchable.search (weight, filter, after, nDocs);
       }
       final ScoreDoc[] scoreDocs = docs.scoreDocs;
-      for (int j = 0; j < scoreDocs.length; j++) { // merge scoreDocs into hq
-        final ScoreDoc scoreDoc = scoreDocs[j];
-        scoreDoc.doc += docBase; // convert doc 
-        //it would be so nice if we had a thread-safe insert 
-        lock.lock();
-        try {
-          if (scoreDoc == hq.insertWithOverflow(scoreDoc))
+      //it would be so nice if we had a thread-safe insert 
+      lock.lock();
+      try {
+        for (int j = 0; j < scoreDocs.length; j++) { // merge scoreDocs into hq
+          final ScoreDoc scoreDoc = scoreDocs[j];
+          if (scoreDoc == hq.insertWithOverflow(scoreDoc)) {
             break;
-        } finally {
-          lock.unlock();
+          }
         }
+      } finally {
+        lock.unlock();
       }
       return docs;
     }
@@ -764,18 +773,16 @@
     private final Filter filter;
     private final int nDocs;
     private final TopFieldCollector hq;
-    private final int docBase;
     private final Sort sort;
 
     public MultiSearcherCallableWithSort(Lock lock, IndexSearcher searchable, Weight weight,
-                                         Filter filter, int nDocs, TopFieldCollector hq, Sort sort, int docBase) {
+                                         Filter filter, int nDocs, TopFieldCollector hq, Sort sort) {
       this.lock = lock;
       this.searchable = searchable;
       this.weight = weight;
       this.filter = filter;
       this.nDocs = nDocs;
       this.hq = hq;
-      this.docBase = docBase;
       this.sort = sort;
     }
 
@@ -825,7 +832,7 @@
           // iterate over the score docs and change their fields value
           for (int j2 = 0; j2 < docs.scoreDocs.length; j2++) {
             FieldDoc fd = (FieldDoc) docs.scoreDocs[j2];
-            fd.fields[j] = Integer.valueOf(((Integer) fd.fields[j]).intValue() + docBase);
+            fd.fields[j] = Integer.valueOf(((Integer) fd.fields[j]).intValue());
           }
           break;
         }
@@ -833,12 +840,13 @@
 
       lock.lock();
       try {
-        hq.setNextReader(searchable.getIndexReader(), docBase);
+        hq.setNextReader(searchable.getIndexReader(), searchable.docBase);
         hq.setScorer(fakeScorer);
         for(ScoreDoc scoreDoc : docs.scoreDocs) {
-          fakeScorer.doc = scoreDoc.doc;
+          final int docID = scoreDoc.doc - searchable.docBase;
+          fakeScorer.doc = docID;
           fakeScorer.score = scoreDoc.score;
-          hq.collect(scoreDoc.doc);
+          hq.collect(docID);
         }
       } finally {
         lock.unlock();