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/10/14 11:49:18 UTC
(lucene) branch branch_10x updated: Fix potential NPE for KnnVectorQuery (#15334)
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 a8c1455fdfe Fix potential NPE for KnnVectorQuery (#15334)
a8c1455fdfe is described below
commit a8c1455fdfe62c0e45082788ef860f7dc7f7eef2
Author: Benjamin Trent <be...@gmail.com>
AuthorDate: Tue Oct 14 07:29:03 2025 -0400
Fix potential NPE for KnnVectorQuery (#15334)
* Fix potential NPE for KnnVectorQuery
* adding test
* removing unused test code
---
lucene/CHANGES.txt | 3 +++
.../org/apache/lucene/search/AbstractKnnVectorQuery.java | 14 +++++++++++---
.../org/apache/lucene/index/memory/TestMemoryIndex.java | 12 ++++++++++++
3 files changed, 26 insertions(+), 3 deletions(-)
diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt
index fe663fc2baf..3e4cb16919d 100644
--- a/lucene/CHANGES.txt
+++ b/lucene/CHANGES.txt
@@ -86,6 +86,9 @@ Bug Fixes
* GITHUB#15319: Fix potential hang in initialisation of TermsEnum and BaseTermsEnum (Chris Hegarty)
+* GITHUB#15334: Fix NPE for MemoryIndex when using nearest neighbor search. MemoryIndex does not support
+ nearest neighbor search, but it should not throw an NPE (Ben Trent)
+
Other
---------------------
* GITHUB#15237: Fix SmartChinese to only deserialize dictionary data from classpath with a native array
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 601d7ad5ff1..fbe18afce6d 100644
--- a/lucene/core/src/java/org/apache/lucene/search/AbstractKnnVectorQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/search/AbstractKnnVectorQuery.java
@@ -209,8 +209,15 @@ abstract class AbstractKnnVectorQuery extends Query {
final int cost = acceptDocs.cost();
QueryTimeout queryTimeout = timeLimitingKnnCollectorManager.getQueryTimeout();
- float leafProportion = ctx.reader().maxDoc() / (float) ctx.parent.reader().maxDoc();
- int perLeafTopK = perLeafTopKCalculation(k, leafProportion);
+ final int perLeafTopK;
+ // ctx.parent could be null if this is a MemoryIndex
+ if (ctx.parent != null) {
+ float leafProportion = ctx.reader().maxDoc() / (float) ctx.parent.reader().maxDoc();
+ perLeafTopK = perLeafTopKCalculation(k, leafProportion);
+ // We don't have a good way to estimate perLeafTopK here, so just do approximate search
+ } else {
+ perLeafTopK = k;
+ }
if (cost <= perLeafTopK) {
// If there are <= perLeafTopK possible matches, short-circuit and perform exact search, since
@@ -253,7 +260,8 @@ abstract class AbstractKnnVectorQuery extends Query {
int visitedLimit, KnnSearchStrategy searchStrategy, LeafReaderContext context)
throws IOException {
// The delegate supports optimistic collection
- if (delegate.isOptimistic()) {
+ // ctx.parent could be null if this is a MemoryIndex
+ if (delegate.isOptimistic() && context.parent != null) {
@SuppressWarnings("resource")
float leafProportion = context.reader().maxDoc() / (float) context.parent.reader().maxDoc();
int perLeafTopK = perLeafTopKCalculation(k, leafProportion);
diff --git a/lucene/memory/src/test/org/apache/lucene/index/memory/TestMemoryIndex.java b/lucene/memory/src/test/org/apache/lucene/index/memory/TestMemoryIndex.java
index 7c592868912..c8562f734e1 100644
--- a/lucene/memory/src/test/org/apache/lucene/index/memory/TestMemoryIndex.java
+++ b/lucene/memory/src/test/org/apache/lucene/index/memory/TestMemoryIndex.java
@@ -77,11 +77,14 @@ import org.apache.lucene.index.VectorSimilarityFunction;
import org.apache.lucene.search.CollectionStatistics;
import org.apache.lucene.search.DocIdSetIterator;
import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.KnnByteVectorQuery;
+import org.apache.lucene.search.KnnFloatVectorQuery;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.PhraseQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TermStatistics;
+import org.apache.lucene.search.TopDocs;
import org.apache.lucene.search.VectorScorer;
import org.apache.lucene.search.similarities.BM25Similarity;
import org.apache.lucene.search.similarities.ClassicSimilarity;
@@ -793,6 +796,11 @@ public class TestMemoryIndex extends LuceneTestCase {
.get(0)
.reader()
.getByteVectorValues("knnByteVectorValue"));
+ TopDocs docs =
+ mi.createSearcher()
+ .search(new KnnFloatVectorQuery("knnFloatA", new float[] {1.0f, 1.0f}, 1), 10);
+ // we don't really do knn search right now for MemoryIndex
+ assertEquals(0, docs.totalHits.value());
}
public void testKnnByteVectorOnlyOneVectorAllowed() throws IOException {
@@ -840,6 +848,10 @@ public class TestMemoryIndex extends LuceneTestCase {
.get(0)
.reader()
.getFloatVectorValues("knnFloatVectorValue"));
+ TopDocs docs =
+ mi.createSearcher().search(new KnnByteVectorQuery("knnByteA", new byte[] {1, 1}, 1), 10);
+ // we don't really do knn search right now for MemoryIndex
+ assertEquals(0, docs.totalHits.value());
}
private static void assertFloatVectorValue(MemoryIndex mi, String fieldName, float[] expected)