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

alessandrobenedetti pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/solr.git


The following commit(s) were added to refs/heads/main by this push:
     new 07036ab1c70 SOLR-18267: Add flat vector index with no HNSW (#4492)
07036ab1c70 is described below

commit 07036ab1c700a1ead8282482d495b17b17bf0387
Author: Adam Quigley <[email protected]>
AuthorDate: Wed Jul 1 04:34:54 2026 -0400

    SOLR-18267: Add flat vector index with no HNSW (#4492)
---
 .../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 2681cc59840..a9e965f0ae6 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.

Reply via email to