From bffb010599f869a8264602cf04b2c99ea59195d6 Mon Sep 17 00:00:00 2001 From: Anna Ruggero Date: Thu, 18 Sep 2025 12:25:53 +0200 Subject: [PATCH 01/16] Added threshold parameter for ACORN --- .../org/apache/solr/schema/DenseVectorField.java | 13 ++++++++++++- .../org/apache/solr/search/neural/KnnQParser.java | 4 +++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/solr/core/src/java/org/apache/solr/schema/DenseVectorField.java b/solr/core/src/java/org/apache/solr/schema/DenseVectorField.java index c355e167a76..52e67968ad5 100644 --- a/solr/core/src/java/org/apache/solr/schema/DenseVectorField.java +++ b/solr/core/src/java/org/apache/solr/schema/DenseVectorField.java @@ -41,6 +41,7 @@ import org.apache.lucene.search.KnnFloatVectorQuery; import org.apache.lucene.search.Query; import org.apache.lucene.search.SortField; +import org.apache.lucene.search.knn.KnnSearchStrategy; import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.hnsw.HnswGraph; import org.apache.solr.common.SolrException; @@ -371,16 +372,26 @@ public ValueSource getValueSource(SchemaField field, QParser parser) { } public Query getKnnVectorQuery( - String fieldName, String vectorToSearch, int topK, Query filterQuery) { + String fieldName, String vectorToSearch, int topK, Query filterQuery, Integer filteredSearchThreshold) { DenseVectorParser vectorBuilder = getVectorBuilder(vectorToSearch, DenseVectorParser.BuilderPhase.QUERY); switch (vectorEncoding) { case FLOAT32: + if (filteredSearchThreshold != null) { + KnnSearchStrategy knnSearchStrategy = new KnnSearchStrategy.Hnsw(filteredSearchThreshold); + return new KnnFloatVectorQuery( + fieldName, vectorBuilder.getFloatVector(), topK, filterQuery, knnSearchStrategy); + } return new KnnFloatVectorQuery( fieldName, vectorBuilder.getFloatVector(), topK, filterQuery); case BYTE: + if (filteredSearchThreshold != null) { + KnnSearchStrategy knnSearchStrategy = new KnnSearchStrategy.Hnsw(filteredSearchThreshold); + return new KnnByteVectorQuery( + fieldName, vectorBuilder.getByteVector(), topK, filterQuery, knnSearchStrategy); + } return new KnnByteVectorQuery(fieldName, vectorBuilder.getByteVector(), topK, filterQuery); default: throw new SolrException( diff --git a/solr/core/src/java/org/apache/solr/search/neural/KnnQParser.java b/solr/core/src/java/org/apache/solr/search/neural/KnnQParser.java index b6d9f2541cd..59cdcc1a495 100644 --- a/solr/core/src/java/org/apache/solr/search/neural/KnnQParser.java +++ b/solr/core/src/java/org/apache/solr/search/neural/KnnQParser.java @@ -28,6 +28,7 @@ public class KnnQParser extends AbstractVectorQParserBase { // retrieve the top K results based on the distance similarity function protected static final String TOP_K = "topK"; protected static final int DEFAULT_TOP_K = 10; + protected static final String FILTERED_SEARCH_THRESHOLD = "filteredSearchThreshold"; public KnnQParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) { super(qstr, localParams, params, req); @@ -39,8 +40,9 @@ public Query parse() throws SyntaxError { final DenseVectorField denseVectorType = getCheckedFieldType(schemaField); final String vectorToSearch = getVectorToSearch(); final int topK = localParams.getInt(TOP_K, DEFAULT_TOP_K); + final Integer filteredSearchThreshold = localParams.getInt(FILTERED_SEARCH_THRESHOLD); return denseVectorType.getKnnVectorQuery( - schemaField.getName(), vectorToSearch, topK, getFilterQuery()); + schemaField.getName(), vectorToSearch, topK, getFilterQuery(), filteredSearchThreshold); } } From e6e6251beae6f715efa509e3b8648b4e2e7f0c48 Mon Sep 17 00:00:00 2001 From: Anna Ruggero Date: Fri, 19 Sep 2025 10:54:53 +0200 Subject: [PATCH 02/16] Moved from if to if-else --- .../src/java/org/apache/solr/schema/DenseVectorField.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/solr/core/src/java/org/apache/solr/schema/DenseVectorField.java b/solr/core/src/java/org/apache/solr/schema/DenseVectorField.java index 52e67968ad5..aedcaa4db20 100644 --- a/solr/core/src/java/org/apache/solr/schema/DenseVectorField.java +++ b/solr/core/src/java/org/apache/solr/schema/DenseVectorField.java @@ -383,16 +383,18 @@ public Query getKnnVectorQuery( KnnSearchStrategy knnSearchStrategy = new KnnSearchStrategy.Hnsw(filteredSearchThreshold); return new KnnFloatVectorQuery( fieldName, vectorBuilder.getFloatVector(), topK, filterQuery, knnSearchStrategy); + } else { + return new KnnFloatVectorQuery( + fieldName, vectorBuilder.getFloatVector(), topK, filterQuery); } - return new KnnFloatVectorQuery( - fieldName, vectorBuilder.getFloatVector(), topK, filterQuery); case BYTE: if (filteredSearchThreshold != null) { KnnSearchStrategy knnSearchStrategy = new KnnSearchStrategy.Hnsw(filteredSearchThreshold); return new KnnByteVectorQuery( fieldName, vectorBuilder.getByteVector(), topK, filterQuery, knnSearchStrategy); + } else { + return new KnnByteVectorQuery(fieldName, vectorBuilder.getByteVector(), topK, filterQuery); } - return new KnnByteVectorQuery(fieldName, vectorBuilder.getByteVector(), topK, filterQuery); default: throw new SolrException( SolrException.ErrorCode.SERVER_ERROR, From aaac6eb6e02ffd776b844ce65251bbd4bca5a7c5 Mon Sep 17 00:00:00 2001 From: Anna Ruggero Date: Mon, 22 Sep 2025 10:59:44 +0200 Subject: [PATCH 03/16] Added test for getKnnVectorQuery --- .../solr/schema/DenseVectorFieldTest.java | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/solr/core/src/test/org/apache/solr/schema/DenseVectorFieldTest.java b/solr/core/src/test/org/apache/solr/schema/DenseVectorFieldTest.java index 7173bffbb9b..f4412f4a78e 100644 --- a/solr/core/src/test/org/apache/solr/schema/DenseVectorFieldTest.java +++ b/solr/core/src/test/org/apache/solr/schema/DenseVectorFieldTest.java @@ -18,15 +18,20 @@ import static org.hamcrest.core.Is.is; +import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import org.apache.lucene.index.VectorEncoding; import org.apache.lucene.index.VectorSimilarityFunction; +import org.apache.lucene.search.KnnFloatVectorQuery; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.knn.KnnSearchStrategy; import org.apache.solr.common.SolrException; import org.apache.solr.common.SolrInputDocument; import org.apache.solr.core.AbstractBadConfigTestBase; +import org.apache.solr.query.FilterQuery; import org.apache.solr.util.vector.DenseVectorParser; import org.junit.Before; import org.junit.Test; @@ -760,4 +765,30 @@ public void denseVectorByteEncoding_shouldRaiseExceptionWithFloatValues() throws deleteCore(); } } + + @Test + public void testFilteredSearchThreshold() throws Exception { + try { + Integer expectedThreshold = 30; + + initCore("solrconfig-basic.xml", "schema-densevector.xml"); + IndexSchema schema = h.getCore().getLatestSchema(); + SchemaField vectorField = schema.getField("vector"); + assertNotNull(vectorField); + DenseVectorField type = (DenseVectorField) vectorField.getType(); + Query vectorQuery = type.getKnnVectorQuery("vector", "[2, 1, 3, 4]", 3, null, expectedThreshold); + + Field strategy = vectorQuery.getClass().getSuperclass().getDeclaredField("searchStrategy"); + strategy.setAccessible(true); + Object strategyObj = strategy.get(vectorQuery); + + Field threshold = strategyObj.getClass().getDeclaredField("filteredSearchThreshold"); + threshold.setAccessible(true); + Integer thresholdObj = (Integer) threshold.get(strategyObj); + + assertEquals(expectedThreshold, thresholdObj); + } finally { + deleteCore(); + } + } } From 9bab79639b79cd18e9f59b9938c9136a7732bf6b Mon Sep 17 00:00:00 2001 From: Anna Ruggero Date: Mon, 22 Sep 2025 11:44:19 +0200 Subject: [PATCH 04/16] Added KnnQParserTests for filteredSearchThreshold --- .../solr/search/neural/KnnQParserTest.java | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/solr/core/src/test/org/apache/solr/search/neural/KnnQParserTest.java b/solr/core/src/test/org/apache/solr/search/neural/KnnQParserTest.java index f5d5668a7e5..ab2e71db014 100644 --- a/solr/core/src/test/org/apache/solr/search/neural/KnnQParserTest.java +++ b/solr/core/src/test/org/apache/solr/search/neural/KnnQParserTest.java @@ -18,10 +18,12 @@ import static org.apache.solr.search.neural.KnnQParser.DEFAULT_TOP_K; +import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; +import org.apache.lucene.search.Query; import org.apache.solr.SolrTestCaseJ4; import org.apache.solr.common.SolrException; import org.apache.solr.common.SolrInputDocument; @@ -967,4 +969,77 @@ public void knnQueryAsRerank_shouldAddSimilarityFunctionScore() { "//result/doc[3]/str[@name='id'][.='3']", "//result/doc[4]/str[@name='id'][.='9']"); } + + @Test + public void testFilteredSearchThresholdParsingFloatEncoding() throws Exception { + Integer expectedThreshold = 30; + String vectorToSearch = "[1.0, 2.0, 3.0, 4.0]"; + String topK = "4"; + + SolrParams params = params( + CommonParams.Q, + "{!knn f=" + vectorField + " topK=" + topK + " filteredSearchThreshold=" + expectedThreshold + "}" + + vectorToSearch); + SolrParams localParams = params("type", + "knn", + "f", + vectorField, + "topK", + topK, + "v", + vectorToSearch, + "filteredSearchThreshold", + expectedThreshold.toString()); + SolrQueryRequest req = req( + CommonParams.Q, + "{!knn f=" + vectorField + " topK=" + topK + " filteredSearchThreshold=" + expectedThreshold + "}" + + vectorToSearch); + + KnnQParser qparser = new KnnQParser(vectorToSearch, localParams, params, req); + try { + Query vectorQuery = qparser.parse(); + + Field strategy = vectorQuery.getClass().getSuperclass().getDeclaredField("searchStrategy"); + strategy.setAccessible(true); + Object strategyObj = strategy.get(vectorQuery); + + Field threshold = strategyObj.getClass().getDeclaredField("filteredSearchThreshold"); + threshold.setAccessible(true); + Integer thresholdObj = (Integer) threshold.get(strategyObj); + + assertEquals(expectedThreshold, thresholdObj); + } finally { + req.close(); + } + } + + @Test + public void testFilteredSearchThresholdParsingByteEncoding() throws Exception { + Integer expectedThreshold = 30; + String vectorToSearch = "[1, 2, 3, 4]"; + String topK = "4"; + + SolrParams params = params( + CommonParams.Q, + "{!knn f=" + vectorFieldByteEncoding + " topK=" + topK + " filteredSearchThreshold=" + expectedThreshold + "}" + vectorToSearch); + SolrParams localParams = params("type", "knn", "f", vectorFieldByteEncoding, "topK", topK, "v", vectorToSearch, "filteredSearchThreshold", expectedThreshold.toString()); + SolrQueryRequest req = req(CommonParams.Q, "{!knn f=" + vectorFieldByteEncoding + " topK=" + topK + " filteredSearchThreshold=" + expectedThreshold + "}" + vectorToSearch); + + KnnQParser qparser = new KnnQParser(vectorToSearch, localParams, params, req); + try { + Query vectorQuery = qparser.parse(); + + Field strategy = vectorQuery.getClass().getSuperclass().getDeclaredField("searchStrategy"); + strategy.setAccessible(true); + Object strategyObj = strategy.get(vectorQuery); + + Field threshold = strategyObj.getClass().getDeclaredField("filteredSearchThreshold"); + threshold.setAccessible(true); + Integer thresholdObj = (Integer) threshold.get(strategyObj); + + assertEquals(expectedThreshold, thresholdObj); + } finally { + req.close(); + } + } } From 9bd2f365ce4aa9880e306418b09b7b7c177a9994 Mon Sep 17 00:00:00 2001 From: Anna Ruggero Date: Mon, 22 Sep 2025 11:57:08 +0200 Subject: [PATCH 05/16] Gradlew tidy --- .../apache/solr/schema/DenseVectorField.java | 9 +- .../solr/schema/DenseVectorFieldTest.java | 6 +- .../solr/search/neural/KnnQParserTest.java | 91 ++++++++++++++----- 3 files changed, 77 insertions(+), 29 deletions(-) diff --git a/solr/core/src/java/org/apache/solr/schema/DenseVectorField.java b/solr/core/src/java/org/apache/solr/schema/DenseVectorField.java index aedcaa4db20..d319278eb1a 100644 --- a/solr/core/src/java/org/apache/solr/schema/DenseVectorField.java +++ b/solr/core/src/java/org/apache/solr/schema/DenseVectorField.java @@ -372,7 +372,11 @@ public ValueSource getValueSource(SchemaField field, QParser parser) { } public Query getKnnVectorQuery( - String fieldName, String vectorToSearch, int topK, Query filterQuery, Integer filteredSearchThreshold) { + String fieldName, + String vectorToSearch, + int topK, + Query filterQuery, + Integer filteredSearchThreshold) { DenseVectorParser vectorBuilder = getVectorBuilder(vectorToSearch, DenseVectorParser.BuilderPhase.QUERY); @@ -393,7 +397,8 @@ public Query getKnnVectorQuery( return new KnnByteVectorQuery( fieldName, vectorBuilder.getByteVector(), topK, filterQuery, knnSearchStrategy); } else { - return new KnnByteVectorQuery(fieldName, vectorBuilder.getByteVector(), topK, filterQuery); + return new KnnByteVectorQuery( + fieldName, vectorBuilder.getByteVector(), topK, filterQuery); } default: throw new SolrException( diff --git a/solr/core/src/test/org/apache/solr/schema/DenseVectorFieldTest.java b/solr/core/src/test/org/apache/solr/schema/DenseVectorFieldTest.java index f4412f4a78e..c281d353903 100644 --- a/solr/core/src/test/org/apache/solr/schema/DenseVectorFieldTest.java +++ b/solr/core/src/test/org/apache/solr/schema/DenseVectorFieldTest.java @@ -25,13 +25,10 @@ import java.util.Map; import org.apache.lucene.index.VectorEncoding; import org.apache.lucene.index.VectorSimilarityFunction; -import org.apache.lucene.search.KnnFloatVectorQuery; import org.apache.lucene.search.Query; -import org.apache.lucene.search.knn.KnnSearchStrategy; import org.apache.solr.common.SolrException; import org.apache.solr.common.SolrInputDocument; import org.apache.solr.core.AbstractBadConfigTestBase; -import org.apache.solr.query.FilterQuery; import org.apache.solr.util.vector.DenseVectorParser; import org.junit.Before; import org.junit.Test; @@ -776,7 +773,8 @@ public void testFilteredSearchThreshold() throws Exception { SchemaField vectorField = schema.getField("vector"); assertNotNull(vectorField); DenseVectorField type = (DenseVectorField) vectorField.getType(); - Query vectorQuery = type.getKnnVectorQuery("vector", "[2, 1, 3, 4]", 3, null, expectedThreshold); + Query vectorQuery = + type.getKnnVectorQuery("vector", "[2, 1, 3, 4]", 3, null, expectedThreshold); Field strategy = vectorQuery.getClass().getSuperclass().getDeclaredField("searchStrategy"); strategy.setAccessible(true); diff --git a/solr/core/src/test/org/apache/solr/search/neural/KnnQParserTest.java b/solr/core/src/test/org/apache/solr/search/neural/KnnQParserTest.java index ab2e71db014..2d05e1e8eaf 100644 --- a/solr/core/src/test/org/apache/solr/search/neural/KnnQParserTest.java +++ b/solr/core/src/test/org/apache/solr/search/neural/KnnQParserTest.java @@ -976,24 +976,40 @@ public void testFilteredSearchThresholdParsingFloatEncoding() throws Exception { String vectorToSearch = "[1.0, 2.0, 3.0, 4.0]"; String topK = "4"; - SolrParams params = params( - CommonParams.Q, - "{!knn f=" + vectorField + " topK=" + topK + " filteredSearchThreshold=" + expectedThreshold + "}" - + vectorToSearch); - SolrParams localParams = params("type", - "knn", - "f", - vectorField, - "topK", - topK, - "v", - vectorToSearch, - "filteredSearchThreshold", - expectedThreshold.toString()); - SolrQueryRequest req = req( - CommonParams.Q, - "{!knn f=" + vectorField + " topK=" + topK + " filteredSearchThreshold=" + expectedThreshold + "}" - + vectorToSearch); + SolrParams params = + params( + CommonParams.Q, + "{!knn f=" + + vectorField + + " topK=" + + topK + + " filteredSearchThreshold=" + + expectedThreshold + + "}" + + vectorToSearch); + SolrParams localParams = + params( + "type", + "knn", + "f", + vectorField, + "topK", + topK, + "v", + vectorToSearch, + "filteredSearchThreshold", + expectedThreshold.toString()); + SolrQueryRequest req = + req( + CommonParams.Q, + "{!knn f=" + + vectorField + + " topK=" + + topK + + " filteredSearchThreshold=" + + expectedThreshold + + "}" + + vectorToSearch); KnnQParser qparser = new KnnQParser(vectorToSearch, localParams, params, req); try { @@ -1019,11 +1035,40 @@ public void testFilteredSearchThresholdParsingByteEncoding() throws Exception { String vectorToSearch = "[1, 2, 3, 4]"; String topK = "4"; - SolrParams params = params( - CommonParams.Q, - "{!knn f=" + vectorFieldByteEncoding + " topK=" + topK + " filteredSearchThreshold=" + expectedThreshold + "}" + vectorToSearch); - SolrParams localParams = params("type", "knn", "f", vectorFieldByteEncoding, "topK", topK, "v", vectorToSearch, "filteredSearchThreshold", expectedThreshold.toString()); - SolrQueryRequest req = req(CommonParams.Q, "{!knn f=" + vectorFieldByteEncoding + " topK=" + topK + " filteredSearchThreshold=" + expectedThreshold + "}" + vectorToSearch); + SolrParams params = + params( + CommonParams.Q, + "{!knn f=" + + vectorFieldByteEncoding + + " topK=" + + topK + + " filteredSearchThreshold=" + + expectedThreshold + + "}" + + vectorToSearch); + SolrParams localParams = + params( + "type", + "knn", + "f", + vectorFieldByteEncoding, + "topK", + topK, + "v", + vectorToSearch, + "filteredSearchThreshold", + expectedThreshold.toString()); + SolrQueryRequest req = + req( + CommonParams.Q, + "{!knn f=" + + vectorFieldByteEncoding + + " topK=" + + topK + + " filteredSearchThreshold=" + + expectedThreshold + + "}" + + vectorToSearch); KnnQParser qparser = new KnnQParser(vectorToSearch, localParams, params, req); try { From 1aac006aca862e92749a27067178311aefeeb947 Mon Sep 17 00:00:00 2001 From: Anna Ruggero Date: Mon, 22 Sep 2025 12:09:13 +0200 Subject: [PATCH 06/16] Modified gradle.build for allowing reflection in tests --- solr/core/build.gradle | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/solr/core/build.gradle b/solr/core/build.gradle index 24fd610fc68..de3d0f5a498 100644 --- a/solr/core/build.gradle +++ b/solr/core/build.gradle @@ -211,3 +211,21 @@ dependencies { testImplementation libs.apache.httpcomponents.httpclient testImplementation libs.apache.httpcomponents.httpcore } + +tasks.withType(Test).configureEach { + jvmArgs(["--add-opens=java.base/java.lang.invoke=ALL-UNNAMED"]) + + doFirst { + if (filter.includePatterns.any { it.contains("KnnQParserTest") }) { + jvmArgs += [ + "--add-opens=org.apache.lucene.core/org.apache.lucene.search.knn=ALL-UNNAMED", + "--add-opens=org.apache.solr.core/org.apache.solr.search.neural=ALL-UNNAMED" + ] + } + } +} + +forbiddenApisTest { + bundledSignatures -= "jdk-reflection" +} + From f8821cc40c0752efe0d8057953ddb1f35bf69f0f Mon Sep 17 00:00:00 2001 From: Anna Ruggero Date: Mon, 22 Sep 2025 16:24:55 +0200 Subject: [PATCH 07/16] Extended Lucene Knn classes to add getter for searchStrategy --- solr/core/build.gradle | 17 --------- .../apache/solr/schema/DenseVectorField.java | 6 ++-- .../search/neural/SolrKnnByteVectorQuery.java | 15 ++++++++ .../neural/SolrKnnFloatVectorQuery.java | 15 ++++++++ .../solr/schema/DenseVectorFieldTest.java | 36 ++++++++++++++----- .../solr/search/neural/KnnQParserTest.java | 29 +++++---------- 6 files changed, 70 insertions(+), 48 deletions(-) create mode 100644 solr/core/src/java/org/apache/solr/search/neural/SolrKnnByteVectorQuery.java create mode 100644 solr/core/src/java/org/apache/solr/search/neural/SolrKnnFloatVectorQuery.java diff --git a/solr/core/build.gradle b/solr/core/build.gradle index de3d0f5a498..6e55e657413 100644 --- a/solr/core/build.gradle +++ b/solr/core/build.gradle @@ -212,20 +212,3 @@ dependencies { testImplementation libs.apache.httpcomponents.httpcore } -tasks.withType(Test).configureEach { - jvmArgs(["--add-opens=java.base/java.lang.invoke=ALL-UNNAMED"]) - - doFirst { - if (filter.includePatterns.any { it.contains("KnnQParserTest") }) { - jvmArgs += [ - "--add-opens=org.apache.lucene.core/org.apache.lucene.search.knn=ALL-UNNAMED", - "--add-opens=org.apache.solr.core/org.apache.solr.search.neural=ALL-UNNAMED" - ] - } - } -} - -forbiddenApisTest { - bundledSignatures -= "jdk-reflection" -} - diff --git a/solr/core/src/java/org/apache/solr/schema/DenseVectorField.java b/solr/core/src/java/org/apache/solr/schema/DenseVectorField.java index d319278eb1a..7abfc41955f 100644 --- a/solr/core/src/java/org/apache/solr/schema/DenseVectorField.java +++ b/solr/core/src/java/org/apache/solr/schema/DenseVectorField.java @@ -46,6 +46,8 @@ import org.apache.lucene.util.hnsw.HnswGraph; import org.apache.solr.common.SolrException; import org.apache.solr.search.QParser; +import org.apache.solr.search.neural.SolrKnnByteVectorQuery; +import org.apache.solr.search.neural.SolrKnnFloatVectorQuery; import org.apache.solr.uninverting.UninvertingReader; import org.apache.solr.util.vector.ByteDenseVectorParser; import org.apache.solr.util.vector.DenseVectorParser; @@ -385,7 +387,7 @@ public Query getKnnVectorQuery( case FLOAT32: if (filteredSearchThreshold != null) { KnnSearchStrategy knnSearchStrategy = new KnnSearchStrategy.Hnsw(filteredSearchThreshold); - return new KnnFloatVectorQuery( + return new SolrKnnFloatVectorQuery( fieldName, vectorBuilder.getFloatVector(), topK, filterQuery, knnSearchStrategy); } else { return new KnnFloatVectorQuery( @@ -394,7 +396,7 @@ public Query getKnnVectorQuery( case BYTE: if (filteredSearchThreshold != null) { KnnSearchStrategy knnSearchStrategy = new KnnSearchStrategy.Hnsw(filteredSearchThreshold); - return new KnnByteVectorQuery( + return new SolrKnnByteVectorQuery( fieldName, vectorBuilder.getByteVector(), topK, filterQuery, knnSearchStrategy); } else { return new KnnByteVectorQuery( diff --git a/solr/core/src/java/org/apache/solr/search/neural/SolrKnnByteVectorQuery.java b/solr/core/src/java/org/apache/solr/search/neural/SolrKnnByteVectorQuery.java new file mode 100644 index 00000000000..16e40b90a24 --- /dev/null +++ b/solr/core/src/java/org/apache/solr/search/neural/SolrKnnByteVectorQuery.java @@ -0,0 +1,15 @@ +package org.apache.solr.search.neural; + +import org.apache.lucene.search.KnnByteVectorQuery; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.knn.KnnSearchStrategy; + +public class SolrKnnByteVectorQuery extends KnnByteVectorQuery { + public SolrKnnByteVectorQuery(String field, byte[] target, int k, Query filter, KnnSearchStrategy searchStrategy) { + super(field, target, k, filter, searchStrategy); + } + + public KnnSearchStrategy getStrategy() { + return searchStrategy; + } +} diff --git a/solr/core/src/java/org/apache/solr/search/neural/SolrKnnFloatVectorQuery.java b/solr/core/src/java/org/apache/solr/search/neural/SolrKnnFloatVectorQuery.java new file mode 100644 index 00000000000..93b804aefcf --- /dev/null +++ b/solr/core/src/java/org/apache/solr/search/neural/SolrKnnFloatVectorQuery.java @@ -0,0 +1,15 @@ +package org.apache.solr.search.neural; + +import org.apache.lucene.search.KnnFloatVectorQuery; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.knn.KnnSearchStrategy; + +public class SolrKnnFloatVectorQuery extends KnnFloatVectorQuery { + public SolrKnnFloatVectorQuery(String field, float[] target, int k, Query filter, KnnSearchStrategy searchStrategy) { + super(field, target, k, filter, searchStrategy); + } + + public KnnSearchStrategy getStrategy() { + return searchStrategy; + } +} diff --git a/solr/core/src/test/org/apache/solr/schema/DenseVectorFieldTest.java b/solr/core/src/test/org/apache/solr/schema/DenseVectorFieldTest.java index c281d353903..fd3ee251cda 100644 --- a/solr/core/src/test/org/apache/solr/schema/DenseVectorFieldTest.java +++ b/solr/core/src/test/org/apache/solr/schema/DenseVectorFieldTest.java @@ -26,9 +26,12 @@ import org.apache.lucene.index.VectorEncoding; import org.apache.lucene.index.VectorSimilarityFunction; import org.apache.lucene.search.Query; +import org.apache.lucene.search.knn.KnnSearchStrategy; import org.apache.solr.common.SolrException; import org.apache.solr.common.SolrInputDocument; import org.apache.solr.core.AbstractBadConfigTestBase; +import org.apache.solr.search.neural.SolrKnnByteVectorQuery; +import org.apache.solr.search.neural.SolrKnnFloatVectorQuery; import org.apache.solr.util.vector.DenseVectorParser; import org.junit.Before; import org.junit.Test; @@ -764,7 +767,7 @@ public void denseVectorByteEncoding_shouldRaiseExceptionWithFloatValues() throws } @Test - public void testFilteredSearchThreshold() throws Exception { + public void testFilteredSearchThresholdKnnFloatVectorQuery() throws Exception { try { Integer expectedThreshold = 30; @@ -773,18 +776,33 @@ public void testFilteredSearchThreshold() throws Exception { SchemaField vectorField = schema.getField("vector"); assertNotNull(vectorField); DenseVectorField type = (DenseVectorField) vectorField.getType(); - Query vectorQuery = + SolrKnnFloatVectorQuery vectorQuery = (SolrKnnFloatVectorQuery) type.getKnnVectorQuery("vector", "[2, 1, 3, 4]", 3, null, expectedThreshold); + KnnSearchStrategy.Hnsw strategy = (KnnSearchStrategy.Hnsw) vectorQuery.getStrategy(); + Integer threshold = strategy.filteredSearchThreshold(); - Field strategy = vectorQuery.getClass().getSuperclass().getDeclaredField("searchStrategy"); - strategy.setAccessible(true); - Object strategyObj = strategy.get(vectorQuery); + assertEquals(expectedThreshold, threshold); + } finally { + deleteCore(); + } + } + + @Test + public void testFilteredSearchThresholdKnnByteVectorQuery() throws Exception { + try { + Integer expectedThreshold = 30; - Field threshold = strategyObj.getClass().getDeclaredField("filteredSearchThreshold"); - threshold.setAccessible(true); - Integer thresholdObj = (Integer) threshold.get(strategyObj); + initCore("solrconfig-basic.xml", "schema-densevector.xml"); + IndexSchema schema = h.getCore().getLatestSchema(); + SchemaField vectorField = schema.getField("vector_byte_encoding"); + assertNotNull(vectorField); + DenseVectorField type = (DenseVectorField) vectorField.getType(); + SolrKnnByteVectorQuery vectorQuery = (SolrKnnByteVectorQuery) + type.getKnnVectorQuery("vector_byte_encoding", "[2, 1, 3, 4]", 3, null, expectedThreshold); + KnnSearchStrategy.Hnsw strategy = (KnnSearchStrategy.Hnsw) vectorQuery.getStrategy(); + Integer threshold = strategy.filteredSearchThreshold(); - assertEquals(expectedThreshold, thresholdObj); + assertEquals(expectedThreshold, threshold); } finally { deleteCore(); } diff --git a/solr/core/src/test/org/apache/solr/search/neural/KnnQParserTest.java b/solr/core/src/test/org/apache/solr/search/neural/KnnQParserTest.java index 2d05e1e8eaf..93e68071218 100644 --- a/solr/core/src/test/org/apache/solr/search/neural/KnnQParserTest.java +++ b/solr/core/src/test/org/apache/solr/search/neural/KnnQParserTest.java @@ -24,6 +24,7 @@ import java.util.Collections; import java.util.List; import org.apache.lucene.search.Query; +import org.apache.lucene.search.knn.KnnSearchStrategy; import org.apache.solr.SolrTestCaseJ4; import org.apache.solr.common.SolrException; import org.apache.solr.common.SolrInputDocument; @@ -1013,17 +1014,11 @@ public void testFilteredSearchThresholdParsingFloatEncoding() throws Exception { KnnQParser qparser = new KnnQParser(vectorToSearch, localParams, params, req); try { - Query vectorQuery = qparser.parse(); + SolrKnnFloatVectorQuery vectorQuery = (SolrKnnFloatVectorQuery) qparser.parse(); + KnnSearchStrategy.Hnsw strategy = (KnnSearchStrategy.Hnsw) vectorQuery.getStrategy(); + Integer threshold = strategy.filteredSearchThreshold(); - Field strategy = vectorQuery.getClass().getSuperclass().getDeclaredField("searchStrategy"); - strategy.setAccessible(true); - Object strategyObj = strategy.get(vectorQuery); - - Field threshold = strategyObj.getClass().getDeclaredField("filteredSearchThreshold"); - threshold.setAccessible(true); - Integer thresholdObj = (Integer) threshold.get(strategyObj); - - assertEquals(expectedThreshold, thresholdObj); + assertEquals(expectedThreshold, threshold); } finally { req.close(); } @@ -1072,17 +1067,11 @@ public void testFilteredSearchThresholdParsingByteEncoding() throws Exception { KnnQParser qparser = new KnnQParser(vectorToSearch, localParams, params, req); try { - Query vectorQuery = qparser.parse(); - - Field strategy = vectorQuery.getClass().getSuperclass().getDeclaredField("searchStrategy"); - strategy.setAccessible(true); - Object strategyObj = strategy.get(vectorQuery); - - Field threshold = strategyObj.getClass().getDeclaredField("filteredSearchThreshold"); - threshold.setAccessible(true); - Integer thresholdObj = (Integer) threshold.get(strategyObj); + SolrKnnByteVectorQuery vectorQuery = (SolrKnnByteVectorQuery) qparser.parse(); + KnnSearchStrategy.Hnsw strategy = (KnnSearchStrategy.Hnsw) vectorQuery.getStrategy(); + Integer threshold = strategy.filteredSearchThreshold(); - assertEquals(expectedThreshold, thresholdObj); + assertEquals(expectedThreshold, threshold); } finally { req.close(); } From f793603e99cd14d09be330d7bfe2eeadd578355d Mon Sep 17 00:00:00 2001 From: Anna Ruggero Date: Mon, 22 Sep 2025 17:21:55 +0200 Subject: [PATCH 08/16] Removed empty row --- solr/core/build.gradle | 1 - 1 file changed, 1 deletion(-) diff --git a/solr/core/build.gradle b/solr/core/build.gradle index 6e55e657413..24fd610fc68 100644 --- a/solr/core/build.gradle +++ b/solr/core/build.gradle @@ -211,4 +211,3 @@ dependencies { testImplementation libs.apache.httpcomponents.httpclient testImplementation libs.apache.httpcomponents.httpcore } - From 1b653f4842b240e8e3a397a920155e954e7b888f Mon Sep 17 00:00:00 2001 From: Anna Ruggero Date: Mon, 22 Sep 2025 17:24:04 +0200 Subject: [PATCH 09/16] Gradle tidy --- .../solr/search/neural/SolrKnnByteVectorQuery.java | 3 ++- .../solr/search/neural/SolrKnnFloatVectorQuery.java | 3 ++- .../apache/solr/schema/DenseVectorFieldTest.java | 13 +++++++------ .../apache/solr/search/neural/KnnQParserTest.java | 2 -- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/solr/core/src/java/org/apache/solr/search/neural/SolrKnnByteVectorQuery.java b/solr/core/src/java/org/apache/solr/search/neural/SolrKnnByteVectorQuery.java index 16e40b90a24..d9bd5a1910a 100644 --- a/solr/core/src/java/org/apache/solr/search/neural/SolrKnnByteVectorQuery.java +++ b/solr/core/src/java/org/apache/solr/search/neural/SolrKnnByteVectorQuery.java @@ -5,7 +5,8 @@ import org.apache.lucene.search.knn.KnnSearchStrategy; public class SolrKnnByteVectorQuery extends KnnByteVectorQuery { - public SolrKnnByteVectorQuery(String field, byte[] target, int k, Query filter, KnnSearchStrategy searchStrategy) { + public SolrKnnByteVectorQuery( + String field, byte[] target, int k, Query filter, KnnSearchStrategy searchStrategy) { super(field, target, k, filter, searchStrategy); } diff --git a/solr/core/src/java/org/apache/solr/search/neural/SolrKnnFloatVectorQuery.java b/solr/core/src/java/org/apache/solr/search/neural/SolrKnnFloatVectorQuery.java index 93b804aefcf..47fee3f34cc 100644 --- a/solr/core/src/java/org/apache/solr/search/neural/SolrKnnFloatVectorQuery.java +++ b/solr/core/src/java/org/apache/solr/search/neural/SolrKnnFloatVectorQuery.java @@ -5,7 +5,8 @@ import org.apache.lucene.search.knn.KnnSearchStrategy; public class SolrKnnFloatVectorQuery extends KnnFloatVectorQuery { - public SolrKnnFloatVectorQuery(String field, float[] target, int k, Query filter, KnnSearchStrategy searchStrategy) { + public SolrKnnFloatVectorQuery( + String field, float[] target, int k, Query filter, KnnSearchStrategy searchStrategy) { super(field, target, k, filter, searchStrategy); } diff --git a/solr/core/src/test/org/apache/solr/schema/DenseVectorFieldTest.java b/solr/core/src/test/org/apache/solr/schema/DenseVectorFieldTest.java index fd3ee251cda..2579046c4b2 100644 --- a/solr/core/src/test/org/apache/solr/schema/DenseVectorFieldTest.java +++ b/solr/core/src/test/org/apache/solr/schema/DenseVectorFieldTest.java @@ -18,14 +18,12 @@ import static org.hamcrest.core.Is.is; -import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import org.apache.lucene.index.VectorEncoding; import org.apache.lucene.index.VectorSimilarityFunction; -import org.apache.lucene.search.Query; import org.apache.lucene.search.knn.KnnSearchStrategy; import org.apache.solr.common.SolrException; import org.apache.solr.common.SolrInputDocument; @@ -776,8 +774,9 @@ public void testFilteredSearchThresholdKnnFloatVectorQuery() throws Exception { SchemaField vectorField = schema.getField("vector"); assertNotNull(vectorField); DenseVectorField type = (DenseVectorField) vectorField.getType(); - SolrKnnFloatVectorQuery vectorQuery = (SolrKnnFloatVectorQuery) - type.getKnnVectorQuery("vector", "[2, 1, 3, 4]", 3, null, expectedThreshold); + SolrKnnFloatVectorQuery vectorQuery = + (SolrKnnFloatVectorQuery) + type.getKnnVectorQuery("vector", "[2, 1, 3, 4]", 3, null, expectedThreshold); KnnSearchStrategy.Hnsw strategy = (KnnSearchStrategy.Hnsw) vectorQuery.getStrategy(); Integer threshold = strategy.filteredSearchThreshold(); @@ -797,8 +796,10 @@ public void testFilteredSearchThresholdKnnByteVectorQuery() throws Exception { SchemaField vectorField = schema.getField("vector_byte_encoding"); assertNotNull(vectorField); DenseVectorField type = (DenseVectorField) vectorField.getType(); - SolrKnnByteVectorQuery vectorQuery = (SolrKnnByteVectorQuery) - type.getKnnVectorQuery("vector_byte_encoding", "[2, 1, 3, 4]", 3, null, expectedThreshold); + SolrKnnByteVectorQuery vectorQuery = + (SolrKnnByteVectorQuery) + type.getKnnVectorQuery( + "vector_byte_encoding", "[2, 1, 3, 4]", 3, null, expectedThreshold); KnnSearchStrategy.Hnsw strategy = (KnnSearchStrategy.Hnsw) vectorQuery.getStrategy(); Integer threshold = strategy.filteredSearchThreshold(); diff --git a/solr/core/src/test/org/apache/solr/search/neural/KnnQParserTest.java b/solr/core/src/test/org/apache/solr/search/neural/KnnQParserTest.java index 93e68071218..7febe360be0 100644 --- a/solr/core/src/test/org/apache/solr/search/neural/KnnQParserTest.java +++ b/solr/core/src/test/org/apache/solr/search/neural/KnnQParserTest.java @@ -18,12 +18,10 @@ import static org.apache.solr.search.neural.KnnQParser.DEFAULT_TOP_K; -import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; -import org.apache.lucene.search.Query; import org.apache.lucene.search.knn.KnnSearchStrategy; import org.apache.solr.SolrTestCaseJ4; import org.apache.solr.common.SolrException; From bcf5ca20b9abc4ba8dab7c5aa0d3dab259062de2 Mon Sep 17 00:00:00 2001 From: Anna Ruggero Date: Thu, 25 Sep 2025 17:47:23 +0200 Subject: [PATCH 10/16] Changed implementation looking at lucene 10.3 --- .../apache/solr/schema/DenseVectorField.java | 6 ++---- .../search/neural/SolrKnnByteVectorQuery.java | 16 ---------------- .../neural/SolrKnnFloatVectorQuery.java | 16 ---------------- .../solr/schema/DenseVectorFieldTest.java | 19 ++++++++----------- .../solr/search/neural/KnnQParserTest.java | 10 ++++++---- 5 files changed, 16 insertions(+), 51 deletions(-) delete mode 100644 solr/core/src/java/org/apache/solr/search/neural/SolrKnnByteVectorQuery.java delete mode 100644 solr/core/src/java/org/apache/solr/search/neural/SolrKnnFloatVectorQuery.java diff --git a/solr/core/src/java/org/apache/solr/schema/DenseVectorField.java b/solr/core/src/java/org/apache/solr/schema/DenseVectorField.java index 7abfc41955f..d319278eb1a 100644 --- a/solr/core/src/java/org/apache/solr/schema/DenseVectorField.java +++ b/solr/core/src/java/org/apache/solr/schema/DenseVectorField.java @@ -46,8 +46,6 @@ import org.apache.lucene.util.hnsw.HnswGraph; import org.apache.solr.common.SolrException; import org.apache.solr.search.QParser; -import org.apache.solr.search.neural.SolrKnnByteVectorQuery; -import org.apache.solr.search.neural.SolrKnnFloatVectorQuery; import org.apache.solr.uninverting.UninvertingReader; import org.apache.solr.util.vector.ByteDenseVectorParser; import org.apache.solr.util.vector.DenseVectorParser; @@ -387,7 +385,7 @@ public Query getKnnVectorQuery( case FLOAT32: if (filteredSearchThreshold != null) { KnnSearchStrategy knnSearchStrategy = new KnnSearchStrategy.Hnsw(filteredSearchThreshold); - return new SolrKnnFloatVectorQuery( + return new KnnFloatVectorQuery( fieldName, vectorBuilder.getFloatVector(), topK, filterQuery, knnSearchStrategy); } else { return new KnnFloatVectorQuery( @@ -396,7 +394,7 @@ public Query getKnnVectorQuery( case BYTE: if (filteredSearchThreshold != null) { KnnSearchStrategy knnSearchStrategy = new KnnSearchStrategy.Hnsw(filteredSearchThreshold); - return new SolrKnnByteVectorQuery( + return new KnnByteVectorQuery( fieldName, vectorBuilder.getByteVector(), topK, filterQuery, knnSearchStrategy); } else { return new KnnByteVectorQuery( diff --git a/solr/core/src/java/org/apache/solr/search/neural/SolrKnnByteVectorQuery.java b/solr/core/src/java/org/apache/solr/search/neural/SolrKnnByteVectorQuery.java deleted file mode 100644 index d9bd5a1910a..00000000000 --- a/solr/core/src/java/org/apache/solr/search/neural/SolrKnnByteVectorQuery.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.apache.solr.search.neural; - -import org.apache.lucene.search.KnnByteVectorQuery; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.knn.KnnSearchStrategy; - -public class SolrKnnByteVectorQuery extends KnnByteVectorQuery { - public SolrKnnByteVectorQuery( - String field, byte[] target, int k, Query filter, KnnSearchStrategy searchStrategy) { - super(field, target, k, filter, searchStrategy); - } - - public KnnSearchStrategy getStrategy() { - return searchStrategy; - } -} diff --git a/solr/core/src/java/org/apache/solr/search/neural/SolrKnnFloatVectorQuery.java b/solr/core/src/java/org/apache/solr/search/neural/SolrKnnFloatVectorQuery.java deleted file mode 100644 index 47fee3f34cc..00000000000 --- a/solr/core/src/java/org/apache/solr/search/neural/SolrKnnFloatVectorQuery.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.apache.solr.search.neural; - -import org.apache.lucene.search.KnnFloatVectorQuery; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.knn.KnnSearchStrategy; - -public class SolrKnnFloatVectorQuery extends KnnFloatVectorQuery { - public SolrKnnFloatVectorQuery( - String field, float[] target, int k, Query filter, KnnSearchStrategy searchStrategy) { - super(field, target, k, filter, searchStrategy); - } - - public KnnSearchStrategy getStrategy() { - return searchStrategy; - } -} diff --git a/solr/core/src/test/org/apache/solr/schema/DenseVectorFieldTest.java b/solr/core/src/test/org/apache/solr/schema/DenseVectorFieldTest.java index 2579046c4b2..7940d57bf8a 100644 --- a/solr/core/src/test/org/apache/solr/schema/DenseVectorFieldTest.java +++ b/solr/core/src/test/org/apache/solr/schema/DenseVectorFieldTest.java @@ -24,12 +24,12 @@ import java.util.Map; import org.apache.lucene.index.VectorEncoding; import org.apache.lucene.index.VectorSimilarityFunction; +import org.apache.lucene.search.KnnByteVectorQuery; +import org.apache.lucene.search.KnnFloatVectorQuery; import org.apache.lucene.search.knn.KnnSearchStrategy; import org.apache.solr.common.SolrException; import org.apache.solr.common.SolrInputDocument; import org.apache.solr.core.AbstractBadConfigTestBase; -import org.apache.solr.search.neural.SolrKnnByteVectorQuery; -import org.apache.solr.search.neural.SolrKnnFloatVectorQuery; import org.apache.solr.util.vector.DenseVectorParser; import org.junit.Before; import org.junit.Test; @@ -774,10 +774,9 @@ public void testFilteredSearchThresholdKnnFloatVectorQuery() throws Exception { SchemaField vectorField = schema.getField("vector"); assertNotNull(vectorField); DenseVectorField type = (DenseVectorField) vectorField.getType(); - SolrKnnFloatVectorQuery vectorQuery = - (SolrKnnFloatVectorQuery) - type.getKnnVectorQuery("vector", "[2, 1, 3, 4]", 3, null, expectedThreshold); - KnnSearchStrategy.Hnsw strategy = (KnnSearchStrategy.Hnsw) vectorQuery.getStrategy(); + KnnFloatVectorQuery vectorQuery = (KnnFloatVectorQuery) type.getKnnVectorQuery( + "vector", "[2, 1, 3, 4]", 3, null, expectedThreshold); + KnnSearchStrategy.Hnsw strategy = (KnnSearchStrategy.Hnsw) vectorQuery.getSearchStrategy(); Integer threshold = strategy.filteredSearchThreshold(); assertEquals(expectedThreshold, threshold); @@ -796,11 +795,9 @@ public void testFilteredSearchThresholdKnnByteVectorQuery() throws Exception { SchemaField vectorField = schema.getField("vector_byte_encoding"); assertNotNull(vectorField); DenseVectorField type = (DenseVectorField) vectorField.getType(); - SolrKnnByteVectorQuery vectorQuery = - (SolrKnnByteVectorQuery) - type.getKnnVectorQuery( - "vector_byte_encoding", "[2, 1, 3, 4]", 3, null, expectedThreshold); - KnnSearchStrategy.Hnsw strategy = (KnnSearchStrategy.Hnsw) vectorQuery.getStrategy(); + KnnByteVectorQuery vectorQuery = (KnnByteVectorQuery) type.getKnnVectorQuery( + "vector_byte_encoding", "[2, 1, 3, 4]", 3, null, expectedThreshold); + KnnSearchStrategy.Hnsw strategy = (KnnSearchStrategy.Hnsw) vectorQuery.getSearchStrategy(); Integer threshold = strategy.filteredSearchThreshold(); assertEquals(expectedThreshold, threshold); diff --git a/solr/core/src/test/org/apache/solr/search/neural/KnnQParserTest.java b/solr/core/src/test/org/apache/solr/search/neural/KnnQParserTest.java index 7febe360be0..d3cc4adb29f 100644 --- a/solr/core/src/test/org/apache/solr/search/neural/KnnQParserTest.java +++ b/solr/core/src/test/org/apache/solr/search/neural/KnnQParserTest.java @@ -22,6 +22,8 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import org.apache.lucene.search.KnnByteVectorQuery; +import org.apache.lucene.search.KnnFloatVectorQuery; import org.apache.lucene.search.knn.KnnSearchStrategy; import org.apache.solr.SolrTestCaseJ4; import org.apache.solr.common.SolrException; @@ -1012,8 +1014,8 @@ public void testFilteredSearchThresholdParsingFloatEncoding() throws Exception { KnnQParser qparser = new KnnQParser(vectorToSearch, localParams, params, req); try { - SolrKnnFloatVectorQuery vectorQuery = (SolrKnnFloatVectorQuery) qparser.parse(); - KnnSearchStrategy.Hnsw strategy = (KnnSearchStrategy.Hnsw) vectorQuery.getStrategy(); + KnnFloatVectorQuery vectorQuery = (KnnFloatVectorQuery) qparser.parse(); + KnnSearchStrategy.Hnsw strategy = (KnnSearchStrategy.Hnsw) vectorQuery.getSearchStrategy(); Integer threshold = strategy.filteredSearchThreshold(); assertEquals(expectedThreshold, threshold); @@ -1065,8 +1067,8 @@ public void testFilteredSearchThresholdParsingByteEncoding() throws Exception { KnnQParser qparser = new KnnQParser(vectorToSearch, localParams, params, req); try { - SolrKnnByteVectorQuery vectorQuery = (SolrKnnByteVectorQuery) qparser.parse(); - KnnSearchStrategy.Hnsw strategy = (KnnSearchStrategy.Hnsw) vectorQuery.getStrategy(); + KnnByteVectorQuery vectorQuery = (KnnByteVectorQuery) qparser.parse(); + KnnSearchStrategy.Hnsw strategy = (KnnSearchStrategy.Hnsw) vectorQuery.getSearchStrategy(); Integer threshold = strategy.filteredSearchThreshold(); assertEquals(expectedThreshold, threshold); From f12ea25ec02e8f099cb40e93376f5a39513f0af6 Mon Sep 17 00:00:00 2001 From: Anna Ruggero Date: Tue, 21 Oct 2025 12:19:59 +0200 Subject: [PATCH 11/16] Merged with main and gradlew tidy --- .../apache/solr/schema/DenseVectorField.java | 47 ++-- .../apache/solr/search/neural/KnnQParser.java | 2 +- .../solr/schema/DenseVectorFieldTest.java | 211 +++++++++++++++++- .../solr/search/neural/KnnQParserTest.java | 2 +- 4 files changed, 230 insertions(+), 32 deletions(-) diff --git a/solr/core/src/java/org/apache/solr/schema/DenseVectorField.java b/solr/core/src/java/org/apache/solr/schema/DenseVectorField.java index 52811ba83a5..0500d7fcbbe 100644 --- a/solr/core/src/java/org/apache/solr/schema/DenseVectorField.java +++ b/solr/core/src/java/org/apache/solr/schema/DenseVectorField.java @@ -386,28 +386,31 @@ public Query getKnnVectorQuery( DenseVectorParser vectorBuilder = getVectorBuilder(vectorToSearch, DenseVectorParser.BuilderPhase.QUERY); - final Query knnQuery = switch (vectorEncoding) { - case FLOAT32 -> { - if (filteredSearchThreshold != null) { - KnnSearchStrategy knnSearchStrategy = new KnnSearchStrategy.Hnsw(filteredSearchThreshold); - yield new KnnFloatVectorQuery( - fieldName, vectorBuilder.getFloatVector(), topK, filterQuery, knnSearchStrategy); - } else { - yield new KnnFloatVectorQuery( - fieldName, vectorBuilder.getFloatVector(), topK, filterQuery); - } - } - case BYTE -> { - if (filteredSearchThreshold != null) { - KnnSearchStrategy knnSearchStrategy = new KnnSearchStrategy.Hnsw(filteredSearchThreshold); - yield new KnnByteVectorQuery( - fieldName, vectorBuilder.getByteVector(), topK, filterQuery, knnSearchStrategy); - } else { - yield new KnnByteVectorQuery( - fieldName, vectorBuilder.getByteVector(), topK, filterQuery); - } - } - }; + final Query knnQuery = + switch (vectorEncoding) { + case FLOAT32 -> { + if (filteredSearchThreshold != null) { + KnnSearchStrategy knnSearchStrategy = + new KnnSearchStrategy.Hnsw(filteredSearchThreshold); + yield new KnnFloatVectorQuery( + fieldName, vectorBuilder.getFloatVector(), topK, filterQuery, knnSearchStrategy); + } else { + yield new KnnFloatVectorQuery( + fieldName, vectorBuilder.getFloatVector(), topK, filterQuery); + } + } + case BYTE -> { + if (filteredSearchThreshold != null) { + KnnSearchStrategy knnSearchStrategy = + new KnnSearchStrategy.Hnsw(filteredSearchThreshold); + yield new KnnByteVectorQuery( + fieldName, vectorBuilder.getByteVector(), topK, filterQuery, knnSearchStrategy); + } else { + yield new KnnByteVectorQuery( + fieldName, vectorBuilder.getByteVector(), topK, filterQuery); + } + } + }; final boolean seedEnabled = (seedQuery != null); final boolean earlyTerminationEnabled = diff --git a/solr/core/src/java/org/apache/solr/search/neural/KnnQParser.java b/solr/core/src/java/org/apache/solr/search/neural/KnnQParser.java index 19e0cb728b6..db355e0b84e 100644 --- a/solr/core/src/java/org/apache/solr/search/neural/KnnQParser.java +++ b/solr/core/src/java/org/apache/solr/search/neural/KnnQParser.java @@ -31,8 +31,8 @@ public class KnnQParser extends AbstractVectorQParserBase { // retrieve the top K results based on the distance similarity function protected static final String TOP_K = "topK"; protected static final int DEFAULT_TOP_K = 10; - protected static final String FILTERED_SEARCH_THRESHOLD = "filteredSearchThreshold"; protected static final String SEED_QUERY = "seedQuery"; + protected static final String FILTERED_SEARCH_THRESHOLD = "filteredSearchThreshold"; // parameters for PatienceKnnVectorQuery, a version of knn vector query that exits early when HNSW // queue saturates over a {@code #saturationThreshold} for more than {@code #patience} times. diff --git a/solr/core/src/test/org/apache/solr/schema/DenseVectorFieldTest.java b/solr/core/src/test/org/apache/solr/schema/DenseVectorFieldTest.java index 317f3e2e3eb..559ce4c2f5e 100644 --- a/solr/core/src/test/org/apache/solr/schema/DenseVectorFieldTest.java +++ b/solr/core/src/test/org/apache/solr/schema/DenseVectorFieldTest.java @@ -25,11 +25,15 @@ import java.util.Map; import org.apache.lucene.index.VectorEncoding; import org.apache.lucene.index.VectorSimilarityFunction; -import org.apache.solr.client.solrj.request.JavaBinUpdateRequestCodec; -import org.apache.solr.client.solrj.request.UpdateRequest; +import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.KnnByteVectorQuery; import org.apache.lucene.search.KnnFloatVectorQuery; +import org.apache.lucene.search.PatienceKnnVectorQuery; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.SeededKnnVectorQuery; import org.apache.lucene.search.knn.KnnSearchStrategy; +import org.apache.solr.client.solrj.request.JavaBinUpdateRequestCodec; +import org.apache.solr.client.solrj.request.UpdateRequest; import org.apache.solr.common.SolrException; import org.apache.solr.common.SolrInputDocument; import org.apache.solr.common.params.ModifiableSolrParams; @@ -38,6 +42,7 @@ import org.apache.solr.handler.loader.JavabinLoader; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.response.SolrQueryResponse; +import org.apache.solr.search.neural.KnnQParser; import org.apache.solr.update.CommitUpdateCommand; import org.apache.solr.update.processor.UpdateRequestProcessor; import org.apache.solr.update.processor.UpdateRequestProcessorChain; @@ -843,8 +848,89 @@ public void testIndexingViaJavaBin() throws Exception { } @Test - public void testFilteredSearchThresholdKnnFloatVectorQuery() throws Exception { + public void testFilteredSearchThresholdKnnFloatVectorQuery_shouldSetCustomThreshold() + throws Exception { + try { + Integer expectedThreshold = 30; + + initCore("solrconfig-basic.xml", "schema-densevector.xml"); + IndexSchema schema = h.getCore().getLatestSchema(); + SchemaField vectorField = schema.getField("vector"); + assertNotNull(vectorField); + DenseVectorField type = (DenseVectorField) vectorField.getType(); + KnnFloatVectorQuery vectorQuery = + (KnnFloatVectorQuery) + type.getKnnVectorQuery( + "vector", "[2, 1, 3, 4]", 3, null, null, null, expectedThreshold); + KnnSearchStrategy.Hnsw strategy = (KnnSearchStrategy.Hnsw) vectorQuery.getSearchStrategy(); + Integer threshold = strategy.filteredSearchThreshold(); + + assertEquals(expectedThreshold, threshold); + } finally { + deleteCore(); + } + } + + @Test + public void testFilteredSearchThresholdSeededKnnFloatVectorQuery_shouldSetCustomThreshold() + throws Exception { + try { + Query seedQuery = new BooleanQuery.Builder().build(); + Integer expectedThreshold = 30; + + initCore("solrconfig-basic.xml", "schema-densevector.xml"); + IndexSchema schema = h.getCore().getLatestSchema(); + SchemaField vectorField = schema.getField("vector"); + assertNotNull(vectorField); + DenseVectorField type = (DenseVectorField) vectorField.getType(); + SeededKnnVectorQuery vectorQuery = + (SeededKnnVectorQuery) + type.getKnnVectorQuery( + "vector", "[2, 1, 3, 4]", 3, null, seedQuery, null, expectedThreshold); + KnnSearchStrategy.Hnsw strategy = (KnnSearchStrategy.Hnsw) vectorQuery.getSearchStrategy(); + Integer threshold = strategy.filteredSearchThreshold(); + + assertEquals(expectedThreshold, threshold); + } finally { + deleteCore(); + } + } + + @Test + public void + testFilteredSearchThresholdEarlyTerminationKnnFloatVectorQuery_shouldSetCustomThreshold() + throws Exception { + try { + KnnQParser.EarlyTerminationParams earlyTermination = + new KnnQParser.EarlyTerminationParams(true, 0.995, 7); + Integer expectedThreshold = 30; + + initCore("solrconfig-basic.xml", "schema-densevector.xml"); + IndexSchema schema = h.getCore().getLatestSchema(); + SchemaField vectorField = schema.getField("vector"); + assertNotNull(vectorField); + DenseVectorField type = (DenseVectorField) vectorField.getType(); + PatienceKnnVectorQuery vectorQuery = + (PatienceKnnVectorQuery) + type.getKnnVectorQuery( + "vector", "[2, 1, 3, 4]", 3, null, null, earlyTermination, expectedThreshold); + KnnSearchStrategy.Hnsw strategy = (KnnSearchStrategy.Hnsw) vectorQuery.getSearchStrategy(); + Integer threshold = strategy.filteredSearchThreshold(); + + assertEquals(expectedThreshold, threshold); + } finally { + deleteCore(); + } + } + + @Test + public void + testFilteredSearchThresholdSeededAndEarlyTerminationKnnFloatVectorQuery_shouldSetCustomThreshold() + throws Exception { try { + Query seedQuery = new BooleanQuery.Builder().build(); + KnnQParser.EarlyTerminationParams earlyTermination = + new KnnQParser.EarlyTerminationParams(true, 0.995, 7); Integer expectedThreshold = 30; initCore("solrconfig-basic.xml", "schema-densevector.xml"); @@ -852,8 +938,104 @@ public void testFilteredSearchThresholdKnnFloatVectorQuery() throws Exception { SchemaField vectorField = schema.getField("vector"); assertNotNull(vectorField); DenseVectorField type = (DenseVectorField) vectorField.getType(); - KnnFloatVectorQuery vectorQuery = (KnnFloatVectorQuery) type.getKnnVectorQuery( - "vector", "[2, 1, 3, 4]", 3, null, expectedThreshold); + PatienceKnnVectorQuery vectorQuery = + (PatienceKnnVectorQuery) + type.getKnnVectorQuery( + "vector", + "[2, 1, 3, 4]", + 3, + null, + seedQuery, + earlyTermination, + expectedThreshold); + KnnSearchStrategy.Hnsw strategy = (KnnSearchStrategy.Hnsw) vectorQuery.getSearchStrategy(); + Integer threshold = strategy.filteredSearchThreshold(); + + assertEquals(expectedThreshold, threshold); + } finally { + deleteCore(); + } + } + + @Test + public void testFilteredSearchThresholdKnnByteVectorQuery_shouldSetCustomThreshold() + throws Exception { + try { + Integer expectedThreshold = 30; + + initCore("solrconfig-basic.xml", "schema-densevector.xml"); + IndexSchema schema = h.getCore().getLatestSchema(); + SchemaField vectorField = schema.getField("vector_byte_encoding"); + assertNotNull(vectorField); + DenseVectorField type = (DenseVectorField) vectorField.getType(); + KnnByteVectorQuery vectorQuery = + (KnnByteVectorQuery) + type.getKnnVectorQuery( + "vector_byte_encoding", "[2, 1, 3, 4]", 3, null, null, null, expectedThreshold); + KnnSearchStrategy.Hnsw strategy = (KnnSearchStrategy.Hnsw) vectorQuery.getSearchStrategy(); + Integer threshold = strategy.filteredSearchThreshold(); + + assertEquals(expectedThreshold, threshold); + } finally { + deleteCore(); + } + } + + @Test + public void testFilteredSearchThresholdSeededKnnByteVectorQuery_shouldSetCustomThreshold() + throws Exception { + try { + Query seedQuery = new BooleanQuery.Builder().build(); + Integer expectedThreshold = 30; + + initCore("solrconfig-basic.xml", "schema-densevector.xml"); + IndexSchema schema = h.getCore().getLatestSchema(); + SchemaField vectorField = schema.getField("vector_byte_encoding"); + assertNotNull(vectorField); + DenseVectorField type = (DenseVectorField) vectorField.getType(); + SeededKnnVectorQuery vectorQuery = + (SeededKnnVectorQuery) + type.getKnnVectorQuery( + "vector_byte_encoding", + "[2, 1, 3, 4]", + 3, + null, + seedQuery, + null, + expectedThreshold); + KnnSearchStrategy.Hnsw strategy = (KnnSearchStrategy.Hnsw) vectorQuery.getSearchStrategy(); + Integer threshold = strategy.filteredSearchThreshold(); + + assertEquals(expectedThreshold, threshold); + } finally { + deleteCore(); + } + } + + @Test + public void + testFilteredSearchThresholdEarlyTerminationKnnByteVectorQuery_shouldSetCustomThreshold() + throws Exception { + try { + KnnQParser.EarlyTerminationParams earlyTermination = + new KnnQParser.EarlyTerminationParams(true, 0.995, 7); + Integer expectedThreshold = 30; + + initCore("solrconfig-basic.xml", "schema-densevector.xml"); + IndexSchema schema = h.getCore().getLatestSchema(); + SchemaField vectorField = schema.getField("vector_byte_encoding"); + assertNotNull(vectorField); + DenseVectorField type = (DenseVectorField) vectorField.getType(); + PatienceKnnVectorQuery vectorQuery = + (PatienceKnnVectorQuery) + type.getKnnVectorQuery( + "vector_byte_encoding", + "[2, 1, 3, 4]", + 3, + null, + null, + earlyTermination, + expectedThreshold); KnnSearchStrategy.Hnsw strategy = (KnnSearchStrategy.Hnsw) vectorQuery.getSearchStrategy(); Integer threshold = strategy.filteredSearchThreshold(); @@ -864,8 +1046,13 @@ public void testFilteredSearchThresholdKnnFloatVectorQuery() throws Exception { } @Test - public void testFilteredSearchThresholdKnnByteVectorQuery() throws Exception { + public void + testFilteredSearchThresholdSeededAndEarlyTerminationKnnByteVectorQuery_shouldSetCustomThreshold() + throws Exception { try { + Query seedQuery = new BooleanQuery.Builder().build(); + KnnQParser.EarlyTerminationParams earlyTermination = + new KnnQParser.EarlyTerminationParams(true, 0.995, 7); Integer expectedThreshold = 30; initCore("solrconfig-basic.xml", "schema-densevector.xml"); @@ -873,8 +1060,16 @@ public void testFilteredSearchThresholdKnnByteVectorQuery() throws Exception { SchemaField vectorField = schema.getField("vector_byte_encoding"); assertNotNull(vectorField); DenseVectorField type = (DenseVectorField) vectorField.getType(); - KnnByteVectorQuery vectorQuery = (KnnByteVectorQuery) type.getKnnVectorQuery( - "vector_byte_encoding", "[2, 1, 3, 4]", 3, null, expectedThreshold); + PatienceKnnVectorQuery vectorQuery = + (PatienceKnnVectorQuery) + type.getKnnVectorQuery( + "vector_byte_encoding", + "[2, 1, 3, 4]", + 3, + null, + seedQuery, + earlyTermination, + expectedThreshold); KnnSearchStrategy.Hnsw strategy = (KnnSearchStrategy.Hnsw) vectorQuery.getSearchStrategy(); Integer threshold = strategy.filteredSearchThreshold(); diff --git a/solr/core/src/test/org/apache/solr/search/neural/KnnQParserTest.java b/solr/core/src/test/org/apache/solr/search/neural/KnnQParserTest.java index 286ee3b3bf2..d7324b9d9f1 100644 --- a/solr/core/src/test/org/apache/solr/search/neural/KnnQParserTest.java +++ b/solr/core/src/test/org/apache/solr/search/neural/KnnQParserTest.java @@ -22,10 +22,10 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Locale; import org.apache.lucene.search.KnnByteVectorQuery; import org.apache.lucene.search.KnnFloatVectorQuery; import org.apache.lucene.search.knn.KnnSearchStrategy; -import java.util.Locale; import org.apache.solr.SolrTestCaseJ4; import org.apache.solr.common.SolrException; import org.apache.solr.common.SolrInputDocument; From 848493c891e5e7c80cc05f59ead911bd7ed482eb Mon Sep 17 00:00:00 2001 From: Anna Ruggero Date: Tue, 21 Oct 2025 13:00:36 +0200 Subject: [PATCH 12/16] Added tests --- .../solr/search/neural/KnnQParserTest.java | 381 +++++++++++++++++- 1 file changed, 379 insertions(+), 2 deletions(-) diff --git a/solr/core/src/test/org/apache/solr/search/neural/KnnQParserTest.java b/solr/core/src/test/org/apache/solr/search/neural/KnnQParserTest.java index d7324b9d9f1..9e1a1a2fa6d 100644 --- a/solr/core/src/test/org/apache/solr/search/neural/KnnQParserTest.java +++ b/solr/core/src/test/org/apache/solr/search/neural/KnnQParserTest.java @@ -25,6 +25,8 @@ import java.util.Locale; import org.apache.lucene.search.KnnByteVectorQuery; import org.apache.lucene.search.KnnFloatVectorQuery; +import org.apache.lucene.search.PatienceKnnVectorQuery; +import org.apache.lucene.search.SeededKnnVectorQuery; import org.apache.lucene.search.knn.KnnSearchStrategy; import org.apache.solr.SolrTestCaseJ4; import org.apache.solr.common.SolrException; @@ -1319,7 +1321,7 @@ public void knnQueryWithKnnSeedQuery_shouldPerformSeededKnnVectorQuery() { } @Test - public void testFilteredSearchThresholdParsingFloatEncoding() throws Exception { + public void testFilteredSearchThresholdParsingFloatEncoding_shouldSetCustomThreshold() throws Exception { Integer expectedThreshold = 30; String vectorToSearch = "[1.0, 2.0, 3.0, 4.0]"; String topK = "4"; @@ -1372,7 +1374,194 @@ public void testFilteredSearchThresholdParsingFloatEncoding() throws Exception { } @Test - public void testFilteredSearchThresholdParsingByteEncoding() throws Exception { + public void testFilteredSearchThresholdSeededParsingFloatEncoding_shouldSetCustomThreshold() throws Exception { + Integer expectedThreshold = 30; + String vectorToSearch = "[1.0, 2.0, 3.0, 4.0]"; + String seedQuery = "id:(1 4 7 8 9)"; + String topK = "4"; + + SolrParams params = + params( + CommonParams.Q, + "{!knn f=" + + vectorField + + " topK=" + + topK + + " seedQuery=" + + seedQuery + + " filteredSearchThreshold=" + + expectedThreshold + + "}" + + vectorToSearch); + SolrParams localParams = + params( + "type", + "knn", + "f", + vectorField, + "topK", + topK, + "v", + vectorToSearch, + "seedQuery", + seedQuery, + "filteredSearchThreshold", + expectedThreshold.toString()); + SolrQueryRequest req = + req( + CommonParams.Q, + "{!knn f=" + + vectorField + + " topK=" + + topK + + " seedQuery=" + + seedQuery + + " filteredSearchThreshold=" + + expectedThreshold + + "}" + + vectorToSearch); + + KnnQParser qparser = new KnnQParser(vectorToSearch, localParams, params, req); + try { + SeededKnnVectorQuery vectorQuery = (SeededKnnVectorQuery) qparser.parse(); + KnnSearchStrategy.Hnsw strategy = (KnnSearchStrategy.Hnsw) vectorQuery.getSearchStrategy(); + Integer threshold = strategy.filteredSearchThreshold(); + + assertEquals(expectedThreshold, threshold); + } finally { + req.close(); + } + } + + @Test + public void testFilteredSearchThresholdEarlyTerminationParsingFloatEncoding_shouldSetCustomThreshold() throws Exception { + Integer expectedThreshold = 30; + String vectorToSearch = "[1.0, 2.0, 3.0, 4.0]"; + String earlyTermination = "true"; + String topK = "4"; + + SolrParams params = + params( + CommonParams.Q, + "{!knn f=" + + vectorField + + " topK=" + + topK + + " earlyTermination=" + + earlyTermination + + " filteredSearchThreshold=" + + expectedThreshold + + "}" + + vectorToSearch); + SolrParams localParams = + params( + "type", + "knn", + "f", + vectorField, + "topK", + topK, + "v", + vectorToSearch, + "earlyTermination", + earlyTermination, + "filteredSearchThreshold", + expectedThreshold.toString()); + SolrQueryRequest req = + req( + CommonParams.Q, + "{!knn f=" + + vectorField + + " topK=" + + topK + + " earlyTermination=" + + earlyTermination + + " filteredSearchThreshold=" + + expectedThreshold + + "}" + + vectorToSearch); + + KnnQParser qparser = new KnnQParser(vectorToSearch, localParams, params, req); + try { + PatienceKnnVectorQuery vectorQuery = (PatienceKnnVectorQuery) qparser.parse(); + KnnSearchStrategy.Hnsw strategy = (KnnSearchStrategy.Hnsw) vectorQuery.getSearchStrategy(); + Integer threshold = strategy.filteredSearchThreshold(); + + assertEquals(expectedThreshold, threshold); + } finally { + req.close(); + } + } + + @Test + public void testFilteredSearchThresholdSeededAndEarlyTerminationParsingFloatEncoding_shouldSetCustomThreshold() throws Exception { + Integer expectedThreshold = 30; + String vectorToSearch = "[1.0, 2.0, 3.0, 4.0]"; + String seedQuery = "id:(1 4 7 8 9)"; + String earlyTermination = "true"; + String topK = "4"; + + SolrParams params = + params( + CommonParams.Q, + "{!knn f=" + + vectorField + + " topK=" + + topK + + " seedQuery=" + + seedQuery + + " earlyTermination=" + + earlyTermination + + " filteredSearchThreshold=" + + expectedThreshold + + "}" + + vectorToSearch); + SolrParams localParams = + params( + "type", + "knn", + "f", + vectorField, + "topK", + topK, + "v", + vectorToSearch, + "seedQuery", + seedQuery, + "earlyTermination", + earlyTermination, + "filteredSearchThreshold", + expectedThreshold.toString()); + SolrQueryRequest req = + req( + CommonParams.Q, + "{!knn f=" + + vectorField + + " topK=" + + topK + + " seedQuery=" + + seedQuery + + " earlyTermination=" + + earlyTermination + + " filteredSearchThreshold=" + + expectedThreshold + + "}" + + vectorToSearch); + + KnnQParser qparser = new KnnQParser(vectorToSearch, localParams, params, req); + try { + PatienceKnnVectorQuery vectorQuery = (PatienceKnnVectorQuery) qparser.parse(); + KnnSearchStrategy.Hnsw strategy = (KnnSearchStrategy.Hnsw) vectorQuery.getSearchStrategy(); + Integer threshold = strategy.filteredSearchThreshold(); + + assertEquals(expectedThreshold, threshold); + } finally { + req.close(); + } + } + + @Test + public void testFilteredSearchThresholdParsingByteEncoding_shouldSetCustomThreshold() throws Exception { Integer expectedThreshold = 30; String vectorToSearch = "[1, 2, 3, 4]"; String topK = "4"; @@ -1423,4 +1612,192 @@ public void testFilteredSearchThresholdParsingByteEncoding() throws Exception { req.close(); } } + + @Test + public void testFilteredSearchThresholdSeededParsingByteEncoding_shouldSetCustomThreshold() throws Exception { + Integer expectedThreshold = 30; + String vectorToSearch = "[1, 2, 3, 4]"; + String seedQuery = "id:(1 4 7 8 9)"; + String topK = "4"; + + SolrParams params = + params( + CommonParams.Q, + "{!knn f=" + + vectorFieldByteEncoding + + " topK=" + + topK + + " seedQuery=" + + seedQuery + + " filteredSearchThreshold=" + + expectedThreshold + + "}" + + vectorToSearch); + SolrParams localParams = + params( + "type", + "knn", + "f", + vectorFieldByteEncoding, + "topK", + topK, + "v", + vectorToSearch, + "seedQuery", + seedQuery, + "filteredSearchThreshold", + expectedThreshold.toString()); + SolrQueryRequest req = + req( + CommonParams.Q, + "{!knn f=" + + vectorFieldByteEncoding + + " topK=" + + topK + + " seedQuery=" + + seedQuery + + " filteredSearchThreshold=" + + expectedThreshold + + "}" + + vectorToSearch); + + KnnQParser qparser = new KnnQParser(vectorToSearch, localParams, params, req); + try { + SeededKnnVectorQuery vectorQuery = (SeededKnnVectorQuery) qparser.parse(); + KnnSearchStrategy.Hnsw strategy = (KnnSearchStrategy.Hnsw) vectorQuery.getSearchStrategy(); + Integer threshold = strategy.filteredSearchThreshold(); + + assertEquals(expectedThreshold, threshold); + } finally { + req.close(); + } + } + + + @Test + public void testFilteredSearchThresholdEarlyTerminationParsingByteEncoding_shouldSetCustomThreshold() throws Exception { + Integer expectedThreshold = 30; + String vectorToSearch = "[1, 2, 3, 4]"; + String earlyTermination = "true"; + String topK = "4"; + + SolrParams params = + params( + CommonParams.Q, + "{!knn f=" + + vectorFieldByteEncoding + + " topK=" + + topK + + " earlyTermination=" + + earlyTermination + + " filteredSearchThreshold=" + + expectedThreshold + + "}" + + vectorToSearch); + SolrParams localParams = + params( + "type", + "knn", + "f", + vectorFieldByteEncoding, + "topK", + topK, + "v", + vectorToSearch, + "earlyTermination", + earlyTermination, + "filteredSearchThreshold", + expectedThreshold.toString()); + SolrQueryRequest req = + req( + CommonParams.Q, + "{!knn f=" + + vectorFieldByteEncoding + + " topK=" + + topK + + " earlyTermination=" + + earlyTermination + + " filteredSearchThreshold=" + + expectedThreshold + + "}" + + vectorToSearch); + + KnnQParser qparser = new KnnQParser(vectorToSearch, localParams, params, req); + try { + PatienceKnnVectorQuery vectorQuery = (PatienceKnnVectorQuery) qparser.parse(); + KnnSearchStrategy.Hnsw strategy = (KnnSearchStrategy.Hnsw) vectorQuery.getSearchStrategy(); + Integer threshold = strategy.filteredSearchThreshold(); + + assertEquals(expectedThreshold, threshold); + } finally { + req.close(); + } + } + + @Test + public void testFilteredSearchThresholdSeededAndEarlyTerminationParsingByteEncoding_shouldSetCustomThreshold() throws Exception { + Integer expectedThreshold = 30; + String vectorToSearch = "[1, 2, 3, 4]"; + String seedQuery = "id:(1 4 7 8 9)"; + String earlyTermination = "true"; + String topK = "4"; + + SolrParams params = + params( + CommonParams.Q, + "{!knn f=" + + vectorFieldByteEncoding + + " topK=" + + topK + + " seedQuery=" + + seedQuery + + " earlyTermination=" + + earlyTermination + + " filteredSearchThreshold=" + + expectedThreshold + + "}" + + vectorToSearch); + SolrParams localParams = + params( + "type", + "knn", + "f", + vectorFieldByteEncoding, + "topK", + topK, + "v", + vectorToSearch, + "seedQuery", + seedQuery, + "earlyTermination", + earlyTermination, + "filteredSearchThreshold", + expectedThreshold.toString()); + SolrQueryRequest req = + req( + CommonParams.Q, + "{!knn f=" + + vectorFieldByteEncoding + + " topK=" + + topK + + " seedQuery=" + + seedQuery + + " earlyTermination=" + + earlyTermination + + " filteredSearchThreshold=" + + expectedThreshold + + "}" + + vectorToSearch); + + KnnQParser qparser = new KnnQParser(vectorToSearch, localParams, params, req); + try { + PatienceKnnVectorQuery vectorQuery = (PatienceKnnVectorQuery) qparser.parse(); + KnnSearchStrategy.Hnsw strategy = (KnnSearchStrategy.Hnsw) vectorQuery.getSearchStrategy(); + Integer threshold = strategy.filteredSearchThreshold(); + + assertEquals(expectedThreshold, threshold); + } finally { + req.close(); + } + } } From a1fec046b9487255716e00de103ffebdc401efdd Mon Sep 17 00:00:00 2001 From: Anna Ruggero Date: Tue, 21 Oct 2025 13:05:03 +0200 Subject: [PATCH 13/16] Gradlew tidy --- .../solr/search/neural/KnnQParserTest.java | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/solr/core/src/test/org/apache/solr/search/neural/KnnQParserTest.java b/solr/core/src/test/org/apache/solr/search/neural/KnnQParserTest.java index 9e1a1a2fa6d..8e855d52edc 100644 --- a/solr/core/src/test/org/apache/solr/search/neural/KnnQParserTest.java +++ b/solr/core/src/test/org/apache/solr/search/neural/KnnQParserTest.java @@ -1321,7 +1321,8 @@ public void knnQueryWithKnnSeedQuery_shouldPerformSeededKnnVectorQuery() { } @Test - public void testFilteredSearchThresholdParsingFloatEncoding_shouldSetCustomThreshold() throws Exception { + public void testFilteredSearchThresholdParsingFloatEncoding_shouldSetCustomThreshold() + throws Exception { Integer expectedThreshold = 30; String vectorToSearch = "[1.0, 2.0, 3.0, 4.0]"; String topK = "4"; @@ -1374,7 +1375,8 @@ public void testFilteredSearchThresholdParsingFloatEncoding_shouldSetCustomThres } @Test - public void testFilteredSearchThresholdSeededParsingFloatEncoding_shouldSetCustomThreshold() throws Exception { + public void testFilteredSearchThresholdSeededParsingFloatEncoding_shouldSetCustomThreshold() + throws Exception { Integer expectedThreshold = 30; String vectorToSearch = "[1.0, 2.0, 3.0, 4.0]"; String seedQuery = "id:(1 4 7 8 9)"; @@ -1434,7 +1436,9 @@ public void testFilteredSearchThresholdSeededParsingFloatEncoding_shouldSetCusto } @Test - public void testFilteredSearchThresholdEarlyTerminationParsingFloatEncoding_shouldSetCustomThreshold() throws Exception { + public void + testFilteredSearchThresholdEarlyTerminationParsingFloatEncoding_shouldSetCustomThreshold() + throws Exception { Integer expectedThreshold = 30; String vectorToSearch = "[1.0, 2.0, 3.0, 4.0]"; String earlyTermination = "true"; @@ -1494,7 +1498,9 @@ public void testFilteredSearchThresholdEarlyTerminationParsingFloatEncoding_shou } @Test - public void testFilteredSearchThresholdSeededAndEarlyTerminationParsingFloatEncoding_shouldSetCustomThreshold() throws Exception { + public void + testFilteredSearchThresholdSeededAndEarlyTerminationParsingFloatEncoding_shouldSetCustomThreshold() + throws Exception { Integer expectedThreshold = 30; String vectorToSearch = "[1.0, 2.0, 3.0, 4.0]"; String seedQuery = "id:(1 4 7 8 9)"; @@ -1561,7 +1567,8 @@ public void testFilteredSearchThresholdSeededAndEarlyTerminationParsingFloatEnco } @Test - public void testFilteredSearchThresholdParsingByteEncoding_shouldSetCustomThreshold() throws Exception { + public void testFilteredSearchThresholdParsingByteEncoding_shouldSetCustomThreshold() + throws Exception { Integer expectedThreshold = 30; String vectorToSearch = "[1, 2, 3, 4]"; String topK = "4"; @@ -1614,7 +1621,8 @@ public void testFilteredSearchThresholdParsingByteEncoding_shouldSetCustomThresh } @Test - public void testFilteredSearchThresholdSeededParsingByteEncoding_shouldSetCustomThreshold() throws Exception { + public void testFilteredSearchThresholdSeededParsingByteEncoding_shouldSetCustomThreshold() + throws Exception { Integer expectedThreshold = 30; String vectorToSearch = "[1, 2, 3, 4]"; String seedQuery = "id:(1 4 7 8 9)"; @@ -1673,9 +1681,10 @@ public void testFilteredSearchThresholdSeededParsingByteEncoding_shouldSetCustom } } - @Test - public void testFilteredSearchThresholdEarlyTerminationParsingByteEncoding_shouldSetCustomThreshold() throws Exception { + public void + testFilteredSearchThresholdEarlyTerminationParsingByteEncoding_shouldSetCustomThreshold() + throws Exception { Integer expectedThreshold = 30; String vectorToSearch = "[1, 2, 3, 4]"; String earlyTermination = "true"; @@ -1735,7 +1744,9 @@ public void testFilteredSearchThresholdEarlyTerminationParsingByteEncoding_shoul } @Test - public void testFilteredSearchThresholdSeededAndEarlyTerminationParsingByteEncoding_shouldSetCustomThreshold() throws Exception { + public void + testFilteredSearchThresholdSeededAndEarlyTerminationParsingByteEncoding_shouldSetCustomThreshold() + throws Exception { Integer expectedThreshold = 30; String vectorToSearch = "[1, 2, 3, 4]"; String seedQuery = "id:(1 4 7 8 9)"; From bddbee293f438c44b13179a683987656b493ca86 Mon Sep 17 00:00:00 2001 From: Anna Ruggero Date: Tue, 21 Oct 2025 16:31:41 +0200 Subject: [PATCH 14/16] Fixed tests names and reference guide --- .../solr/schema/DenseVectorFieldTest.java | 64 +++++++-- .../solr/search/neural/KnnQParserTest.java | 124 ++++++++++++++++-- .../pages/dense-vector-search.adoc | 22 ++++ 3 files changed, 194 insertions(+), 16 deletions(-) diff --git a/solr/core/src/test/org/apache/solr/schema/DenseVectorFieldTest.java b/solr/core/src/test/org/apache/solr/schema/DenseVectorFieldTest.java index 559ce4c2f5e..cb4876bd013 100644 --- a/solr/core/src/test/org/apache/solr/schema/DenseVectorFieldTest.java +++ b/solr/core/src/test/org/apache/solr/schema/DenseVectorFieldTest.java @@ -848,7 +848,31 @@ public void testIndexingViaJavaBin() throws Exception { } @Test - public void testFilteredSearchThresholdKnnFloatVectorQuery_shouldSetCustomThreshold() + public void testFilteredSearchThreshold_floatNoThresholdInInput_shouldSetDefaultThreshold() + throws Exception { + try { + Integer expectedThreshold = KnnSearchStrategy.DEFAULT_FILTERED_SEARCH_THRESHOLD; + + initCore("solrconfig-basic.xml", "schema-densevector.xml"); + IndexSchema schema = h.getCore().getLatestSchema(); + SchemaField vectorField = schema.getField("vector"); + assertNotNull(vectorField); + DenseVectorField type = (DenseVectorField) vectorField.getType(); + KnnFloatVectorQuery vectorQuery = + (KnnFloatVectorQuery) + type.getKnnVectorQuery( + "vector", "[2, 1, 3, 4]", 3, null, null, null, null); + KnnSearchStrategy.Hnsw strategy = (KnnSearchStrategy.Hnsw) vectorQuery.getSearchStrategy(); + Integer threshold = strategy.filteredSearchThreshold(); + + assertEquals(expectedThreshold, threshold); + } finally { + deleteCore(); + } + } + + @Test + public void testFilteredSearchThreshold_floatThresholdInInput_shouldSetCustomThreshold() throws Exception { try { Integer expectedThreshold = 30; @@ -872,7 +896,7 @@ public void testFilteredSearchThresholdKnnFloatVectorQuery_shouldSetCustomThresh } @Test - public void testFilteredSearchThresholdSeededKnnFloatVectorQuery_shouldSetCustomThreshold() + public void testFilteredSearchThreshold_seededFloatThresholdInInput_shouldSetCustomThreshold() throws Exception { try { Query seedQuery = new BooleanQuery.Builder().build(); @@ -898,7 +922,7 @@ public void testFilteredSearchThresholdSeededKnnFloatVectorQuery_shouldSetCustom @Test public void - testFilteredSearchThresholdEarlyTerminationKnnFloatVectorQuery_shouldSetCustomThreshold() + testFilteredSearchThreshold_earlyTerminationFloatThresholdInInput_shouldSetCustomThreshold() throws Exception { try { KnnQParser.EarlyTerminationParams earlyTermination = @@ -925,7 +949,7 @@ public void testFilteredSearchThresholdSeededKnnFloatVectorQuery_shouldSetCustom @Test public void - testFilteredSearchThresholdSeededAndEarlyTerminationKnnFloatVectorQuery_shouldSetCustomThreshold() + testFilteredSearchThreshold_seededAndEarlyTerminationFloatThresholdInInput_shouldSetCustomThreshold() throws Exception { try { Query seedQuery = new BooleanQuery.Builder().build(); @@ -958,7 +982,31 @@ public void testFilteredSearchThresholdSeededKnnFloatVectorQuery_shouldSetCustom } @Test - public void testFilteredSearchThresholdKnnByteVectorQuery_shouldSetCustomThreshold() + public void testFilteredSearchThreshold_byteNoThresholdInInput_shouldSetDefaultThreshold() + throws Exception { + try { + Integer expectedThreshold = KnnSearchStrategy.DEFAULT_FILTERED_SEARCH_THRESHOLD; + + initCore("solrconfig-basic.xml", "schema-densevector.xml"); + IndexSchema schema = h.getCore().getLatestSchema(); + SchemaField vectorField = schema.getField("vector_byte_encoding"); + assertNotNull(vectorField); + DenseVectorField type = (DenseVectorField) vectorField.getType(); + KnnByteVectorQuery vectorQuery = + (KnnByteVectorQuery) + type.getKnnVectorQuery( + "vector_byte_encoding", "[2, 1, 3, 4]", 3, null, null, null, null); + KnnSearchStrategy.Hnsw strategy = (KnnSearchStrategy.Hnsw) vectorQuery.getSearchStrategy(); + Integer threshold = strategy.filteredSearchThreshold(); + + assertEquals(expectedThreshold, threshold); + } finally { + deleteCore(); + } + } + + @Test + public void testFilteredSearchThreshold_byteThresholdInInput_shouldSetCustomThreshold() throws Exception { try { Integer expectedThreshold = 30; @@ -982,7 +1030,7 @@ public void testFilteredSearchThresholdKnnByteVectorQuery_shouldSetCustomThresho } @Test - public void testFilteredSearchThresholdSeededKnnByteVectorQuery_shouldSetCustomThreshold() + public void testFilteredSearchThreshold_seededByteThresholdInInput_shouldSetCustomThreshold() throws Exception { try { Query seedQuery = new BooleanQuery.Builder().build(); @@ -1014,7 +1062,7 @@ public void testFilteredSearchThresholdSeededKnnByteVectorQuery_shouldSetCustomT @Test public void - testFilteredSearchThresholdEarlyTerminationKnnByteVectorQuery_shouldSetCustomThreshold() + testFilteredSearchThreshold_earlyTerminationByteThresholdInInput_shouldSetCustomThreshold() throws Exception { try { KnnQParser.EarlyTerminationParams earlyTermination = @@ -1047,7 +1095,7 @@ public void testFilteredSearchThresholdSeededKnnByteVectorQuery_shouldSetCustomT @Test public void - testFilteredSearchThresholdSeededAndEarlyTerminationKnnByteVectorQuery_shouldSetCustomThreshold() + testFilteredSearchThreshold_seededAndEarlyTerminationByteThresholdInInput_shouldSetCustomThreshold() throws Exception { try { Query seedQuery = new BooleanQuery.Builder().build(); diff --git a/solr/core/src/test/org/apache/solr/search/neural/KnnQParserTest.java b/solr/core/src/test/org/apache/solr/search/neural/KnnQParserTest.java index 8e855d52edc..398b9215044 100644 --- a/solr/core/src/test/org/apache/solr/search/neural/KnnQParserTest.java +++ b/solr/core/src/test/org/apache/solr/search/neural/KnnQParserTest.java @@ -1321,7 +1321,61 @@ public void knnQueryWithKnnSeedQuery_shouldPerformSeededKnnVectorQuery() { } @Test - public void testFilteredSearchThresholdParsingFloatEncoding_shouldSetCustomThreshold() + public void testFilteredSearchThreshold_parsingFloatEncoding_shouldSetDefaultThreshold() + throws Exception { + Integer expectedThreshold = 0; + String vectorToSearch = "[1.0, 2.0, 3.0, 4.0]"; + String topK = "4"; + + SolrParams params = + params( + CommonParams.Q, + "{!knn f=" + + vectorField + + " topK=" + + topK + + " filteredSearchThreshold=" + + expectedThreshold + + "}" + + vectorToSearch); + SolrParams localParams = + params( + "type", + "knn", + "f", + vectorField, + "topK", + topK, + "v", + vectorToSearch, + "filteredSearchThreshold", + expectedThreshold.toString()); + SolrQueryRequest req = + req( + CommonParams.Q, + "{!knn f=" + + vectorField + + " topK=" + + topK + + " filteredSearchThreshold=" + + expectedThreshold + + "}" + + vectorToSearch); + + KnnQParser qparser = new KnnQParser(vectorToSearch, localParams, params, req); + try { + KnnFloatVectorQuery vectorQuery = (KnnFloatVectorQuery) qparser.parse(); + KnnSearchStrategy.Hnsw strategy = (KnnSearchStrategy.Hnsw) vectorQuery.getSearchStrategy(); + Integer threshold = strategy.filteredSearchThreshold(); + + assertEquals(expectedThreshold, threshold); + } finally { + req.close(); + } + } + + @Test + public void testFilteredSearchThreshold_parsingFloatEncoding_shouldSetCustomThreshold() throws Exception { Integer expectedThreshold = 30; String vectorToSearch = "[1.0, 2.0, 3.0, 4.0]"; @@ -1375,7 +1429,7 @@ public void testFilteredSearchThresholdParsingFloatEncoding_shouldSetCustomThres } @Test - public void testFilteredSearchThresholdSeededParsingFloatEncoding_shouldSetCustomThreshold() + public void testFilteredSearchThreshold_seededParsingFloatEncoding_shouldSetCustomThreshold() throws Exception { Integer expectedThreshold = 30; String vectorToSearch = "[1.0, 2.0, 3.0, 4.0]"; @@ -1437,7 +1491,7 @@ public void testFilteredSearchThresholdSeededParsingFloatEncoding_shouldSetCusto @Test public void - testFilteredSearchThresholdEarlyTerminationParsingFloatEncoding_shouldSetCustomThreshold() + testFilteredSearchThreshold_earlyTerminationParsingFloatEncoding_shouldSetCustomThreshold() throws Exception { Integer expectedThreshold = 30; String vectorToSearch = "[1.0, 2.0, 3.0, 4.0]"; @@ -1499,7 +1553,7 @@ public void testFilteredSearchThresholdSeededParsingFloatEncoding_shouldSetCusto @Test public void - testFilteredSearchThresholdSeededAndEarlyTerminationParsingFloatEncoding_shouldSetCustomThreshold() + testFilteredSearchThreshold_seededAndEarlyTerminationParsingFloatEncoding_shouldSetCustomThreshold() throws Exception { Integer expectedThreshold = 30; String vectorToSearch = "[1.0, 2.0, 3.0, 4.0]"; @@ -1567,7 +1621,61 @@ public void testFilteredSearchThresholdSeededParsingFloatEncoding_shouldSetCusto } @Test - public void testFilteredSearchThresholdParsingByteEncoding_shouldSetCustomThreshold() + public void testFilteredSearchThreshold_parsingByteEncoding_shouldSetDefaultThreshold() + throws Exception { + Integer expectedThreshold = 0; + String vectorToSearch = "[1, 2, 3, 4]"; + String topK = "4"; + + SolrParams params = + params( + CommonParams.Q, + "{!knn f=" + + vectorFieldByteEncoding + + " topK=" + + topK + + " filteredSearchThreshold=" + + expectedThreshold + + "}" + + vectorToSearch); + SolrParams localParams = + params( + "type", + "knn", + "f", + vectorFieldByteEncoding, + "topK", + topK, + "v", + vectorToSearch, + "filteredSearchThreshold", + expectedThreshold.toString()); + SolrQueryRequest req = + req( + CommonParams.Q, + "{!knn f=" + + vectorFieldByteEncoding + + " topK=" + + topK + + " filteredSearchThreshold=" + + expectedThreshold + + "}" + + vectorToSearch); + + KnnQParser qparser = new KnnQParser(vectorToSearch, localParams, params, req); + try { + KnnByteVectorQuery vectorQuery = (KnnByteVectorQuery) qparser.parse(); + KnnSearchStrategy.Hnsw strategy = (KnnSearchStrategy.Hnsw) vectorQuery.getSearchStrategy(); + Integer threshold = strategy.filteredSearchThreshold(); + + assertEquals(expectedThreshold, threshold); + } finally { + req.close(); + } + } + + @Test + public void testFilteredSearchThreshold_parsingByteEncoding_shouldSetCustomThreshold() throws Exception { Integer expectedThreshold = 30; String vectorToSearch = "[1, 2, 3, 4]"; @@ -1621,7 +1729,7 @@ public void testFilteredSearchThresholdParsingByteEncoding_shouldSetCustomThresh } @Test - public void testFilteredSearchThresholdSeededParsingByteEncoding_shouldSetCustomThreshold() + public void testFilteredSearchThreshold_seededParsingByteEncoding_shouldSetCustomThreshold() throws Exception { Integer expectedThreshold = 30; String vectorToSearch = "[1, 2, 3, 4]"; @@ -1683,7 +1791,7 @@ public void testFilteredSearchThresholdSeededParsingByteEncoding_shouldSetCustom @Test public void - testFilteredSearchThresholdEarlyTerminationParsingByteEncoding_shouldSetCustomThreshold() + testFilteredSearchThreshold_earlyTerminationParsingByteEncoding_shouldSetCustomThreshold() throws Exception { Integer expectedThreshold = 30; String vectorToSearch = "[1, 2, 3, 4]"; @@ -1745,7 +1853,7 @@ public void testFilteredSearchThresholdSeededParsingByteEncoding_shouldSetCustom @Test public void - testFilteredSearchThresholdSeededAndEarlyTerminationParsingByteEncoding_shouldSetCustomThreshold() + testFilteredSearchThreshold_seededAndEarlyTerminationParsingByteEncoding_shouldSetCustomThreshold() throws Exception { Integer expectedThreshold = 30; String vectorToSearch = "[1, 2, 3, 4]"; diff --git a/solr/solr-ref-guide/modules/query-guide/pages/dense-vector-search.adoc b/solr/solr-ref-guide/modules/query-guide/pages/dense-vector-search.adoc index 82ae6648a05..aa1c316f51d 100644 --- a/solr/solr-ref-guide/modules/query-guide/pages/dense-vector-search.adoc +++ b/solr/solr-ref-guide/modules/query-guide/pages/dense-vector-search.adoc @@ -477,6 +477,28 @@ Here is an example of a `knn` search using a `seedQuery`: The search results retrieved are the k=10 nearest documents to the vector in input `[1.0, 2.0, 3.0, 4.0]`. Documents matching the query `id:(1 4 10)` are used as entry points for the ANN search. If no documents match the seed, Solr falls back to a regular knn search without seeding, starting instead from random entry points. +`filteredSearchThreshold`:: ++ +[%autowidth,frame=none] +|=== +|Optional |Default: {lucene-javadocs}/core/constant-values.html#org.apache.lucene.search.knn.KnnSearchStrategy.DEFAULT_FILTERED_SEARCH_THRESHOLD[Lucene default] |An integer value from 0 to 100 +|=== ++ +ACORN is an algorithm designed to make hybrid searches consisting of a filter and a vector search more efficient. +This approach tackles both the performance limitations of pre- and post- filtering. +It modifies the construction of the HNSW graph and the search on it. Based on https://arxiv.org/abs/2403.04871[ACORN: Performant and Predicate-Agnostic Search Over Vector Embeddings and Structured Data (2024)]. ++ +Solr relies on Lucene's implementation of the `filteredSearchThreshold` in the {lucene-javadocs}/core/org/apache/lucene/search/knn/KnnSearchStrategy.html[KnnSearchStrategy]. ++ +A suggested value is 60 based on a benchmark you can read more about in this Github https://github.com/apache/lucene/pull/14160#issue-2805145799[comment]. ++ +The `filteredSearchThreshold` regulates this behavior. If the percentage of documents that satisfies the filter is less than the threshold ACORN will be used. + +Here is an example of a `knn` search using a `filteredSearchThreshold`: + +[source,text] +?q={!knn f=vector topK=10 filteredSearchThreshold=60}[1.0, 2.0, 3.0, 4.0] + === knn_text_to_vector Query Parser The `knn_text_to_vector` query parser encode a textual query to a vector using a dedicated Large Language Model(fine tuned for the task of encoding text to vector for sentence similarity) and matches k-nearest neighbours documents to such query vector. From 988e098187741154ede734484fab063212c7cd98 Mon Sep 17 00:00:00 2001 From: Anna Ruggero Date: Tue, 21 Oct 2025 16:32:46 +0200 Subject: [PATCH 15/16] Gradlew tidy --- .../src/test/org/apache/solr/schema/DenseVectorFieldTest.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/solr/core/src/test/org/apache/solr/schema/DenseVectorFieldTest.java b/solr/core/src/test/org/apache/solr/schema/DenseVectorFieldTest.java index cb4876bd013..d3790dbbefd 100644 --- a/solr/core/src/test/org/apache/solr/schema/DenseVectorFieldTest.java +++ b/solr/core/src/test/org/apache/solr/schema/DenseVectorFieldTest.java @@ -860,8 +860,7 @@ public void testFilteredSearchThreshold_floatNoThresholdInInput_shouldSetDefault DenseVectorField type = (DenseVectorField) vectorField.getType(); KnnFloatVectorQuery vectorQuery = (KnnFloatVectorQuery) - type.getKnnVectorQuery( - "vector", "[2, 1, 3, 4]", 3, null, null, null, null); + type.getKnnVectorQuery("vector", "[2, 1, 3, 4]", 3, null, null, null, null); KnnSearchStrategy.Hnsw strategy = (KnnSearchStrategy.Hnsw) vectorQuery.getSearchStrategy(); Integer threshold = strategy.filteredSearchThreshold(); From 702bada872060a0a0012cb3e944477de5206914c Mon Sep 17 00:00:00 2001 From: Anna Ruggero Date: Tue, 21 Oct 2025 16:40:03 +0200 Subject: [PATCH 16/16] Adapted changes.txt --- solr/CHANGES.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt index 32568729f53..93254556845 100644 --- a/solr/CHANGES.txt +++ b/solr/CHANGES.txt @@ -83,6 +83,8 @@ New Features * SOLR-17813: Add support for SeededKnnVectorQuery (Ilaria Petreti via Alessandro Benedetti) +* SOLR-17815: Add parameter to regulate for ACORN-based filtering in vector search. (Anna Ruggero, Alessandro Benedetti) + Improvements ---------------------