You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by be...@apache.org on 2025/07/16 15:54:08 UTC

(lucene) branch branch_10x updated: Knn dont filter match all (#14936)

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

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


The following commit(s) were added to refs/heads/branch_10x by this push:
     new e32b39fa0a2 Knn dont filter match all (#14936)
e32b39fa0a2 is described below

commit e32b39fa0a2317cd03645cd90a4470ac25949318
Author: Benjamin Trent <be...@gmail.com>
AuthorDate: Wed Jul 16 11:33:39 2025 -0400

    Knn dont filter match all (#14936)
    
    If a given filter is a MatchAllQuery, we shouldn't bother going through the filtered search path. It adds unnecessary overhead through:
    
    building a bitset for filter checking
    checking a bit set to see if a vector matches a filter
---
 lucene/CHANGES.txt                                 |  2 ++
 .../lucene/search/AbstractKnnVectorQuery.java      | 30 +++++++++++++++-------
 .../lucene/search/BaseKnnVectorQueryTestCase.java  | 14 ++++++++++
 3 files changed, 37 insertions(+), 9 deletions(-)

diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt
index d60be30f5bb..d4cc1cc535b 100644
--- a/lucene/CHANGES.txt
+++ b/lucene/CHANGES.txt
@@ -116,6 +116,8 @@ Optimizations
 
 * GITHUB#14935: Speed up PostingsEnum#nextPostings when block is encoded as bitset. (Guo Feng)
 
+* GITHUB#14936: Don't do the filtered knn path when the provided filter is a MatchAllDocsQuery. (Ben Trent)
+
 Changes in Runtime Behavior
 ---------------------
 * GITHUB#14823: Decrease TieredMergePolicy's default number of segments per
diff --git a/lucene/core/src/java/org/apache/lucene/search/AbstractKnnVectorQuery.java b/lucene/core/src/java/org/apache/lucene/search/AbstractKnnVectorQuery.java
index e68f5d64671..57e2859e5da 100644
--- a/lucene/core/src/java/org/apache/lucene/search/AbstractKnnVectorQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/search/AbstractKnnVectorQuery.java
@@ -72,16 +72,28 @@ abstract class AbstractKnnVectorQuery extends Query {
 
     final Weight filterWeight;
     if (filter != null) {
-      BooleanQuery booleanQuery =
-          new BooleanQuery.Builder()
-              .add(filter, BooleanClause.Occur.FILTER)
-              .add(new FieldExistsQuery(field), BooleanClause.Occur.FILTER)
-              .build();
-      Query rewritten = indexSearcher.rewrite(booleanQuery);
-      if (rewritten.getClass() == MatchNoDocsQuery.class) {
-        return rewritten;
+      // rewrite inner filter query first to determine if its a match all
+      // or match no docs query, so we can skip the knn search
+      Query rewrittenFilter = filter.rewrite(indexSearcher);
+      if (rewrittenFilter.getClass() == MatchNoDocsQuery.class) {
+        // If the filter is a match no docs query, we can also skip it
+        return rewrittenFilter;
+      }
+      if (rewrittenFilter.getClass() != MatchAllDocsQuery.class) {
+        BooleanQuery booleanQuery =
+            new BooleanQuery.Builder()
+                .add(filter, BooleanClause.Occur.FILTER)
+                .add(new FieldExistsQuery(field), BooleanClause.Occur.FILTER)
+                .build();
+        Query rewritten = indexSearcher.rewrite(booleanQuery);
+        if (rewritten.getClass() == MatchNoDocsQuery.class) {
+          return rewritten;
+        }
+        filterWeight = rewritten.createWeight(indexSearcher, ScoreMode.COMPLETE_NO_SCORES, 1f);
+      } else {
+        // If the filter is a match all docs query, we can skip it
+        filterWeight = null;
       }
-      filterWeight = indexSearcher.createWeight(rewritten, ScoreMode.COMPLETE_NO_SCORES, 1f);
     } else {
       filterWeight = null;
     }
diff --git a/lucene/core/src/test/org/apache/lucene/search/BaseKnnVectorQueryTestCase.java b/lucene/core/src/test/org/apache/lucene/search/BaseKnnVectorQueryTestCase.java
index b26cddddff1..d5151069bef 100644
--- a/lucene/core/src/test/org/apache/lucene/search/BaseKnnVectorQueryTestCase.java
+++ b/lucene/core/src/test/org/apache/lucene/search/BaseKnnVectorQueryTestCase.java
@@ -246,6 +246,20 @@ abstract class BaseKnnVectorQueryTestCase extends LuceneTestCase {
     }
   }
 
+  public void testMatchAllFilter() throws IOException {
+    try (Directory indexStore =
+            getIndexStore("field", new float[] {0, 1}, new float[] {1, 2}, new float[] {0, 0});
+        IndexReader reader = DirectoryReader.open(indexStore)) {
+      IndexSearcher searcher = newSearcher(reader);
+
+      // make sure we don't drop to exact search, even though the filter matches fewer than k docs
+      Query kvq =
+          getThrowingKnnVectorQuery("field", new float[] {0, 0}, 10, new MatchAllDocsQuery());
+      TopDocs topDocs = searcher.search(kvq, 3);
+      assertEquals(3, topDocs.totalHits.value());
+    }
+  }
+
   /** testDimensionMismatch */
   public void testDimensionMismatch() throws IOException {
     try (Directory indexStore =