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 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 fields; + + VectorSearchIndexDefinition(final List 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. */