You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by ja...@apache.org on 2024/07/30 15:50:20 UTC

(lucene) branch branch_9x updated: Introduce IndexSearcher#searchLeaf(LeafReaderContext, Weight, Collector) method (#13603)

This is an automated email from the ASF dual-hosted git repository.

javanna pushed a commit to branch branch_9x
in repository https://gitbox.apache.org/repos/asf/lucene.git


The following commit(s) were added to refs/heads/branch_9x by this push:
     new 539274f4530 Introduce IndexSearcher#searchLeaf(LeafReaderContext, Weight, Collector) method (#13603)
539274f4530 is described below

commit 539274f453097a5201ae1bec6a742e9e98f8d6eb
Author: Luca Cavanna <ja...@apache.org>
AuthorDate: Tue Jul 30 17:50:14 2024 +0200

    Introduce IndexSearcher#searchLeaf(LeafReaderContext, Weight, Collector) method (#13603)
    
    There's a couple of places in the codebase where we extend `IndexSearcher` to customize
    per leaf behaviour, and in order to do that, we need to override the entire search method
    that loops through the leaves. A good example is `ScorerIndexSearcher`.
    
    Adding a `searchLeaf` method that provides the per leaf behaviour makes those cases a little
    easier to deal with.
---
 lucene/CHANGES.txt                                 |  4 ++
 .../org/apache/lucene/search/IndexSearcher.java    | 69 ++++++++++++++--------
 .../org/apache/lucene/search/TestTopDocsMerge.java | 10 ++--
 .../lucene/search/grouping/TestGrouping.java       |  8 +--
 .../lucene/tests/search/ScorerIndexSearcher.java   | 33 +++++------
 5 files changed, 71 insertions(+), 53 deletions(-)

diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt
index d4a8343ca1f..6d17ba1d048 100644
--- a/lucene/CHANGES.txt
+++ b/lucene/CHANGES.txt
@@ -14,6 +14,10 @@ API Changes
 
 * GITHUB#13612: Hunspell: add Suggester#proceedPastRep to avoid losing relevant suggestions. (Peter Gromov)
 
+* GITHUB#13603: Introduced `IndexSearcher#searchLeaf(LeafReaderContext, Weight, Collector)` protected method to
+  facilitate customizing per-leaf behavior of search without requiring to override
+  `search(LeafReaderContext[], Weight, Collector)` which requires overriding the entire loop across the leaves (Luca Cavanna)
+
 New Features
 ---------------------
 
