Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion evergreen/evergreen.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2099,7 +2099,7 @@ task_groups:
- "AWS_SESSION_TOKEN"
env:
CLUSTER_PREFIX: dbx-csharp-search-index
MONGODB_VERSION: "7.0"
MONGODB_VERSION: "8.0"
args:
- ${DRIVERS_TOOLS}/.evergreen/atlas/setup-atlas-cluster.sh
- command: expansions.update
Expand Down
48 changes: 37 additions & 11 deletions src/MongoDB.Driver/CreateSearchIndexModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@
namespace MongoDB.Driver
{
/// <summary>
/// Model for creating a search index.
/// Defines a vector search index model using a <see cref="BsonDocument"/> definition. Consider using
/// <see cref="CreateVectorIndexModel{TDocument}"/> to build Atlas indexes without specifying the BSON directly.
/// </summary>
public sealed class CreateSearchIndexModel
public class CreateSearchIndexModel
{
/// <summary>Gets the index name.</summary>
/// <value>The index name.</value>
Expand All @@ -32,26 +33,51 @@ public sealed class CreateSearchIndexModel

/// <summary>Gets the index definition.</summary>
/// <value>The definition.</value>
public BsonDocument Definition { get; }
public virtual BsonDocument Definition { get; }

/// <summary>
/// Initializes a new instance of the <see cref="CreateSearchIndexModel"/> class.
/// Initializes a new instance of the <see cref="CreateSearchIndexModel"/> class, passing the index
/// model as a <see cref="BsonDocument"/>.
/// </summary>
/// <param name="name">The name.</param>
/// <param name="definition">The definition.</param>
public CreateSearchIndexModel(string name, BsonDocument definition) : this(name, null, definition) { }
/// <remarks>
/// Consider using <see cref="CreateVectorIndexModel{TDocument}"/> to build Atlas indexes without specifying the
/// BSON directly.
/// </remarks>
/// <param name="name">The index name.</param>
/// <param name="definition">The index definition.</param>
public CreateSearchIndexModel(string name, BsonDocument definition)
: this(name, null, definition)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="CreateSearchIndexModel"/> class.
/// Initializes a new instance of the <see cref="CreateSearchIndexModel"/> class, passing the index
/// model as a <see cref="BsonDocument"/>.
/// </summary>
/// <param name="name">The name.</param>
/// <param name="type">The type.</param>
/// <param name="definition">The definition.</param>
/// <remarks>
/// Consider using <see cref="CreateVectorIndexModel{TDocument}"/> to build indexes without specifying the
/// BSON directly.
/// </remarks>
/// <param name="name">The index name.</param>
/// <param name="type">The index type.</param>
/// <param name="definition">The index definition.</param>
public CreateSearchIndexModel(string name, SearchIndexType? type, BsonDocument definition)
{
Name = name;
Type = type;
Definition = definition;

}

/// <summary>
/// Initializes a new instance of the <see cref="CreateSearchIndexModel"/> class.
/// </summary>
/// <param name="name">The index name.</param>
/// <param name="type">The index type.</param>
protected CreateSearchIndexModel(string name, SearchIndexType? type)
{
Name = name;
Type = type;
}
}
}
173 changes: 173 additions & 0 deletions src/MongoDB.Driver/CreateVectorIndexModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
/* Copyright 2010-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.
*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using MongoDB.Bson;

namespace MongoDB.Driver;

/// <summary>
/// Defines a vector index model using strongly-typed C# APIs.
/// </summary>
public sealed class CreateVectorIndexModel<TDocument> : CreateSearchIndexModel
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure, but should this class be named CreateVectorSearchIndexModel?

The server calls this type of index a "vector search index"?

{
/// <summary>
/// The field containing the vectors to index.
/// </summary>
public FieldDefinition<TDocument> Field { get; }

/// <summary>
/// The <see cref="VectorSimilarity"/> to use to search for top K-nearest neighbors.
/// </summary>
public VectorSimilarity Similarity { get; }

/// <summary>
/// Number of vector dimensions that vector search enforces at index-time and query-time.
/// </summary>
public int Dimensions { get; }

/// <summary>
/// Fields that may be used as filters in the vector query.
/// </summary>
public IReadOnlyList<FieldDefinition<TDocument>> FilterFields { get; }

/// <summary>
/// Type of automatic vector quantization for your vectors.
/// </summary>
public VectorQuantization? Quantization { get; init; }

/// <summary>
/// Maximum number of edges (or connections) that a node can have in the Hierarchical Navigable Small Worlds graph.
/// </summary>
public int? HnswMaxEdges { get; init; }

/// <summary>
/// Analogous to numCandidates at query-time, this parameter controls the maximum number of nodes to evaluate to find the closest neighbors to connect to a new node.
/// </summary>
public int? HnswNumEdgeCandidates { get; init; }

/// <summary>
/// This method should not be called on this subtype. Instead, call <see cref="Render"/> to create a BSON
/// document for the index model.
/// </summary>
public override BsonDocument Definition
=> throw new NotSupportedException(
"This method should not be called on this subtype. Instead, call 'Render' to create a BSON document for the index model.");

/// <summary>
/// Initializes a new instance of the <see cref="CreateVectorIndexModel{TDocument}"/> class, passing the
/// required options for <see cref="VectorSimilarity"/> and the number of vector dimensions to the constructor.
/// </summary>
/// <param name="name">The index name.</param>
/// <param name="field">The field containing the vectors to index.</param>
/// <param name="similarity">The <see cref="VectorSimilarity"/> to use to search for top K-nearest neighbors.</param>
/// <param name="dimensions">Number of vector dimensions that vector search enforces at index-time and query-time.</param>
/// <param name="filterFields">Fields that may be used as filters in the vector query.</param>
public CreateVectorIndexModel(
FieldDefinition<TDocument> field,
string name,
VectorSimilarity similarity,
int dimensions,
params FieldDefinition<TDocument>[] filterFields)
: base(name, SearchIndexType.VectorSearch)
{
Field = field;
Similarity = similarity;
Dimensions = dimensions;
FilterFields = filterFields?.ToList() ?? [];
}

/// <summary>
/// Initializes a new instance of the <see cref="CreateVectorIndexModel{TDocument}"/> class, passing the
/// required options for <see cref="VectorSimilarity"/> and the number of vector dimensions to the constructor.
/// </summary>
/// <param name="name">The index name.</param>
/// <param name="field">An expression pointing to the field containing the vectors to index.</param>
/// <param name="similarity">The <see cref="VectorSimilarity"/> to use to search for top K-nearest neighbors.</param>
/// <param name="dimensions">Number of vector dimensions that vector search enforces at index-time and query-time.</param>
/// <param name="filterFields">Expressions pointing to fields that may be used as filters in the vector query.</param>
public CreateVectorIndexModel(
Expression<Func<TDocument, object>> field,
string name,
VectorSimilarity similarity,
int dimensions,
params Expression<Func<TDocument, object>>[] filterFields)
: this(
new ExpressionFieldDefinition<TDocument>(field),
name,
similarity,
dimensions,
filterFields?
.Select(f => (FieldDefinition<TDocument>)new ExpressionFieldDefinition<TDocument>(f))
.ToArray())
{
}

/// <summary>
/// Renders the index model to a <see cref="BsonDocument"/>.
/// </summary>
/// <param name="renderArgs">The render arguments.</param>
/// <returns>A <see cref="BsonDocument" />.</returns>
public BsonDocument Render(RenderArgs<TDocument> renderArgs)
{
var similarityValue = Similarity == VectorSimilarity.DotProduct
? "dotProduct" // Because neither "DotProduct" or "dotproduct" are allowed.
: Similarity.ToString().ToLowerInvariant();

var vectorField = new BsonDocument
{
{ "type", BsonString.Create("vector") },
{ "path", Field.Render(renderArgs).FieldName },
{ "numDimensions", BsonInt32.Create(Dimensions) },
{ "similarity", BsonString.Create(similarityValue) },
};

if (Quantization.HasValue)
{
vectorField.Add("quantization", BsonString.Create(Quantization.ToString()?.ToLower()));
}

if (HnswMaxEdges != null || HnswNumEdgeCandidates != null)
{
var hnswDocument = new BsonDocument
{
{ "maxEdges", BsonInt32.Create(HnswMaxEdges ?? 16) },
{ "numEdgeCandidates", BsonInt32.Create(HnswNumEdgeCandidates ?? 100) }
};
vectorField.Add("hnswOptions", hnswDocument);
}

var fieldDocuments = new List<BsonDocument> { vectorField };

if (FilterFields != null)
{
foreach (var filterPath in FilterFields)
{
var fieldDocument = new BsonDocument
{
{ "type", BsonString.Create("filter") },
{ "path", BsonString.Create(filterPath.Render(renderArgs).FieldName) }
};

fieldDocuments.Add(fieldDocument);
}
}

return new BsonDocument { { "fields", BsonArray.Create(fieldDocuments) } };
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would use new BsonArray(fieldDocuments) instead of BsonArray.Create(fieldDocuments).

BsonArray.Create takes a single object parameter and does all sorts of weird obsolete mappings.

}
}
18 changes: 15 additions & 3 deletions src/MongoDB.Driver/MongoCollectionImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1741,10 +1741,22 @@ private PipelineDefinition<TDocument, BsonDocument> CreateListIndexesStage(strin
return new BsonDocumentStagePipelineDefinition<TDocument, BsonDocument>(new[] { stage });
}

private CreateSearchIndexesOperation CreateCreateIndexesOperation(IEnumerable<CreateSearchIndexModel> models) =>
new(_collection._collectionNamespace,
models.Select(m => new CreateSearchIndexRequest(m.Name, m.Type, m.Definition)),
private CreateSearchIndexesOperation CreateCreateIndexesOperation(
IEnumerable<CreateSearchIndexModel> models)
{
var renderArgs = _collection.GetRenderArgs();

return new CreateSearchIndexesOperation(
_collection._collectionNamespace,
models.Select(model
=> new CreateSearchIndexRequest(
model.Name,
model.Type,
model is CreateVectorIndexModel<TDocument> createVectorIndexModel
? createVectorIndexModel.Render(renderArgs)
: model.Definition)),
_collection._messageEncoderSettings);
}

private string[] GetIndexNames(BsonDocument createSearchIndexesResponse) =>
createSearchIndexesResponse["indexesCreated"]
Expand Down
42 changes: 42 additions & 0 deletions src/MongoDB.Driver/VectorQuantization.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/* Copyright 2010-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.
*/

