This is an automated email from the ASF dual-hosted git repository.
alessandrobenedetti pushed a commit to branch branch_10x
in repository https://gitbox.apache.org/repos/asf/solr.git
The following commit(s) were added to refs/heads/branch_10x by this push:
new bec1acb9af5 SOLR-18267: Add flat vector index with no HNSW (#4492)
bec1acb9af5 is described below
commit bec1acb9af50f5969f570349d3f535359001ab18
Author: Adam Quigley <[email protected]>
AuthorDate: Wed Jul 1 04:34:54 2026 -0400
SOLR-18267: Add flat vector index with no HNSW (#4492)
(cherry picked from commit 07036ab1c700a1ead8282482d495b17b17bf0387)
---
.../SOLR-18267-add-flat-vector-index.yml | 7 +
.../org/apache/solr/core/SchemaCodecFactory.java | 3 +-
.../apache/solr/core/Solr101FlatVectorFormat.java | 66 ++++++++
.../schema/BinaryQuantizedDenseVectorField.java | 13 ++
.../org/apache/solr/schema/DenseVectorField.java | 15 +-
.../schema/ScalarQuantizedDenseVectorField.java | 6 +
.../org/apache/solr/search/vector/KnnQParser.java | 8 +
.../search/vector/VectorSimilarityQParser.java | 8 +
.../org.apache.lucene.codecs.KnnVectorsFormat | 16 ++
...bad-schema-densevector-flat-binaryQuantized.xml | 29 ++++
...bad-schema-densevector-flat-scalarQuantized.xml | 29 ++++
.../collection1/conf/schema-densevector-flat.xml | 31 ++++
.../BinaryQuantizedDenseVectorFieldTest.java | 8 +
.../apache/solr/schema/DenseVectorFieldTest.java | 166 +++++++++++++++++++++
.../ScalarQuantizedDenseVectorFieldTest.java | 12 +-
.../query-guide/pages/dense-vector-search.adoc | 17 ++-
16 files changed, 426 insertions(+), 8 deletions(-)
diff --git a/changelog/unreleased/SOLR-18267-add-flat-vector-index.yml
b/changelog/unreleased/SOLR-18267-add-flat-vector-index.yml
new file mode 100644
index 00000000000..94bb9c5b7f2
--- /dev/null
+++ b/changelog/unreleased/SOLR-18267-add-flat-vector-index.yml
@@ -0,0 +1,7 @@
+title: Add knnAlgorithm="flat" option to DenseVectorField to skip HNSW graph
construction for exact vector search
+type: added
+authors:
+- name: Adam Quigley
+links:
+- name: SOLR-18267
+ url: https://issues.apache.org/jira/browse/SOLR-18267
\ No newline at end of file
diff --git a/solr/core/src/java/org/apache/solr/core/SchemaCodecFactory.java
b/solr/core/src/java/org/apache/solr/core/SchemaCodecFactory.java
index 1746c0ce3f6..4d4815fbfd7 100644
--- a/solr/core/src/java/org/apache/solr/core/SchemaCodecFactory.java
+++ b/solr/core/src/java/org/apache/solr/core/SchemaCodecFactory.java
@@ -128,7 +128,8 @@ public class SchemaCodecFactory extends CodecFactory
implements SolrCoreAware {
FieldType fieldType = (schemaField == null ? null :
schemaField.getType());
if (fieldType instanceof DenseVectorField vectorField) {
final String knnAlgorithm = vectorField.getKnnAlgorithm();
- if (!DenseVectorField.HNSW_ALGORITHM.equals(knnAlgorithm)) {
+ if (!DenseVectorField.HNSW_ALGORITHM.equals(knnAlgorithm)
+ && !DenseVectorField.FLAT_ALGORITHM.equals(knnAlgorithm)) {
throw new SolrException(
ErrorCode.SERVER_ERROR, knnAlgorithm + " KNN algorithm is
not supported");
}
diff --git
a/solr/core/src/java/org/apache/solr/core/Solr101FlatVectorFormat.java
b/solr/core/src/java/org/apache/solr/core/Solr101FlatVectorFormat.java
new file mode 100644
index 00000000000..d81119f0156
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/core/Solr101FlatVectorFormat.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.core;
+
+import java.io.IOException;
+import org.apache.lucene.codecs.KnnVectorsFormat;
+import org.apache.lucene.codecs.KnnVectorsReader;
+import org.apache.lucene.codecs.KnnVectorsWriter;
+import org.apache.lucene.codecs.hnsw.FlatVectorScorerUtil;
+import org.apache.lucene.codecs.lucene99.Lucene99FlatVectorsFormat;
+import org.apache.lucene.index.SegmentReadState;
+import org.apache.lucene.index.SegmentWriteState;
+
+/**
+ * SPI-registered wrapper for {@link Lucene99FlatVectorsFormat}, which Lucene
does not register as a
+ * {@link KnnVectorsFormat} in SPI.
+ *
+ * @lucene.spi {@value #NAME}
+ * @since 10.1
+ */
+public final class Solr101FlatVectorFormat extends KnnVectorsFormat {
+
+ static final String NAME = "Solr101FlatVectorFormat";
+
+ private final Lucene99FlatVectorsFormat delegate;
+
+ public Solr101FlatVectorFormat() {
+ super(NAME);
+ this.delegate =
+ new
Lucene99FlatVectorsFormat(FlatVectorScorerUtil.getLucene99FlatVectorsScorer());
+ }
+
+ @Override
+ public KnnVectorsWriter fieldsWriter(SegmentWriteState state) throws
IOException {
+ return delegate.fieldsWriter(state);
+ }
+
+ @Override
+ public KnnVectorsReader fieldsReader(SegmentReadState state) throws
IOException {
+ return delegate.fieldsReader(state);
+ }
+
+ @Override
+ public int getMaxDimensions(String fieldName) {
+ return delegate.getMaxDimensions(fieldName);
+ }
+
+ @Override
+ public String toString() {
+ return NAME;
+ }
+}
diff --git
a/solr/core/src/java/org/apache/solr/schema/BinaryQuantizedDenseVectorField.java
b/solr/core/src/java/org/apache/solr/schema/BinaryQuantizedDenseVectorField.java
index d8e15f16435..28da4f678da 100644
---
a/solr/core/src/java/org/apache/solr/schema/BinaryQuantizedDenseVectorField.java
+++
b/solr/core/src/java/org/apache/solr/schema/BinaryQuantizedDenseVectorField.java
@@ -16,12 +16,25 @@
*/
package org.apache.solr.schema;
+import java.util.Map;
import org.apache.lucene.codecs.KnnVectorsFormat;
import
org.apache.lucene.codecs.lucene104.Lucene104HnswScalarQuantizedVectorsFormat;
import
org.apache.lucene.codecs.lucene104.Lucene104ScalarQuantizedVectorsFormat.ScalarEncoding;
+import org.apache.solr.common.SolrException;
public class BinaryQuantizedDenseVectorField extends DenseVectorField {
+ @Override
+ public void init(IndexSchema schema, Map<String, String> args) {
+ super.init(schema, args);
+
+ if (FLAT_ALGORITHM.equals(getKnnAlgorithm())) {
+ throw new SolrException(
+ SolrException.ErrorCode.BAD_REQUEST,
+ "knnAlgorithm 'flat' is not supported for
BinaryQuantizedDenseVectorField");
+ }
+ }
+
@Override
public KnnVectorsFormat buildKnnVectorsFormat() {
return new Lucene104HnswScalarQuantizedVectorsFormat(
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 f29714d5b1e..d1f1430bcee 100644
--- a/solr/core/src/java/org/apache/solr/schema/DenseVectorField.java
+++ b/solr/core/src/java/org/apache/solr/schema/DenseVectorField.java
@@ -45,6 +45,7 @@ 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;
+import org.apache.solr.core.Solr101FlatVectorFormat;
import org.apache.solr.search.QParser;
import org.apache.solr.search.vector.KnnQParser.EarlyTerminationParams;
import org.apache.solr.search.vector.SolrKnnByteVectorQuery;
@@ -69,6 +70,7 @@ import org.slf4j.LoggerFactory;
public class DenseVectorField extends FloatPointField {
private static final Logger log =
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
public static final String HNSW_ALGORITHM = "hnsw";
+ public static final String FLAT_ALGORITHM = "flat";
public static final String CAGRA_HNSW_ALGORITHM = "cagra_hnsw";
public static final String DEFAULT_KNN_ALGORITHM = HNSW_ALGORITHM;
static final String KNN_VECTOR_DIMENSION = "vectorDimension";
@@ -471,7 +473,11 @@ public class DenseVectorField extends FloatPointField {
}
public KnnVectorsFormat buildKnnVectorsFormat() {
- return new Lucene99HnswVectorsFormat(hnswM, hnswEfConstruction);
+ if (FLAT_ALGORITHM.equals(knnAlgorithm)) {
+ return new Solr101FlatVectorFormat();
+ } else {
+ return new Lucene99HnswVectorsFormat(hnswM, hnswEfConstruction);
+ }
}
@Override
@@ -503,6 +509,13 @@ public class DenseVectorField extends FloatPointField {
EarlyTerminationParams earlyTermination,
Integer filteredSearchThreshold) {
+ if (FLAT_ALGORITHM.equals(knnAlgorithm)) {
+ throw new SolrException(
+ SolrException.ErrorCode.BAD_REQUEST,
+ "KNN vector queries are not supported for fields using
knnAlgorithm=\"flat\". "
+ + "Use vectorSimilarity() function queries instead.");
+ }
+
DenseVectorParser vectorBuilder =
getVectorBuilder(vectorToSearch, DenseVectorParser.BuilderPhase.QUERY);
diff --git
a/solr/core/src/java/org/apache/solr/schema/ScalarQuantizedDenseVectorField.java
b/solr/core/src/java/org/apache/solr/schema/ScalarQuantizedDenseVectorField.java
index 4d91d1eafe2..26a33be48a2 100644
---
a/solr/core/src/java/org/apache/solr/schema/ScalarQuantizedDenseVectorField.java
+++
b/solr/core/src/java/org/apache/solr/schema/ScalarQuantizedDenseVectorField.java
@@ -122,6 +122,12 @@ public class ScalarQuantizedDenseVectorField extends
DenseVectorField {
}
super.init(schema, args);
+
+ if (FLAT_ALGORITHM.equals(getKnnAlgorithm())) {
+ throw new SolrException(
+ SolrException.ErrorCode.BAD_REQUEST,
+ "knnAlgorithm 'flat' is not supported for
ScalarQuantizedDenseVectorField");
+ }
}
@Override
diff --git a/solr/core/src/java/org/apache/solr/search/vector/KnnQParser.java
b/solr/core/src/java/org/apache/solr/search/vector/KnnQParser.java
index 2170f56f19b..d376bfa5a40 100644
--- a/solr/core/src/java/org/apache/solr/search/vector/KnnQParser.java
+++ b/solr/core/src/java/org/apache/solr/search/vector/KnnQParser.java
@@ -119,6 +119,14 @@ public class KnnQParser extends AbstractVectorQParserBase {
final String vectorField = getFieldName();
final SchemaField schemaField =
req.getCore().getLatestSchema().getField(getFieldName());
final DenseVectorField denseVectorType = getCheckedFieldType(schemaField);
+
+ if
(DenseVectorField.FLAT_ALGORITHM.equals(denseVectorType.getKnnAlgorithm())) {
+ throw new SolrException(
+ SolrException.ErrorCode.BAD_REQUEST,
+ "The {!knn} query parser is not supported for fields using
knnAlgorithm=\"flat\". "
+ + "Use vectorSimilarity() function queries instead.");
+ }
+
final String vectorToSearch = getVectorToSearch();
final int topK = localParams.getInt(TOP_K, DEFAULT_TOP_K);
diff --git
a/solr/core/src/java/org/apache/solr/search/vector/VectorSimilarityQParser.java
b/solr/core/src/java/org/apache/solr/search/vector/VectorSimilarityQParser.java
index f2d9be3738a..a88d1dfb306 100644
---
a/solr/core/src/java/org/apache/solr/search/vector/VectorSimilarityQParser.java
+++
b/solr/core/src/java/org/apache/solr/search/vector/VectorSimilarityQParser.java
@@ -46,6 +46,14 @@ public class VectorSimilarityQParser extends
AbstractVectorQParserBase {
final String fieldName = getFieldName();
final SchemaField schemaField =
req.getCore().getLatestSchema().getField(fieldName);
final DenseVectorField denseVectorType = getCheckedFieldType(schemaField);
+
+ if
(DenseVectorField.FLAT_ALGORITHM.equals(denseVectorType.getKnnAlgorithm())) {
+ throw new SolrException(
+ SolrException.ErrorCode.BAD_REQUEST,
+ "The {!vectorSimilarity} query parser is not supported for fields
using knnAlgorithm=\"flat\". "
+ + "Use vectorSimilarity() function queries instead.");
+ }
+
final String vectorToSearch = getVectorToSearch();
final float minTraverse = localParams.getFloat(MIN_TRAVERSE,
DEFAULT_MIN_TRAVERSE);
final Float minReturn = localParams.getFloat(MIN_RETURN);
diff --git
a/solr/core/src/resources/META-INF/services/org.apache.lucene.codecs.KnnVectorsFormat
b/solr/core/src/resources/META-INF/services/org.apache.lucene.codecs.KnnVectorsFormat
new file mode 100644
index 00000000000..2e6fda571db
--- /dev/null
+++
b/solr/core/src/resources/META-INF/services/org.apache.lucene.codecs.KnnVectorsFormat
@@ -0,0 +1,16 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+org.apache.solr.core.Solr101FlatVectorFormat
diff --git
a/solr/core/src/test-files/solr/collection1/conf/bad-schema-densevector-flat-binaryQuantized.xml
b/solr/core/src/test-files/solr/collection1/conf/bad-schema-densevector-flat-binaryQuantized.xml
new file mode 100644
index 00000000000..f4e6d392f9e
--- /dev/null
+++
b/solr/core/src/test-files/solr/collection1/conf/bad-schema-densevector-flat-binaryQuantized.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" ?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- Test schema file for DenseVectorField -->
+
+<schema name="bad-schema-densevector-flat-bq" version="1.7">
+ <fieldType name="string" class="solr.StrField" multiValued="true"/>
+ <fieldType name="knn_vector_flat_bq"
class="solr.BinaryQuantizedDenseVectorField" vectorDimension="4"
similarityFunction="cosine" knnAlgorithm="flat"/>
+
+ <field name="id" type="string" indexed="true" stored="true"
multiValued="false" required="false"/>
+ <field name="vector" type="knn_vector_flat_bq" indexed="true" stored="true"/>
+
+ <uniqueKey>id</uniqueKey>
+</schema>
\ No newline at end of file
diff --git
a/solr/core/src/test-files/solr/collection1/conf/bad-schema-densevector-flat-scalarQuantized.xml
b/solr/core/src/test-files/solr/collection1/conf/bad-schema-densevector-flat-scalarQuantized.xml
new file mode 100644
index 00000000000..365bddd03e7
--- /dev/null
+++
b/solr/core/src/test-files/solr/collection1/conf/bad-schema-densevector-flat-scalarQuantized.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" ?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- Test schema file for DenseVectorField -->
+
+<schema name="bad-schema-densevector-flat-quantized" version="1.7">
+ <fieldType name="string" class="solr.StrField" multiValued="true"/>
+ <fieldType name="knn_vector_flat_sq"
class="solr.ScalarQuantizedDenseVectorField" vectorDimension="4"
similarityFunction="cosine" knnAlgorithm="flat"/>
+
+ <field name="id" type="string" indexed="true" stored="true"
multiValued="false" required="false"/>
+ <field name="vector" type="knn_vector_flat_sq" indexed="true" stored="true"/>
+
+ <uniqueKey>id</uniqueKey>
+</schema>
diff --git
a/solr/core/src/test-files/solr/collection1/conf/schema-densevector-flat.xml
b/solr/core/src/test-files/solr/collection1/conf/schema-densevector-flat.xml
new file mode 100644
index 00000000000..a1e66ee3aa2
--- /dev/null
+++ b/solr/core/src/test-files/solr/collection1/conf/schema-densevector-flat.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" ?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- Test schema file for DenseVectorField with flat (non-HNSW) algorithm -->
+
+<schema name="schema-densevector-flat" version="1.7">
+ <fieldType name="string" class="solr.StrField" multiValued="true"/>
+ <fieldType name="knn_vector_flat" class="solr.DenseVectorField"
vectorDimension="4" similarityFunction="cosine" knnAlgorithm="flat"/>
+ <fieldType name="knn_vector_flat_byte" class="solr.DenseVectorField"
vectorDimension="4" similarityFunction="cosine" knnAlgorithm="flat"
vectorEncoding="BYTE"/>
+
+ <field name="id" type="string" indexed="true" stored="true"
multiValued="false" required="false"/>
+ <field name="vector_flat" type="knn_vector_flat" indexed="true"
stored="true"/>
+ <field name="vector_flat_byte" type="knn_vector_flat_byte" indexed="true"
stored="true"/>
+
+ <uniqueKey>id</uniqueKey>
+</schema>
diff --git
a/solr/core/src/test/org/apache/solr/schema/BinaryQuantizedDenseVectorFieldTest.java
b/solr/core/src/test/org/apache/solr/schema/BinaryQuantizedDenseVectorFieldTest.java
index de08d3f7ed2..7e0e798bbf2 100644
---
a/solr/core/src/test/org/apache/solr/schema/BinaryQuantizedDenseVectorFieldTest.java
+++
b/solr/core/src/test/org/apache/solr/schema/BinaryQuantizedDenseVectorFieldTest.java
@@ -38,6 +38,14 @@ public class BinaryQuantizedDenseVectorFieldTest extends
AbstractBadConfigTestBa
}
}
+ @Test
+ public void fieldDefinition_flatAlgorithm_shouldThrowException() throws
Exception {
+ assertConfigs(
+ "solrconfig-basic.xml",
+ "bad-schema-densevector-flat-binaryQuantized.xml",
+ "knnAlgorithm 'flat' is not supported for
BinaryQuantizedDenseVectorField");
+ }
+
// there are no major interface differences between
BinaryBitQuantizedDenseVectorField and
// DenseVectorField
// so we can rely on those tests for validation cases
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 f84eaf40ef0..a11e184e2e1 100644
--- a/solr/core/src/test/org/apache/solr/schema/DenseVectorFieldTest.java
+++ b/solr/core/src/test/org/apache/solr/schema/DenseVectorFieldTest.java
@@ -1136,4 +1136,170 @@ public class DenseVectorFieldTest extends
AbstractBadConfigTestBase {
deleteCore();
}
}
+
+ @Test
+ public void fieldDefinition_flatAlgorithm_shouldLoadSchemaField() throws
Exception {
+ try {
+ initCore("solrconfig_codec.xml", "schema-densevector-flat.xml");
+ IndexSchema schema = h.getCore().getLatestSchema();
+
+ SchemaField vector = schema.getField("vector_flat");
+ assertNotNull(vector);
+
+ DenseVectorField type = (DenseVectorField) vector.getType();
+ assertThat(type.getKnnAlgorithm(), is("flat"));
+ assertThat(type.getDimension(), is(4));
+ assertThat(type.getSimilarityFunction(),
is(VectorSimilarityFunction.COSINE));
+
+ assertTrue(vector.indexed());
+ assertTrue(vector.stored());
+ } finally {
+ deleteCore();
+ }
+ }
+
+ @Test
+ public void flatAlgorithm_knnQuery_shouldThrowException() throws Exception {
+ try {
+ initCore("solrconfig_codec.xml", "schema-densevector-flat.xml");
+
+ assertQEx(
+ "Running {!knn} on a flat vector field should raise an Exception",
+ "knnAlgorithm=\"flat\"",
+ req("q", "{!knn f=vector_flat topK=2}[1, 2, 3, 4]", "fl", "id"),
+ SolrException.ErrorCode.BAD_REQUEST);
+ } finally {
+ deleteCore();
+ }
+ }
+
+ @Test
+ public void flatAlgorithm_vectorSimilarityFunction_shouldReturnResults()
throws Exception {
+ try {
+ initCore("solrconfig_codec.xml", "schema-densevector-flat.xml");
+
+ SolrInputDocument doc1 = new SolrInputDocument();
+ doc1.addField("id", "0");
+ doc1.addField("vector_flat", Arrays.asList(1, 2, 3, 4));
+ assertU(adoc(doc1));
+
+ SolrInputDocument doc2 = new SolrInputDocument();
+ doc2.addField("id", "1");
+ doc2.addField("vector_flat", Arrays.asList(2, 3, 4, 5));
+ assertU(adoc(doc2));
+
+ SolrInputDocument doc3 = new SolrInputDocument();
+ doc3.addField("id", "2");
+ doc3.addField("vector_flat", Arrays.asList(100, 200, 50, 25));
+ assertU(adoc(doc3));
+
+ assertU(commit());
+
+ assertJQ(
+ req(
+ "q", "{!func}vectorSimilarity(vector_flat,[1, 2, 3, 4])",
+ "fl", "id,score"),
+ "/response/numFound==3",
+ "/response/docs/[0]/id=='0'",
+ "/response/docs/[0]/score==1.0");
+
+ // Filtered test
+ assertJQ(
+ req(
+ "q", "{!func}vectorSimilarity(vector_flat,[1, 2, 3, 4])",
+ "fq", "id:(0 2)",
+ "fl", "id,score"),
+ "/response/numFound==2",
+ "/response/docs/[0]/id=='0'",
+ "/response/docs/[0]/score==1.0");
+ } finally {
+ deleteCore();
+ }
+ }
+
+ @Test
+ public void flatAlgorithm_storedField_shouldBeReturnedInResults() throws
Exception {
+ try {
+ initCore("solrconfig_codec.xml", "schema-densevector-flat.xml");
+
+ SolrInputDocument doc1 = new SolrInputDocument();
+ doc1.addField("id", "0");
+ doc1.addField("vector_flat", Arrays.asList(1.1f, 2.2f, 3.3f, 4.4f));
+ assertU(adoc(doc1));
+ assertU(commit());
+
+ assertJQ(
+ req("q", "id:0", "fl", "vector_flat"),
+ "/response/docs/[0]=={'vector_flat':[1.1,2.2,3.3,4.4]}");
+ } finally {
+ deleteCore();
+ }
+ }
+
+ @Test
+ public void flatAlgorithm_byteEncoding_shouldWork() throws Exception {
+ try {
+ initCore("solrconfig_codec.xml", "schema-densevector-flat.xml");
+
+ SolrInputDocument doc1 = new SolrInputDocument();
+ doc1.addField("id", "0");
+ doc1.addField("vector_flat_byte", Arrays.asList(1, 2, 3, 4));
+ assertU(adoc(doc1));
+
+ SolrInputDocument doc2 = new SolrInputDocument();
+ doc2.addField("id", "1");
+ doc2.addField("vector_flat_byte", Arrays.asList(5, 6, 7, 8));
+ assertU(adoc(doc2));
+
+ assertU(commit());
+
+ assertJQ(
+ req(
+ "q", "{!func}vectorSimilarity(vector_flat_byte,[1, 2, 3, 4])",
+ "fl", "id,score"),
+ "/response/numFound==2",
+ "/response/docs/[0]/id=='0'",
+ "/response/docs/[0]/score==1.0");
+ } finally {
+ deleteCore();
+ }
+ }
+
+ @Test
+ public void flatAlgorithm_vectorSimilarityQParser_shouldThrowException()
throws Exception {
+ try {
+ initCore("solrconfig_codec.xml", "schema-densevector-flat.xml");
+
+ assertQEx(
+ "Running {!vectorSimilarity} on a flat vector field should raise an
Exception",
+ "knnAlgorithm=\"flat\"",
+ req(
+ "q", "{!vectorSimilarity f=vector_flat minReturn=0.99}[1, 2, 3,
4]",
+ "fl", "id"),
+ SolrException.ErrorCode.BAD_REQUEST);
+ } finally {
+ deleteCore();
+ }
+ }
+
+ @Test
+ public void flatAlgorithm_getKnnVectorQuery_shouldThrowException() throws
Exception {
+ try {
+ initCore("solrconfig_codec.xml", "schema-densevector-flat.xml");
+ IndexSchema schema = h.getCore().getLatestSchema();
+ SchemaField vectorField = schema.getField("vector_flat");
+ assertNotNull(vectorField);
+ DenseVectorField type = (DenseVectorField) vectorField.getType();
+
+ SolrException ex =
+ expectThrows(
+ SolrException.class,
+ () ->
+ type.getKnnVectorQuery(
+ "vector_flat", "[1, 2, 3, 4]", 3, 3, null, null, null,
null));
+ assertTrue(ex.getMessage().contains("knnAlgorithm=\"flat\""));
+ } finally {
+ deleteCore();
+ }
+ }
}
diff --git
a/solr/core/src/test/org/apache/solr/schema/ScalarQuantizedDenseVectorFieldTest.java
b/solr/core/src/test/org/apache/solr/schema/ScalarQuantizedDenseVectorFieldTest.java
index 8168288c044..94ebc63003b 100644
---
a/solr/core/src/test/org/apache/solr/schema/ScalarQuantizedDenseVectorFieldTest.java
+++
b/solr/core/src/test/org/apache/solr/schema/ScalarQuantizedDenseVectorFieldTest.java
@@ -20,13 +20,9 @@ import static org.hamcrest.core.Is.is;
import org.apache.lucene.index.VectorSimilarityFunction;
import org.apache.solr.core.AbstractBadConfigTestBase;
-import org.junit.Before;
import org.junit.Test;
public class ScalarQuantizedDenseVectorFieldTest extends
AbstractBadConfigTestBase {
- @Before
- public void init() {}
-
@Test
public void fieldTypeDefinition_invalidBitSize_shouldThrowException() throws
Exception {
assertConfigs(
@@ -132,4 +128,12 @@ public class ScalarQuantizedDenseVectorFieldTest extends
AbstractBadConfigTestBa
deleteCore();
}
}
+
+ @Test
+ public void fieldDefinition_flatAlgorithm_shouldThrowException() throws
Exception {
+ assertConfigs(
+ "solrconfig-basic.xml",
+ "bad-schema-densevector-flat-scalarQuantized.xml",
+ "knnAlgorithm 'flat' is not supported for
ScalarQuantizedDenseVectorField");
+ }
}
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 1653863299f..db02ddceda1 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
@@ -126,8 +126,10 @@ Here's how `DenseVectorField` can be configured with the
advanced hyperparameter
+
(advanced) Specifies the underlying knn algorithm to use
+
-Accepted values: `hnsw`, `cagra_hnsw` (requires GPU acceleration setup).
-
+Accepted values: `hnsw`, `flat`, `cagra_hnsw` (requires GPU acceleration
setup).
++
+The `flat` algorithm stores vectors without building an HNSW graph. This
avoids the indexing overhead of graph construction, but does not support the
`{!knn}` query parser. Use `vectorSimilarity()` function queries to score and
rank documents by vector similarity. See <<Flat Vector Index>> for details.
++
Please note that the `knnAlgorithm` accepted values may change in future
releases.
`vectorEncoding`::
@@ -837,6 +839,17 @@ Details about using the ReRank Query Parser can be found
in the xref:query-guide
====
+== Flat Vector Index
+
+Setting `knnAlgorithm="flat"` stores vectors without building an HNSW graph,
avoiding the indexing cost of graph construction.
+
+Flat fields do not support the `{!knn}`, `{!knn_text_to_vector}`, or
`{!vectorSimilarity}` query parsers.
+Use `vectorSimilarity()` function queries to score and rank by similarity:
+
+[source]
+q={!func}vectorSimilarity(vector,[1.0, 2.0, 3.0, 4.0])&fl=id,score
+
+
== Indexing Multi-Vectors for Late Interaction
For Late Interaction usecases, Solr provides a
`StrFloatLateInteractionVectorField` field type, which supports indexing a
variable length "Multi-Vector" of Float vectors, serialized as as a single
String value.