diff --git a/driver-core/src/main/com/mongodb/client/model/VectorSearchIndexDefinition.java b/driver-core/src/main/com/mongodb/client/model/VectorSearchIndexDefinition.java index 25e87ce113..12b90751e0 100644 --- a/driver-core/src/main/com/mongodb/client/model/VectorSearchIndexDefinition.java +++ b/driver-core/src/main/com/mongodb/client/model/VectorSearchIndexDefinition.java @@ -16,6 +16,7 @@ package com.mongodb.client.model; +import com.mongodb.lang.Nullable; import org.bson.BsonArray; import org.bson.BsonDocument; import org.bson.codecs.configuration.CodecRegistry; @@ -38,10 +39,32 @@ */ public final class VectorSearchIndexDefinition implements SearchIndexDefinition { private final List fields; + @Nullable + private final Bson storedSource; VectorSearchIndexDefinition(final List fields) { + this(fields, null); + } + + VectorSearchIndexDefinition(final List fields, @Nullable final Bson storedSource) { doesNotContainNull("fields", notNull("fields", fields)); this.fields = new ArrayList<>(fields); + this.storedSource = storedSource; + } + + /** + * Creates a new {@link VectorSearchIndexDefinition} with the specified stored source configuration. + * + *

The stored source configuration controls which fields are stored in the index + * and can be returned without reading the full document from the collection.

+ * + * @param storedSource a document specifying the stored source configuration, + * e.g., {@code {"include": ["field1", "field2"]}} or {@code {"exclude": ["field3"]}} + * @return a new {@link VectorSearchIndexDefinition} with the stored source configuration + * @since 5.8 + */ + public VectorSearchIndexDefinition storedSource(final Bson storedSource) { + return new VectorSearchIndexDefinition(this.fields, notNull("storedSource", storedSource)); } @Override @@ -50,13 +73,18 @@ public BsonDocument toBsonDocument(final Class documentCl for (Bson field : fields) { fieldArray.add(field.toBsonDocument(documentClass, codecRegistry)); } - return new BsonDocument("fields", fieldArray); + BsonDocument document = new BsonDocument("fields", fieldArray); + if (storedSource != null) { + document.append("storedSource", storedSource.toBsonDocument(documentClass, codecRegistry)); + } + return document; } @Override public String toString() { return "VectorSearchIndexDefinition{" + "fields=" + fields + + ", storedSource=" + storedSource + '}'; } } diff --git a/driver-core/src/main/com/mongodb/client/model/search/VectorSearchConstructibleBson.java b/driver-core/src/main/com/mongodb/client/model/search/VectorSearchConstructibleBson.java index 3e28189082..39a043ca82 100644 --- a/driver-core/src/main/com/mongodb/client/model/search/VectorSearchConstructibleBson.java +++ b/driver-core/src/main/com/mongodb/client/model/search/VectorSearchConstructibleBson.java @@ -17,6 +17,7 @@ import com.mongodb.annotations.Immutable; import com.mongodb.internal.client.model.AbstractConstructibleBson; +import org.bson.BsonBoolean; import org.bson.BsonDocument; import org.bson.Document; import org.bson.conversions.Bson; @@ -45,7 +46,12 @@ protected VectorSearchConstructibleBson newSelf(final Bson base, final Document @Override public VectorSearchOptions filter(final Bson filter) { - return newAppended("filter", notNull("name", filter)); + return newAppended("filter", notNull("filter", filter)); + } + + @Override + public VectorSearchOptions returnStoredSource(final boolean returnStoredSource) { + return newAppended("returnStoredSource", new BsonBoolean(returnStoredSource)); } @Override diff --git a/driver-core/src/main/com/mongodb/client/model/search/VectorSearchOptions.java b/driver-core/src/main/com/mongodb/client/model/search/VectorSearchOptions.java index 073c05b237..d3bcf3aea4 100644 --- a/driver-core/src/main/com/mongodb/client/model/search/VectorSearchOptions.java +++ b/driver-core/src/main/com/mongodb/client/model/search/VectorSearchOptions.java @@ -41,6 +41,16 @@ public interface VectorSearchOptions extends Bson { */ VectorSearchOptions filter(Bson filter); + /** + * Creates a new {@link VectorSearchOptions} that instructs to return only stored source fields. + * + * @param returnStoredSource The option to return only stored source fields. + * @return A new {@link VectorSearchOptions}. + * @mongodb.atlas.manual atlas-vector-search/vector-search-stage/ $vectorSearch + * @since 5.8 + */ + VectorSearchOptions returnStoredSource(boolean returnStoredSource); + /** * Creates a new {@link VectorSearchOptions} with the specified option in situations when there is no builder method * that better satisfies your needs. 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 index 87633f842f..31c34d82aa 100644 --- a/driver-core/src/test/unit/com/mongodb/client/model/SearchIndexDefinitionTest.java +++ b/driver-core/src/test/unit/com/mongodb/client/model/SearchIndexDefinitionTest.java @@ -141,4 +141,36 @@ void vectorSearchRejectsEmptyVarargs() { void vectorSearchRejectsEmptyList() { assertThrows(IllegalArgumentException.class, () -> SearchIndexDefinition.vectorSearch(emptyList())); } + + @Test + void vectorSearchWithStoredSource() { + VectorSearchIndexDefinition definition = SearchIndexDefinition.vectorSearch( + vectorField("embedding") + .numDimensions(1536) + .similarity("cosine") + ).storedSource(new Document("include", asList("plot", "title"))); + + 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("cosine")) + ))).append("storedSource", new BsonDocument("include", new BsonArray(asList( + new BsonString("plot"), + new BsonString("title") + )))), + definition.toBsonDocument() + ); + } + + @Test + void vectorSearchStoredSourceRejectsNull() { + VectorSearchIndexDefinition definition = SearchIndexDefinition.vectorSearch( + vectorField("embedding") + .numDimensions(1536) + .similarity("cosine") + ); + assertThrows(IllegalArgumentException.class, () -> definition.storedSource(null)); + } } diff --git a/driver-core/src/test/unit/com/mongodb/client/model/search/BinaryVectorSearchOptionsTest.java b/driver-core/src/test/unit/com/mongodb/client/model/search/BinaryVectorSearchOptionsTest.java index 1fde037dbe..952974b8ed 100644 --- a/driver-core/src/test/unit/com/mongodb/client/model/search/BinaryVectorSearchOptionsTest.java +++ b/driver-core/src/test/unit/com/mongodb/client/model/search/BinaryVectorSearchOptionsTest.java @@ -110,6 +110,30 @@ void optionsExact() { ); } + @Test + void returnStoredSourceApproximate() { + assertEquals( + new BsonDocument() + .append("returnStoredSource", new BsonBoolean(true)) + .append("numCandidates", new BsonInt64(1)), + VectorSearchOptions.approximateVectorSearchOptions(1) + .returnStoredSource(true) + .toBsonDocument() + ); + } + + @Test + void returnStoredSourceExact() { + assertEquals( + new BsonDocument() + .append("returnStoredSource", new BsonBoolean(true)) + .append("exact", new BsonBoolean(true)), + VectorSearchOptions.exactVectorSearchOptions() + .returnStoredSource(true) + .toBsonDocument() + ); + } + @Test void approximateVectorSearchOptionsIsUnmodifiable() { String expected = VectorSearchOptions.approximateVectorSearchOptions(1).toBsonDocument().toJson(); diff --git a/driver-scala/src/test/scala/org/mongodb/scala/model/SearchIndexDefinitionSpec.scala b/driver-scala/src/test/scala/org/mongodb/scala/model/SearchIndexDefinitionSpec.scala index fd36ef5246..efbe29917f 100644 --- a/driver-scala/src/test/scala/org/mongodb/scala/model/SearchIndexDefinitionSpec.scala +++ b/driver-scala/src/test/scala/org/mongodb/scala/model/SearchIndexDefinitionSpec.scala @@ -77,6 +77,20 @@ class SearchIndexDefinitionSpec extends BaseSpec { ) } + it should "create a vectorSearch definition with storedSource" in { + toBson( + vectorSearch( + vectorField("embedding").numDimensions(1536).similarity("cosine") + ).storedSource(Document("include" -> List("plot", "title"))) + ) should equal( + Document( + """{"fields": [ + |{"type": "vector", "path": "embedding", "numDimensions": 1536, "similarity": "cosine"} + |], "storedSource": {"include": ["plot", "title"]}}""".stripMargin.replaceAll("\n", " ") + ) + ) + } + it should "create a SearchIndexModel with VectorSearchIndexDefinition" in { val definition = vectorSearch( vectorField("embedding").numDimensions(1536).similarity("cosine")