namespace MongoDB.Driver;

/// <summary>
/// Type of automatic vector quantization for your vectors. Use this setting only if your embeddings are float
/// or double vectors. See <see href="https://www.mongodb.com/docs/atlas/atlas-vector-search/vector-quantization/">
/// Vector Quantization</see> for more information.
/// </summary>
public enum VectorQuantization
{
/// <summary>
/// Indicates no automatic quantization for the vector embeddings. Use this setting if you have pre-quantized
/// vectors for ingestion. If omitted, this is the default value.
/// </summary>
None,

/// <summary>
/// Indicates scalar quantization, which transforms values to 1 byte integers.
/// </summary>
Scalar,

/// <summary>
/// Indicates binary quantization, which transforms values to a single bit.
/// To use this value, numDimensions must be a multiple of 8.
/// If precision is critical, select <see cref="None"/> or <see cref="Scalar"/> instead of <see cref="Binary"/>.
/// </summary>
Binary,
}
39 changes: 39 additions & 0 deletions src/MongoDB.Driver/VectorSimilarity.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/* Copyright 2010-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.
*/

namespace MongoDB.Driver;

/// <summary>
/// Vector similarity function to use to search for top K-nearest neighbors.
/// See <see href="https://www.mongodb.com/docs/atlas/atlas-vector-search/vector-search-type/">How to Index Fields for
/// Vector Search</see> for more information.
/// </summary>
public enum VectorSimilarity
{
/// <summary>
/// Measures the distance between ends of vectors.
/// </summary>
Euclidean,

/// <summary>
/// Measures similarity based on the angle between vectors.
/// </summary>
Cosine,

/// <summary>
/// Measures similarity like cosine, but takes into account the magnitude of the vector.
/// </summary>
DotProduct,
}
Loading
Loading