diff --git a/lucene/core/src/java/org/apache/lucene/search/IndexSearcher.java b/lucene/core/src/java/org/apache/lucene/search/IndexSearcher.java
index 16728675745..9f0d5b71a08 100644
--- a/lucene/core/src/java/org/apache/lucene/search/IndexSearcher.java
+++ b/lucene/core/src/java/org/apache/lucene/search/IndexSearcher.java
@@ -712,7 +712,8 @@ public class IndexSearcher {
   /**
    * Lower-level search API.
    *
-   * <p>{@link LeafCollector#collect(int)} is called for every document. <br>
+   * <p>{@link #searchLeaf(LeafReaderContext, Weight, Collector)} is called for every leaf
+   * partition. <br>
    *
    * <p>NOTE: this method executes the searches on all given leaves exclusively. To search across
    * all the searchers leaves use {@link #leafContexts}.
@@ -732,38 +733,54 @@ public class IndexSearcher {
     // threaded...? the Collector could be sync'd?
     // always use single thread:
     for (LeafReaderContext ctx : leaves) { // search each subreader
-      final LeafCollector leafCollector;
+      searchLeaf(ctx, weight, collector);
+    }
+  }
+
+  /**
+   * Lower-level search API
+   *
+   * <p>{@link LeafCollector#collect(int)} is called for every document. <br>
+   *
+   * @param ctx the leaf to execute the search against
+   * @param weight to match document
+   * @param collector to receive hits
+   * @throws TooManyClauses If a query would exceed {@link IndexSearcher#getMaxClauseCount()}
+   *     clauses.
+   */
+  protected void searchLeaf(LeafReaderContext ctx, Weight weight, Collector collector)
+      throws IOException {
+    final LeafCollector leafCollector;
+    try {
+      leafCollector = collector.getLeafCollector(ctx);
+    } catch (
+        @SuppressWarnings("unused")
+        CollectionTerminatedException e) {
+      // there is no doc of interest in this reader context
+      // continue with the following leaf
+      return;
+    }
+    BulkScorer scorer = weight.bulkScorer(ctx);
+    if (scorer != null) {
+      if (queryTimeout != null) {
+        scorer = new TimeLimitingBulkScorer(scorer, queryTimeout);
+      }
       try {
-        leafCollector = collector.getLeafCollector(ctx);
+        scorer.score(leafCollector, ctx.reader().getLiveDocs());
       } catch (
           @SuppressWarnings("unused")
           CollectionTerminatedException e) {
-        // there is no doc of interest in this reader context
+        // collection was terminated prematurely
         // continue with the following leaf
-        continue;
-      }
-      BulkScorer scorer = weight.bulkScorer(ctx);
-      if (scorer != null) {
-        if (queryTimeout != null) {
-          scorer = new TimeLimitingBulkScorer(scorer, queryTimeout);
-        }
-        try {
-          scorer.score(leafCollector, ctx.reader().getLiveDocs());
-        } catch (
-            @SuppressWarnings("unused")
-            CollectionTerminatedException e) {
-          // collection was terminated prematurely
-          // continue with the following leaf
-        } catch (
-            @SuppressWarnings("unused")
-            TimeLimitingBulkScorer.TimeExceededException e) {
-          partialResult = true;
-        }
+      } catch (
+          @SuppressWarnings("unused")
+          TimeLimitingBulkScorer.TimeExceededException e) {
+        partialResult = true;
       }
-      // Note: this is called if collection ran successfully, including the above special cases of
-      // CollectionTerminatedException and TimeExceededException, but no other exception.
-      leafCollector.finish();
     }
+    // Note: this is called if collection ran successfully, including the above special cases of
+    // CollectionTerminatedException and TimeExceededException, but no other exception.
+    leafCollector.finish();
   }
 
   /**
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestTopDocsMerge.java b/lucene/core/src/test/org/apache/lucene/search/TestTopDocsMerge.java
index e683b39c310..1dedbe3d3dc 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestTopDocsMerge.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestTopDocsMerge.java
@@ -43,26 +43,26 @@ import org.apache.lucene.util.BytesRef;
 public class TestTopDocsMerge extends LuceneTestCase {
 
   private static class ShardSearcher extends IndexSearcher {
-    private final List<LeafReaderContext> ctx;
+    private final LeafReaderContext ctx;
 
     public ShardSearcher(LeafReaderContext ctx, IndexReaderContext parent) {
       super(parent);
-      this.ctx = Collections.singletonList(ctx);
+      this.ctx = ctx;
     }
 
     public void search(Weight weight, Collector collector) throws IOException {
-      search(ctx, weight, collector);
+      searchLeaf(ctx, weight, collector);
     }
 
     public TopDocs search(Weight weight, int topN) throws IOException {
       TopScoreDocCollector collector = TopScoreDocCollector.create(topN, Integer.MAX_VALUE);
-      search(ctx, weight, collector);
+      searchLeaf(ctx, weight, collector);
       return collector.topDocs();
     }
 
     @Override
     public String toString() {
-      return "ShardSearcher(" + ctx.get(0) + ")";
+      return "ShardSearcher(" + ctx + ")";
     }
   }
 
diff --git a/lucene/grouping/src/test/org/apache/lucene/search/grouping/TestGrouping.java b/lucene/grouping/src/test/org/apache/lucene/search/grouping/TestGrouping.java
index 436a4ca45a2..38493a01bb6 100644
--- a/lucene/grouping/src/test/org/apache/lucene/search/grouping/TestGrouping.java
+++ b/lucene/grouping/src/test/org/apache/lucene/search/grouping/TestGrouping.java
@@ -1571,20 +1571,20 @@ public class TestGrouping extends LuceneTestCase {
   }
 
   private static class ShardSearcher extends IndexSearcher {
-    private final List<LeafReaderContext> ctx;
+    private final LeafReaderContext ctx;
 
     public ShardSearcher(LeafReaderContext ctx, IndexReaderContext parent) {
       super(parent);
-      this.ctx = Collections.singletonList(ctx);
+      this.ctx = ctx;
     }
 
     public void search(Weight weight, Collector collector) throws IOException {
-      search(ctx, weight, collector);
+      searchLeaf(ctx, weight, collector);
     }
 
     @Override
     public String toString() {
-      return "ShardSearcher(" + ctx.get(0).reader() + ")";
+      return "ShardSearcher(" + ctx.reader() + ")";
     }
   }
 }
diff --git a/lucene/test-framework/src/java/org/apache/lucene/tests/search/ScorerIndexSearcher.java b/lucene/test-framework/src/java/org/apache/lucene/tests/search/ScorerIndexSearcher.java
index d3a414bc988..a77229ef68f 100644
--- a/lucene/test-framework/src/java/org/apache/lucene/tests/search/ScorerIndexSearcher.java
+++ b/lucene/test-framework/src/java/org/apache/lucene/tests/search/ScorerIndexSearcher.java
@@ -17,7 +17,6 @@
 package org.apache.lucene.tests.search;
 
 import java.io.IOException;
-import java.util.List;
 import java.util.concurrent.Executor;
 import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.LeafReaderContext;
@@ -53,24 +52,22 @@ public class ScorerIndexSearcher extends IndexSearcher {
   }
 
   @Override
-  protected void search(List<LeafReaderContext> leaves, Weight weight, Collector collector)
+  protected void searchLeaf(LeafReaderContext ctx, Weight weight, Collector collector)
       throws IOException {
-    for (LeafReaderContext ctx : leaves) { // search each subreader
-      // we force the use of Scorer (not BulkScorer) to make sure
-      // that the scorer passed to LeafCollector.setScorer supports
-      // Scorer.getChildren
-      Scorer scorer = weight.scorer(ctx);
-      if (scorer != null) {
-        final DocIdSetIterator iterator = scorer.iterator();
-        final LeafCollector leafCollector = collector.getLeafCollector(ctx);
-        leafCollector.setScorer(scorer);
-        final Bits liveDocs = ctx.reader().getLiveDocs();
-        for (int doc = iterator.nextDoc();
-            doc != DocIdSetIterator.NO_MORE_DOCS;
-            doc = iterator.nextDoc()) {
-          if (liveDocs == null || liveDocs.get(doc)) {
-            leafCollector.collect(doc);
-          }
+    // we force the use of Scorer (not BulkScorer) to make sure
+    // that the scorer passed to LeafCollector.setScorer supports
+    // Scorer.getChildren
+    Scorer scorer = weight.scorer(ctx);
+    if (scorer != null) {
+      final DocIdSetIterator iterator = scorer.iterator();
+      final LeafCollector leafCollector = collector.getLeafCollector(ctx);
+      leafCollector.setScorer(scorer);
+      final Bits liveDocs = ctx.reader().getLiveDocs();
+      for (int doc = iterator.nextDoc();
+          doc != DocIdSetIterator.NO_MORE_DOCS;
+          doc = iterator.nextDoc()) {
+        if (liveDocs == null || liveDocs.get(doc)) {
+          leafCollector.collect(doc);
         }
       }
     }