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/01/29 15:25:12 UTC

(lucene) branch branch_10x updated: Add knn result consistency test (#14167)

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 63574600f3f Add knn result consistency test (#14167)
63574600f3f is described below

commit 63574600f3ffbcd37ce01c26e8679b5a4b1c98c2
Author: Benjamin Trent <be...@gmail.com>
AuthorDate: Wed Jan 29 10:12:36 2025 -0500

    Add knn result consistency test (#14167)
---
 .../lucene/search/BaseKnnVectorQueryTestCase.java  | 60 ++++++++++++++++++++++
 1 file changed, 60 insertions(+)

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 fe1f614dfcc..efe9500635a 100644
--- a/lucene/core/src/test/org/apache/lucene/search/BaseKnnVectorQueryTestCase.java
+++ b/lucene/core/src/test/org/apache/lucene/search/BaseKnnVectorQueryTestCase.java
@@ -24,6 +24,7 @@ import static org.apache.lucene.search.DocIdSetIterator.NO_MORE_DOCS;
 
 import java.io.IOException;
 import java.util.HashSet;
+import java.util.Random;
 import java.util.Set;
 import org.apache.lucene.codecs.KnnVectorsFormat;
 import org.apache.lucene.document.Document;
@@ -41,7 +42,9 @@ import org.apache.lucene.index.IndexWriterConfig;
 import org.apache.lucene.index.KnnVectorValues;
 import org.apache.lucene.index.LeafReader;
 import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.NoMergePolicy;
 import org.apache.lucene.index.QueryTimeout;
+import org.apache.lucene.index.SerialMergeScheduler;
 import org.apache.lucene.index.StoredFields;
 import org.apache.lucene.index.Term;
 import org.apache.lucene.index.VectorEncoding;
@@ -61,6 +64,7 @@ import org.apache.lucene.util.FixedBitSet;
 
 /** Test cases for AbstractKnnVectorQuery objects. */
 abstract class BaseKnnVectorQueryTestCase extends LuceneTestCase {
+  static final float EPSILON = 0.001f;
 
   abstract AbstractKnnVectorQuery getKnnVectorQuery(
       String field, float[] query, int k, Query queryFilter);
@@ -482,6 +486,62 @@ abstract class BaseKnnVectorQueryTestCase extends LuceneTestCase {
     }
   }
 
+  /** Tests with random vectors, number of documents, etc. Uses RandomIndexWriter. */
+  public void testRandomConsistencySingleThreaded() throws IOException {
+    assertRandomConsistency(false);
+  }
+
+  @AwaitsFix(bugUrl = "https://github.com/apache/lucene/issues/14180")
+  public void testRandomConsistencyMultiThreaded() throws IOException {
+    assertRandomConsistency(true);
+  }
+
+  private void assertRandomConsistency(boolean multiThreaded) throws IOException {
+    int numDocs = 100;
+    int dimension = 4;
+    int numIters = 10;
+    boolean everyDocHasAVector = random().nextBoolean();
+    Random r = random();
+    try (Directory d = newDirectoryForTest()) {
+      // To ensure consistency between seeded runs, remove some randomness
+      IndexWriterConfig iwc = new IndexWriterConfig(new MockAnalyzer(random()));
+      iwc.setMergeScheduler(new SerialMergeScheduler());
+      iwc.setMergePolicy(NoMergePolicy.INSTANCE);
+      iwc.setMaxBufferedDocs(numDocs);
+      iwc.setRAMBufferSizeMB(IndexWriterConfig.DISABLE_AUTO_FLUSH);
+      try (IndexWriter w = new IndexWriter(d, iwc)) {
+        for (int i = 0; i < numDocs; i++) {
+          Document doc = new Document();
+          if (everyDocHasAVector || random().nextInt(10) != 2) {
+            doc.add(getKnnVectorField("field", randomVector(dimension)));
+          }
+          w.addDocument(doc);
+          if (r.nextBoolean() && i % 50 == 0) {
+            w.flush();
+          }
+        }
+      }
+      try (IndexReader reader = DirectoryReader.open(d)) {
+        IndexSearcher searcher = newSearcher(reader, true, true, multiThreaded);
+        // first get the initial set of docs, and we expect all future queries to be exactly the
+        // same
+        int k = random().nextInt(80) + 1;
+        AbstractKnnVectorQuery query = getKnnVectorQuery("field", randomVector(dimension), k);
+        int n = random().nextInt(100) + 1;
+        TopDocs expectedResults = searcher.search(query, n);
+        for (int i = 0; i < numIters; i++) {
+          TopDocs results = searcher.search(query, n);
+          assertEquals(expectedResults.totalHits.value(), results.totalHits.value());
+          assertEquals(expectedResults.scoreDocs.length, results.scoreDocs.length);
+          for (int j = 0; j < results.scoreDocs.length; j++) {
+            assertEquals(expectedResults.scoreDocs[j].doc, results.scoreDocs[j].doc);
+            assertEquals(expectedResults.scoreDocs[j].score, results.scoreDocs[j].score, EPSILON);
+          }
+        }
+      }
+    }
+  }
+
   /** Tests with random vectors, number of documents, etc. Uses RandomIndexWriter. */
   public void testRandom() throws IOException {
     int numDocs = atLeast(100);