From f7d238891c3c8f974dac108c144d38b234ab5727 Mon Sep 17 00:00:00 2001
From: Ross Lawley
Date: Tue, 5 May 2026 17:13:34 +0100
Subject: [PATCH] Add typed builder API for vector search index definitions
* Introduced SearchIndexDefinition sealed interface with factory methods,
* VectorSearchIndexFields (vectorField, filterField, autoEmbedField builders),
* HnswSearchIndexOptions for HNSW-specific parameters, and
* VectorSearchIndexDefinition as the concrete implementation. Updates
* Updated SearchIndexModel javadoc to reference the new builders.
JAVA-6112
---
.../client/model/HnswSearchIndexOptions.java | 100 +++++
.../client/model/SearchIndexDefinition.java | 77 ++++
.../client/model/SearchIndexModel.java | 9 +
.../model/VectorSearchIndexDefinition.java | 76 ++++
.../client/model/VectorSearchIndexFields.java | 342 ++++++++++++++++++
.../model/SearchIndexDefinitionTest.java | 225 ++++++++++++
.../org/mongodb/scala/model/package.scala | 24 ++
7 files changed, 853 insertions(+)
create mode 100644 driver-core/src/main/com/mongodb/client/model/HnswSearchIndexOptions.java
create mode 100644 driver-core/src/main/com/mongodb/client/model/SearchIndexDefinition.java
create mode 100644 driver-core/src/main/com/mongodb/client/model/VectorSearchIndexDefinition.java
create mode 100644 driver-core/src/main/com/mongodb/client/model/VectorSearchIndexFields.java
create mode 100644 driver-core/src/test/unit/com/mongodb/client/model/SearchIndexDefinitionTest.java
diff --git a/driver-core/src/main/com/mongodb/client/model/HnswSearchIndexOptions.java b/driver-core/src/main/com/mongodb/client/model/HnswSearchIndexOptions.java
new file mode 100644
index 00000000000..f8410e858fe
--- /dev/null
+++ b/driver-core/src/main/com/mongodb/client/model/HnswSearchIndexOptions.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2008-present MongoDB, Inc.
+ *
+ * Licensed 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 com.mongodb.client.model;
+
+import com.mongodb.lang.Nullable;
+import org.bson.BsonDocument;
+import org.bson.BsonInt32;
+import org.bson.codecs.configuration.CodecRegistry;
+import org.bson.conversions.Bson;
+
+/**
+ * Options for the HNSW (Hierarchical Navigable Small World) indexing method in a vector search index.
+ *
+ * This class provides a fluent builder for specifying HNSW-specific parameters when creating
+ * a vector search index with {@code indexingMethod("hnsw")}.
+ *
+ * Since {@link VectorSearchIndexFields.VectorField#hnswOptions(Bson)} accepts any {@link Bson},
+ * a raw {@link org.bson.Document} may also be passed directly for forward compatibility.
+ *
+ * {@code
+ * vectorField("embedding")
+ * .indexingMethod("hnsw")
+ * .hnswOptions(new HnswSearchIndexOptions().maxEdges(16).numEdgeCandidates(200))
+ * }
+ *
+ * @see VectorSearchIndexFields.VectorField#hnswOptions(Bson)
+ * @since 5.8
+ */
+public final class HnswSearchIndexOptions implements Bson {
+ @Nullable
+ private Integer maxEdges;
+ @Nullable
+ private Integer numEdgeCandidates;
+
+ /**
+ * Creates a new instance with default settings.
+ *
+ * @since 5.8
+ */
+ public HnswSearchIndexOptions() {
+ }
+
+ /**
+ * Sets the maximum number of connected neighbors for each node in the HNSW graph.
+ *
+ * @param maxEdges the maximum number of edges (connected neighbors)
+ * @return this
+ * @since 5.8
+ */
+ public HnswSearchIndexOptions maxEdges(final int maxEdges) {
+ this.maxEdges = maxEdges;
+ return this;
+ }
+
+ /**
+ * Sets the number of nearest neighbor candidates to consider when building the HNSW graph.
+ *
+ * @param numEdgeCandidates the number of nearest neighbor candidates
+ * @return this
+ * @since 5.8
+ */
+ public HnswSearchIndexOptions numEdgeCandidates(final int numEdgeCandidates) {
+ this.numEdgeCandidates = numEdgeCandidates;
+ return this;
+ }
+
+ @Override
+ public BsonDocument toBsonDocument(final Class documentClass, final CodecRegistry codecRegistry) {
+ BsonDocument doc = new BsonDocument();
+ if (maxEdges != null) {
+ doc.append("maxEdges", new BsonInt32(maxEdges));
+ }
+ if (numEdgeCandidates != null) {
+ doc.append("numEdgeCandidates", new BsonInt32(numEdgeCandidates));
+ }
+ return doc;
+ }
+
+ @Override
+ public String toString() {
+ return "HnswSearchIndexOptions{"
+ + "maxEdges=" + maxEdges
+ + ", numEdgeCandidates=" + numEdgeCandidates
+ + '}';
+ }
+}
diff --git a/driver-core/src/main/com/mongodb/client/model/SearchIndexDefinition.java b/driver-core/src/main/com/mongodb/client/model/SearchIndexDefinition.java
new file mode 100644
index 00000000000..504d2425591
--- /dev/null
+++ b/driver-core/src/main/com/mongodb/client/model/SearchIndexDefinition.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2008-present MongoDB, Inc.
+ *
+ * Licensed 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 com.mongodb.client.model;
+
+import com.mongodb.annotations.Sealed;
+import org.bson.conversions.Bson;
+
+import java.util.List;
+
+import static com.mongodb.assertions.Assertions.notNull;
+import static java.util.Arrays.asList;
+
+/**
+ * A definition for an Atlas Search index.
+ *
+ * This interface provides factory methods for creating search index definitions
+ * that can be passed to {@link SearchIndexModel}.
+ *
+ * @see SearchIndexModel
+ * @see VectorSearchIndexDefinition
+ * @since 5.8
+ */
+@Sealed
+public interface SearchIndexDefinition extends Bson {
+
+ /**
+ * Creates a vector search index definition with the specified fields.
+ *
+ * The resulting definition produces a document of the form {@code {"fields": [...]}},
+ * suitable for use with {@link SearchIndexType#vectorSearch()}.
+ *
+ * @param fields the fields for the vector search index. Each field should be created using
+ * {@link VectorSearchIndexFields} factory methods, or may be a raw {@link Bson} document.
+ * @return a new {@link VectorSearchIndexDefinition}
+ * @see VectorSearchIndexFields#vectorField(String)
+ * @see VectorSearchIndexFields#filterField(String)
+ * @see VectorSearchIndexFields#autoEmbedField(String)
+ * @since 5.8
+ */
+ static VectorSearchIndexDefinition vectorSearch(final Bson... fields) {
+ notNull("fields", fields);
+ return vectorSearch(asList(fields));
+ }
+
+ /**
+ * Creates a vector search index definition with the specified fields.
+ *
+ * The resulting definition produces a document of the form {@code {"fields": [...]}},
+ * suitable for use with {@link SearchIndexType#vectorSearch()}.
+ *
+ * @param fields the fields for the vector search index. Each field should be created using
+ * {@link VectorSearchIndexFields} factory methods, or may be a raw {@link Bson} document.
+ * @return a new {@link VectorSearchIndexDefinition}
+ * @see VectorSearchIndexFields#vectorField(String)
+ * @see VectorSearchIndexFields#filterField(String)
+ * @see VectorSearchIndexFields#autoEmbedField(String)
+ * @since 5.8
+ */
+ static VectorSearchIndexDefinition vectorSearch(final List extends Bson> fields) {
+ notNull("fields", fields);
+ return new VectorSearchIndexDefinition(fields);
+ }
+}
diff --git a/driver-core/src/main/com/mongodb/client/model/SearchIndexModel.java b/driver-core/src/main/com/mongodb/client/model/SearchIndexModel.java
index 2a229e1a579..8a8de167aae 100644
--- a/driver-core/src/main/com/mongodb/client/model/SearchIndexModel.java
+++ b/driver-core/src/main/com/mongodb/client/model/SearchIndexModel.java
@@ -24,6 +24,12 @@
/**
* A model describing the creation of a single Atlas Search index.
*
+ * The {@code definition} parameter accepts any {@link org.bson.conversions.Bson} instance.
+ * For vector search indexes, use the builders provided by {@link SearchIndexDefinition#vectorSearch(Bson...)}
+ * and {@link VectorSearchIndexFields} to construct the definition.
+ *
+ * @see SearchIndexDefinition
+ * @see VectorSearchIndexFields
* @since 4.11
* @mongodb.server.release 6.0
*/
@@ -42,6 +48,7 @@ public final class SearchIndexModel {
* will be used to create the search index.
*
* @param definition the search index mapping definition.
+ * @see SearchIndexDefinition#vectorSearch(Bson...)
*/
public SearchIndexModel(final Bson definition) {
this(null, definition, null);
@@ -52,6 +59,7 @@ public SearchIndexModel(final Bson definition) {
*
* @param name the search index name.
* @param definition the search index mapping definition.
+ * @see SearchIndexDefinition#vectorSearch(Bson...)
*/
public SearchIndexModel(final String name, final Bson definition) {
this(name, definition, null);
@@ -63,6 +71,7 @@ public SearchIndexModel(final String name, final Bson definition) {
* @param name the search index name.
* @param definition the search index mapping definition.
* @param type the search index type.
+ * @see SearchIndexDefinition#vectorSearch(Bson...)
* @since 5.2
*/
public SearchIndexModel(@Nullable final String name, final Bson definition, @Nullable final SearchIndexType type) {
diff --git a/driver-core/src/main/com/mongodb/client/model/VectorSearchIndexDefinition.java b/driver-core/src/main/com/mongodb/client/model/VectorSearchIndexDefinition.java
new file mode 100644
index 00000000000..bf4c41723bf
--- /dev/null
+++ b/driver-core/src/main/com/mongodb/client/model/VectorSearchIndexDefinition.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2008-present MongoDB, Inc.
+ *
+ * Licensed 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 com.mongodb.client.model;
+
+import org.bson.BsonArray;
+import org.bson.BsonDocument;
+import org.bson.codecs.configuration.CodecRegistry;
+import org.bson.conversions.Bson;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A vector search index definition, producing a document of the form {@code {"fields": [...]}}.
+ *
+ * Instances are created via {@link SearchIndexDefinition#vectorSearch(Bson...)}.
+ *
+ * @see SearchIndexDefinition
+ * @see SearchIndexType#vectorSearch()
+ * @since 5.8
+ */
+public final class VectorSearchIndexDefinition implements SearchIndexDefinition {
+ private final List extends Bson> fields;
+
+ VectorSearchIndexDefinition(final List extends Bson> fields) {
+ this.fields = new ArrayList<>(fields);
+ }
+
+ @Override
+ public BsonDocument toBsonDocument(final Class documentClass, final CodecRegistry codecRegistry) {
+ BsonArray fieldArray = new BsonArray();
+ for (Bson field : fields) {
+ fieldArray.add(field.toBsonDocument(documentClass, codecRegistry));
+ }
+ return new BsonDocument("fields", fieldArray);
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ VectorSearchIndexDefinition that = (VectorSearchIndexDefinition) o;
+ return Objects.equals(fields, that.fields);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(fields);
+ }
+
+ @Override
+ public String toString() {
+ return "VectorSearchIndexDefinition{"
+ + "fields=" + fields
+ + '}';
+ }
+}
diff --git a/driver-core/src/main/com/mongodb/client/model/VectorSearchIndexFields.java b/driver-core/src/main/com/mongodb/client/model/VectorSearchIndexFields.java
new file mode 100644
index 00000000000..f40cc5837f2
--- /dev/null
+++ b/driver-core/src/main/com/mongodb/client/model/VectorSearchIndexFields.java
@@ -0,0 +1,342 @@
+/*
+ * Copyright 2008-present MongoDB, Inc.
+ *
+ * Licensed 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 com.mongodb.client.model;
+
+import com.mongodb.lang.Nullable;
+import org.bson.BsonDocument;
+import org.bson.BsonInt32;
+import org.bson.BsonString;
+import org.bson.codecs.configuration.CodecRegistry;
+import org.bson.conversions.Bson;
+
+import static com.mongodb.assertions.Assertions.notNull;
+
+/**
+ * A factory for defining fields within a vector search index definition.
+ *
+ * A convenient way to use this class is to statically import all of its methods, which allows usage like:
+ * {@code
+ * SearchIndexDefinition.vectorSearch(
+ * vectorField("plot_embedding")
+ * .numDimensions(1536)
+ * .similarity("euclidean")
+ * .indexingMethod("flat"),
+ * filterField("genre")
+ * );
+ * }
+ *
+ * @see SearchIndexDefinition#vectorSearch(Bson...)
+ * @since 5.8
+ */
+public final class VectorSearchIndexFields {
+
+ private VectorSearchIndexFields() {
+ }
+
+ /**
+ * Creates a vector field definition for a vector search index.
+ *
+ * @param path the field path in the document
+ * @return a new {@link VectorField}
+ * @since 5.8
+ */
+ public static VectorField vectorField(final String path) {
+ return new VectorField(notNull("path", path));
+ }
+
+ /**
+ * Creates a filter field definition for a vector search index.
+ *
+ * @param path the field path in the document
+ * @return a new {@link FilterField}
+ * @since 5.8
+ */
+ public static FilterField filterField(final String path) {
+ return new FilterField(notNull("path", path));
+ }
+
+ /**
+ * Creates an auto-embed field definition for a vector search index.
+ *
+ * @param path the field path in the document containing the content to embed
+ * @return a new {@link AutoEmbedField}
+ * @since 5.8
+ */
+ public static AutoEmbedField autoEmbedField(final String path) {
+ return new AutoEmbedField(notNull("path", path));
+ }
+
+ /**
+ * A vector field definition for a vector search index.
+ *
+ * Instances are created via {@link #vectorField(String)}.
+ *
+ * @since 5.8
+ */
+ public static final class VectorField implements Bson {
+ private final String path;
+ @Nullable
+ private Integer numDimensions;
+ @Nullable
+ private String similarity;
+ @Nullable
+ private String indexingMethod;
+ @Nullable
+ private Bson hnswOptions;
+
+ private VectorField(final String path) {
+ this.path = path;
+ }
+
+ /**
+ * Sets the number of dimensions for the vector field.
+ *
+ * @param numDimensions the number of vector dimensions
+ * @return this
+ * @since 5.8
+ */
+ public VectorField numDimensions(final int numDimensions) {
+ this.numDimensions = numDimensions;
+ return this;
+ }
+
+ /**
+ * Sets the similarity function used to compare vectors.
+ *
+ * Supported values:
+ *
+ * - {@code "euclidean"} — measures the distance between ends of vectors
+ * - {@code "cosine"} — measures the angle between vectors
+ * - {@code "dotProduct"} — measures both the magnitude and direction of vectors
+ *
+ *
+ * @param similarity the similarity function name
+ * @return this
+ * @since 5.8
+ */
+ public VectorField similarity(final String similarity) {
+ this.similarity = notNull("similarity", similarity);
+ return this;
+ }
+
+ /**
+ * Sets the indexing method for this vector field.
+ *
+ * Supported values:
+ *
+ * - {@code "flat"} — optimized for multi-tenant use cases with singular, static filters
+ * - {@code "hnsw"} — Hierarchical Navigable Small World graph (default)
+ *
+ *
+ * @param indexingMethod the indexing method name
+ * @return this
+ * @since 5.8
+ */
+ public VectorField indexingMethod(final String indexingMethod) {
+ this.indexingMethod = notNull("indexingMethod", indexingMethod);
+ return this;
+ }
+
+ /**
+ * Sets the HNSW options for this vector field.
+ *
+ * This is only applicable when the indexing method is {@code "hnsw"}.
+ * A convenience builder is available via {@link HnswSearchIndexOptions}, or a raw
+ * {@link org.bson.Document} may be passed directly.
+ *
+ * @param hnswOptions the HNSW options
+ * @return this
+ * @see HnswSearchIndexOptions
+ * @since 5.8
+ */
+ public VectorField hnswOptions(final Bson hnswOptions) {
+ this.hnswOptions = notNull("hnswOptions", hnswOptions);
+ return this;
+ }
+
+ @Override
+ public BsonDocument toBsonDocument(final Class documentClass, final CodecRegistry codecRegistry) {
+ BsonDocument doc = new BsonDocument();
+ doc.append("type", new BsonString("vector"));
+ doc.append("path", new BsonString(path));
+ if (numDimensions != null) {
+ doc.append("numDimensions", new BsonInt32(numDimensions));
+ }
+ if (similarity != null) {
+ doc.append("similarity", new BsonString(similarity));
+ }
+ if (indexingMethod != null) {
+ doc.append("indexingMethod", new BsonString(indexingMethod));
+ }
+ if (hnswOptions != null) {
+ doc.append("hnswOptions", hnswOptions.toBsonDocument(documentClass, codecRegistry));
+ }
+ return doc;
+ }
+
+ @Override
+ public String toString() {
+ return "VectorField{"
+ + "path='" + path + '\''
+ + ", numDimensions=" + numDimensions
+ + ", similarity='" + similarity + '\''
+ + ", indexingMethod='" + indexingMethod + '\''
+ + ", hnswOptions=" + hnswOptions
+ + '}';
+ }
+ }
+
+ /**
+ * A filter field definition for a vector search index.
+ *
+ * Instances are created via {@link #filterField(String)}.
+ *
+ * @since 5.8
+ */
+ public static final class FilterField implements Bson {
+ private final String path;
+
+ private FilterField(final String path) {
+ this.path = path;
+ }
+
+ @Override
+ public BsonDocument toBsonDocument(final Class documentClass, final CodecRegistry codecRegistry) {
+ BsonDocument doc = new BsonDocument();
+ doc.append("type", new BsonString("filter"));
+ doc.append("path", new BsonString(path));
+ return doc;
+ }
+
+ @Override
+ public String toString() {
+ return "FilterField{"
+ + "path='" + path + '\''
+ + '}';
+ }
+ }
+
+ /**
+ * An auto-embed field definition for a vector search index.
+ *
+ * Instances are created via {@link #autoEmbedField(String)}.
+ *
+ * @since 5.8
+ */
+ public static final class AutoEmbedField implements Bson {
+ private final String path;
+ @Nullable
+ private String modality;
+ @Nullable
+ private String model;
+ @Nullable
+ private Integer numDimensions;
+ @Nullable
+ private String quantization;
+
+ private AutoEmbedField(final String path) {
+ this.path = path;
+ }
+
+ /**
+ * Sets the modality for auto-embedding.
+ *
+ * @param modality the modality (e.g., {@code "text"})
+ * @return this
+ * @since 5.8
+ */
+ public AutoEmbedField modality(final String modality) {
+ this.modality = notNull("modality", modality);
+ return this;
+ }
+
+ /**
+ * Sets the embedding model to use.
+ *
+ * @param model the model name (e.g., {@code "voyage-4-large"})
+ * @return this
+ * @since 5.8
+ */
+ public AutoEmbedField model(final String model) {
+ this.model = notNull("model", model);
+ return this;
+ }
+
+ /**
+ * Sets the number of dimensions for the auto-embedded vector.
+ *
+ * @param numDimensions the number of vector dimensions
+ * @return this
+ * @since 5.8
+ */
+ public AutoEmbedField numDimensions(final int numDimensions) {
+ this.numDimensions = numDimensions;
+ return this;
+ }
+
+ /**
+ * Sets the quantization type for the auto-embedded vector.
+ *
+ * Supported values:
+ *
+ * - {@code "float"}
+ * - {@code "scalar"}
+ * - {@code "binary"}
+ * - {@code "binaryNoRescore"}
+ *
+ *
+ * @param quantization the quantization type
+ * @return this
+ * @since 5.8
+ */
+ public AutoEmbedField quantization(final String quantization) {
+ this.quantization = notNull("quantization", quantization);
+ return this;
+ }
+
+ @Override
+ public BsonDocument toBsonDocument(final Class documentClass, final CodecRegistry codecRegistry) {
+ BsonDocument doc = new BsonDocument();
+ doc.append("type", new BsonString("autoEmbed"));
+ doc.append("path", new BsonString(path));
+ if (modality != null) {
+ doc.append("modality", new BsonString(modality));
+ }
+ if (model != null) {
+ doc.append("model", new BsonString(model));
+ }
+ if (numDimensions != null) {
+ doc.append("numDimensions", new BsonInt32(numDimensions));
+ }
+ if (quantization != null) {
+ doc.append("quantization", new BsonString(quantization));
+ }
+ return doc;
+ }
+
+ @Override
+ public String toString() {
+ return "AutoEmbedField{"
+ + "path='" + path + '\''
+ + ", modality='" + modality + '\''
+ + ", model='" + model + '\''
+ + ", numDimensions=" + numDimensions
+ + ", quantization='" + quantization + '\''
+ + '}';
+ }
+ }
+}
diff --git a/driver-core/src/test/unit/com/mongodb/client/model/SearchIndexDefinitionTest.java b/driver-core/src/test/unit/com/mongodb/client/model/SearchIndexDefinitionTest.java
new file mode 100644
index 00000000000..486cd38abe7
--- /dev/null
+++ b/driver-core/src/test/unit/com/mongodb/client/model/SearchIndexDefinitionTest.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright 2008-present MongoDB, Inc.
+ *
+ * Licensed 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 com.mongodb.client.model;
+
+import org.bson.BsonArray;
+import org.bson.BsonDocument;
+import org.bson.BsonInt32;
+import org.bson.BsonString;
+import org.bson.Document;
+import org.junit.jupiter.api.Test;
+
+import static com.mongodb.client.model.VectorSearchIndexFields.autoEmbedField;
+import static com.mongodb.client.model.VectorSearchIndexFields.filterField;
+import static com.mongodb.client.model.VectorSearchIndexFields.vectorField;
+import static java.util.Arrays.asList;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+final class SearchIndexDefinitionTest {
+
+ @Test
+ void vectorSearchWithSingleVectorField() {
+ VectorSearchIndexDefinition definition = SearchIndexDefinition.vectorSearch(
+ vectorField("plot_embedding")
+ .numDimensions(1536)
+ .similarity("euclidean")
+ );
+
+ assertEquals(
+ new BsonDocument("fields", new BsonArray(asList(
+ new BsonDocument("type", new BsonString("vector"))
+ .append("path", new BsonString("plot_embedding"))
+ .append("numDimensions", new BsonInt32(1536))
+ .append("similarity", new BsonString("euclidean"))
+ ))),
+ definition.toBsonDocument()
+ );
+ }
+
+ @Test
+ void vectorSearchWithFlatIndexingMethod() {
+ VectorSearchIndexDefinition definition = SearchIndexDefinition.vectorSearch(
+ vectorField("embedding")
+ .numDimensions(1536)
+ .similarity("euclidean")
+ .indexingMethod("flat"),
+ filterField("tenantId")
+ );
+
+ assertEquals(
+ new BsonDocument("fields", new BsonArray(asList(
+ new BsonDocument("type", new BsonString("vector"))
+ .append("path", new BsonString("embedding"))
+ .append("numDimensions", new BsonInt32(1536))
+ .append("similarity", new BsonString("euclidean"))
+ .append("indexingMethod", new BsonString("flat")),
+ new BsonDocument("type", new BsonString("filter"))
+ .append("path", new BsonString("tenantId"))
+ ))),
+ definition.toBsonDocument()
+ );
+ }
+
+ @Test
+ void vectorSearchWithHnswOptionsBuilder() {
+ VectorSearchIndexDefinition definition = SearchIndexDefinition.vectorSearch(
+ vectorField("embedding")
+ .numDimensions(768)
+ .similarity("cosine")
+ .indexingMethod("hnsw")
+ .hnswOptions(new HnswSearchIndexOptions().maxEdges(16).numEdgeCandidates(200))
+ );
+
+ assertEquals(
+ new BsonDocument("fields", new BsonArray(asList(
+ new BsonDocument("type", new BsonString("vector"))
+ .append("path", new BsonString("embedding"))
+ .append("numDimensions", new BsonInt32(768))
+ .append("similarity", new BsonString("cosine"))
+ .append("indexingMethod", new BsonString("hnsw"))
+ .append("hnswOptions", new BsonDocument("maxEdges", new BsonInt32(16))
+ .append("numEdgeCandidates", new BsonInt32(200)))
+ ))),
+ definition.toBsonDocument()
+ );
+ }
+
+ @Test
+ void vectorSearchWithRawBsonHnswOptions() {
+ VectorSearchIndexDefinition definition = SearchIndexDefinition.vectorSearch(
+ vectorField("embedding")
+ .numDimensions(1536)
+ .similarity("dotProduct")
+ .indexingMethod("hnsw")
+ .hnswOptions(new Document("maxEdges", 32).append("numEdgeCandidates", 400))
+ );
+
+ assertEquals(
+ new BsonDocument("fields", new BsonArray(asList(
+ new BsonDocument("type", new BsonString("vector"))
+ .append("path", new BsonString("embedding"))
+ .append("numDimensions", new BsonInt32(1536))
+ .append("similarity", new BsonString("dotProduct"))
+ .append("indexingMethod", new BsonString("hnsw"))
+ .append("hnswOptions", new BsonDocument("maxEdges", new BsonInt32(32))
+ .append("numEdgeCandidates", new BsonInt32(400)))
+ ))),
+ definition.toBsonDocument()
+ );
+ }
+
+ @Test
+ void vectorSearchWithAutoEmbedField() {
+ VectorSearchIndexDefinition definition = SearchIndexDefinition.vectorSearch(
+ autoEmbedField("product.description")
+ .modality("text")
+ .model("voyage-4-large")
+ .numDimensions(256)
+ .quantization("binary"),
+ filterField("script.author")
+ );
+
+ assertEquals(
+ new BsonDocument("fields", new BsonArray(asList(
+ new BsonDocument("type", new BsonString("autoEmbed"))
+ .append("path", new BsonString("product.description"))
+ .append("modality", new BsonString("text"))
+ .append("model", new BsonString("voyage-4-large"))
+ .append("numDimensions", new BsonInt32(256))
+ .append("quantization", new BsonString("binary")),
+ new BsonDocument("type", new BsonString("filter"))
+ .append("path", new BsonString("script.author"))
+ ))),
+ definition.toBsonDocument()
+ );
+ }
+
+ @Test
+ void vectorSearchWithListOfFields() {
+ VectorSearchIndexDefinition definition = SearchIndexDefinition.vectorSearch(asList(
+ vectorField("embedding")
+ .numDimensions(1536)
+ .similarity("euclidean"),
+ filterField("category")
+ ));
+
+ assertEquals(
+ new BsonDocument("fields", new BsonArray(asList(
+ new BsonDocument("type", new BsonString("vector"))
+ .append("path", new BsonString("embedding"))
+ .append("numDimensions", new BsonInt32(1536))
+ .append("similarity", new BsonString("euclidean")),
+ new BsonDocument("type", new BsonString("filter"))
+ .append("path", new BsonString("category"))
+ ))),
+ definition.toBsonDocument()
+ );
+ }
+
+ @Test
+ void vectorSearchWithRawBsonField() {
+ VectorSearchIndexDefinition definition = SearchIndexDefinition.vectorSearch(
+ new Document("type", "vector")
+ .append("path", "raw_field")
+ .append("numDimensions", 512)
+ .append("similarity", "cosine")
+ );
+
+ assertEquals(
+ new BsonDocument("fields", new BsonArray(asList(
+ new BsonDocument("type", new BsonString("vector"))
+ .append("path", new BsonString("raw_field"))
+ .append("numDimensions", new BsonInt32(512))
+ .append("similarity", new BsonString("cosine"))
+ ))),
+ definition.toBsonDocument()
+ );
+ }
+
+ @Test
+ void hnswSearchIndexOptionsEmpty() {
+ assertEquals(
+ new BsonDocument(),
+ new HnswSearchIndexOptions().toBsonDocument()
+ );
+ }
+
+ @Test
+ void hnswSearchIndexOptionsPartial() {
+ assertEquals(
+ new BsonDocument("maxEdges", new BsonInt32(24)),
+ new HnswSearchIndexOptions().maxEdges(24).toBsonDocument()
+ );
+ }
+
+ @Test
+ void filterFieldOnly() {
+ assertEquals(
+ new BsonDocument("type", new BsonString("filter"))
+ .append("path", new BsonString("status")),
+ filterField("status").toBsonDocument()
+ );
+ }
+
+ @Test
+ void vectorFieldMinimal() {
+ assertEquals(
+ new BsonDocument("type", new BsonString("vector"))
+ .append("path", new BsonString("vec")),
+ vectorField("vec").toBsonDocument()
+ );
+ }
+}
diff --git a/driver-scala/src/main/scala/org/mongodb/scala/model/package.scala b/driver-scala/src/main/scala/org/mongodb/scala/model/package.scala
index 0d23a38c2e8..9cd174bfdf0 100644
--- a/driver-scala/src/main/scala/org/mongodb/scala/model/package.scala
+++ b/driver-scala/src/main/scala/org/mongodb/scala/model/package.scala
@@ -481,6 +481,30 @@ package object model {
*/
type SearchIndexModel = com.mongodb.client.model.SearchIndexModel
+ /**
+ * A definition for an Atlas Search index.
+ * @since 5.8
+ */
+ type SearchIndexDefinition = com.mongodb.client.model.SearchIndexDefinition
+
+ /**
+ * A vector search index definition.
+ * @since 5.8
+ */
+ type VectorSearchIndexDefinition = com.mongodb.client.model.VectorSearchIndexDefinition
+
+ /**
+ * A factory for defining fields within a vector search index definition.
+ * @since 5.8
+ */
+ type VectorSearchIndexFields = com.mongodb.client.model.VectorSearchIndexFields
+
+ /**
+ * Options for the HNSW indexing method in a vector search index.
+ * @since 5.8
+ */
+ type HnswSearchIndexOptions = com.mongodb.client.model.HnswSearchIndexOptions
+
/**
* Represents an Atlas Search Index type, which is utilized for creating specific types of indexes.
*/