From cf85836c892ebe052d6c92fea58af6625c0303dd Mon Sep 17 00:00:00 2001 From: Chris Price Date: Thu, 18 Apr 2024 15:55:46 -0700 Subject: [PATCH] feat: remove vector client --- .../Config/IVectorIndexConfiguration.cs | 22 - .../IVectorIndexTransportStrategy.cs | 36 -- .../StaticVectorIndexTransportStrategy.cs | 69 --- .../Config/VectorIndexConfiguration.cs | 53 -- .../Config/VectorIndexConfigurations.cs | 58 -- src/Momento.Sdk/IPreviewVectorIndexClient.cs | 286 --------- .../ExtensionMethods/DictionaryExtensions.cs | 50 -- .../Internal/VectorIndexControlClient.cs | 149 ----- .../Internal/VectorIndexControlGrpcManager.cs | 81 --- .../Internal/VectorIndexDataClient.cs | 368 ----------- .../Internal/VectorIndexDataGrpcManager.cs | 107 ---- .../Messages/Vector/MetadataValue.cs | 280 --------- src/Momento.Sdk/Momento.Sdk.csproj | 1 + src/Momento.Sdk/PreviewVectorIndexClient.cs | 110 ---- src/Momento.Sdk/Requests/Vector/Item.cs | 79 --- .../Requests/Vector/MetadataFields.cs | 56 -- .../Requests/Vector/SimilarityMetric.cs | 27 - .../Responses/Vector/CountItemsResponse.cs | 80 --- .../Responses/Vector/CreateIndexResponse.cs | 69 --- .../Responses/Vector/DeleteIndexResponse.cs | 61 -- .../Vector/DeleteItemBatchResponse.cs | 58 -- .../Responses/Vector/GetItemBatchResponse.cs | 139 ----- .../Vector/GetItemMetadataResponse.cs | 117 ---- src/Momento.Sdk/Responses/Vector/IndexInfo.cs | 65 -- .../Responses/Vector/ListIndexesResponse.cs | 81 --- .../Vector/SearchAndFetchVectorsResponse.cs | 83 --- src/Momento.Sdk/Responses/Vector/SearchHit.cs | 150 ----- .../Responses/Vector/SearchResponse.cs | 83 --- .../Vector/UpsertItemBatchResponse.cs | 58 -- .../Integration/Momento.Sdk.Tests/Fixtures.cs | 36 -- tests/Integration/Momento.Sdk.Tests/Utils.cs | 48 -- .../VectorIndexControlTest.cs | 88 --- .../Momento.Sdk.Tests/VectorIndexDataTest.cs | 586 ------------------ 33 files changed, 1 insertion(+), 3633 deletions(-) delete mode 100644 src/Momento.Sdk/Config/IVectorIndexConfiguration.cs delete mode 100644 src/Momento.Sdk/Config/Transport/IVectorIndexTransportStrategy.cs delete mode 100644 src/Momento.Sdk/Config/Transport/StaticVectorIndexTransportStrategy.cs delete mode 100644 src/Momento.Sdk/Config/VectorIndexConfiguration.cs delete mode 100644 src/Momento.Sdk/Config/VectorIndexConfigurations.cs delete mode 100644 src/Momento.Sdk/IPreviewVectorIndexClient.cs delete mode 100644 src/Momento.Sdk/Internal/VectorIndexControlClient.cs delete mode 100644 src/Momento.Sdk/Internal/VectorIndexControlGrpcManager.cs delete mode 100644 src/Momento.Sdk/Internal/VectorIndexDataClient.cs delete mode 100644 src/Momento.Sdk/Internal/VectorIndexDataGrpcManager.cs delete mode 100644 src/Momento.Sdk/Messages/Vector/MetadataValue.cs delete mode 100644 src/Momento.Sdk/PreviewVectorIndexClient.cs delete mode 100644 src/Momento.Sdk/Requests/Vector/Item.cs delete mode 100644 src/Momento.Sdk/Requests/Vector/MetadataFields.cs delete mode 100644 src/Momento.Sdk/Requests/Vector/SimilarityMetric.cs delete mode 100644 src/Momento.Sdk/Responses/Vector/CountItemsResponse.cs delete mode 100644 src/Momento.Sdk/Responses/Vector/CreateIndexResponse.cs delete mode 100644 src/Momento.Sdk/Responses/Vector/DeleteIndexResponse.cs delete mode 100644 src/Momento.Sdk/Responses/Vector/DeleteItemBatchResponse.cs delete mode 100644 src/Momento.Sdk/Responses/Vector/GetItemBatchResponse.cs delete mode 100644 src/Momento.Sdk/Responses/Vector/GetItemMetadataResponse.cs delete mode 100644 src/Momento.Sdk/Responses/Vector/IndexInfo.cs delete mode 100644 src/Momento.Sdk/Responses/Vector/ListIndexesResponse.cs delete mode 100644 src/Momento.Sdk/Responses/Vector/SearchAndFetchVectorsResponse.cs delete mode 100644 src/Momento.Sdk/Responses/Vector/SearchHit.cs delete mode 100644 src/Momento.Sdk/Responses/Vector/SearchResponse.cs delete mode 100644 src/Momento.Sdk/Responses/Vector/UpsertItemBatchResponse.cs delete mode 100644 tests/Integration/Momento.Sdk.Tests/VectorIndexControlTest.cs delete mode 100644 tests/Integration/Momento.Sdk.Tests/VectorIndexDataTest.cs diff --git a/src/Momento.Sdk/Config/IVectorIndexConfiguration.cs b/src/Momento.Sdk/Config/IVectorIndexConfiguration.cs deleted file mode 100644 index f772caec..00000000 --- a/src/Momento.Sdk/Config/IVectorIndexConfiguration.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Microsoft.Extensions.Logging; -using Momento.Sdk.Config.Transport; - -namespace Momento.Sdk.Config; - -/// -/// Contract for the Vector Index SDK configurables. -/// -public interface IVectorIndexConfiguration -{ - /// - public ILoggerFactory LoggerFactory { get; } - /// - public IVectorIndexTransportStrategy TransportStrategy { get; } - - /// - /// Creates a new instance of the VectorIndexConfiguration object, updated to use the specified transport strategy. - /// - /// This is responsible for configuring network tunables. - /// A VectorIndexConfiguration object using the provided transport strategy. - public IVectorIndexConfiguration WithTransportStrategy(IVectorIndexTransportStrategy transportStrategy); -} diff --git a/src/Momento.Sdk/Config/Transport/IVectorIndexTransportStrategy.cs b/src/Momento.Sdk/Config/Transport/IVectorIndexTransportStrategy.cs deleted file mode 100644 index ebaa158a..00000000 --- a/src/Momento.Sdk/Config/Transport/IVectorIndexTransportStrategy.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; - -namespace Momento.Sdk.Config.Transport; - -/// -/// This is responsible for configuring network tunables for the vector index client. -/// -public interface IVectorIndexTransportStrategy -{ - /// - /// Configures the low-level gRPC settings for the Momento Vector Index client's communication - /// with the Momento server. - /// - public IGrpcConfiguration GrpcConfig { get; } - - /// - /// Copy constructor to update the gRPC configuration - /// - /// - /// A new IVectorIndexTransportStrategy with the specified grpcConfig - public IVectorIndexTransportStrategy WithGrpcConfig(IGrpcConfiguration grpcConfig); - - /// - /// Copy constructor to update the client timeout - /// - /// - /// A new IVectorIndexTransportStrategy with the specified client timeout - public IVectorIndexTransportStrategy WithClientTimeout(TimeSpan clientTimeout); - - /// - /// Copy constructor to update the SocketsHttpHandler's options - /// - /// - /// - public IVectorIndexTransportStrategy WithSocketsHttpHandlerOptions(SocketsHttpHandlerOptions options); -} diff --git a/src/Momento.Sdk/Config/Transport/StaticVectorIndexTransportStrategy.cs b/src/Momento.Sdk/Config/Transport/StaticVectorIndexTransportStrategy.cs deleted file mode 100644 index 46a708b3..00000000 --- a/src/Momento.Sdk/Config/Transport/StaticVectorIndexTransportStrategy.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System; -using Microsoft.Extensions.Logging; - -namespace Momento.Sdk.Config.Transport; - -/// -/// The simplest way to configure the transport layer for the Momento Vector Index client. -/// Provides static values for the gRPC configuration. -/// -public class StaticVectorIndexTransportStrategy : IVectorIndexTransportStrategy -{ - private readonly ILoggerFactory _loggerFactory; - - /// - public IGrpcConfiguration GrpcConfig { get; } - - /// - /// Configures the transport layer for the Momento client. - /// - /// - /// Configures how Momento Vector Index client interacts with the Momento service via gRPC - public StaticVectorIndexTransportStrategy(ILoggerFactory loggerFactory, IGrpcConfiguration grpcConfig) - { - _loggerFactory = loggerFactory; - GrpcConfig = grpcConfig; - } - - /// - public IVectorIndexTransportStrategy WithGrpcConfig(IGrpcConfiguration grpcConfig) - { - return new StaticVectorIndexTransportStrategy(_loggerFactory, grpcConfig); - } - - /// - public IVectorIndexTransportStrategy WithClientTimeout(TimeSpan clientTimeout) - { - return new StaticVectorIndexTransportStrategy(_loggerFactory, GrpcConfig.WithDeadline(clientTimeout)); - } - - public IVectorIndexTransportStrategy WithSocketsHttpHandlerOptions(SocketsHttpHandlerOptions options) - { - return new StaticVectorIndexTransportStrategy(_loggerFactory, GrpcConfig.WithSocketsHttpHandlerOptions(options)); - } - - /// - /// Test equality by value. - /// - /// - /// - public override bool Equals(object obj) - { - if ((obj == null) || !this.GetType().Equals(obj.GetType())) - { - return false; - } - - var other = (StaticVectorIndexTransportStrategy)obj; - return GrpcConfig.Equals(other.GrpcConfig); - } - - /// - /// Trivial hash code implementation. - /// - /// - public override int GetHashCode() - { - return base.GetHashCode(); - } -} diff --git a/src/Momento.Sdk/Config/VectorIndexConfiguration.cs b/src/Momento.Sdk/Config/VectorIndexConfiguration.cs deleted file mode 100644 index 1c021965..00000000 --- a/src/Momento.Sdk/Config/VectorIndexConfiguration.cs +++ /dev/null @@ -1,53 +0,0 @@ -using Microsoft.Extensions.Logging; -using Momento.Sdk.Config.Transport; - -namespace Momento.Sdk.Config; - -/// -public class VectorIndexConfiguration : IVectorIndexConfiguration -{ - /// - public ILoggerFactory LoggerFactory { get; } - /// - public IVectorIndexTransportStrategy TransportStrategy { get; } - - - /// - /// Create a new instance of a Vector Index Configuration object with provided arguments: - /// , - /// and - /// - /// This is responsible for configuring network tunables. - /// This is responsible for configuring logging. - public VectorIndexConfiguration(ILoggerFactory loggerFactory, IVectorIndexTransportStrategy transportStrategy) - { - LoggerFactory = loggerFactory; - TransportStrategy = transportStrategy; - } - - /// - public IVectorIndexConfiguration WithTransportStrategy(IVectorIndexTransportStrategy transportStrategy) - { - return new VectorIndexConfiguration(LoggerFactory, transportStrategy); - } - - /// - public override bool Equals(object obj) - { - if ((obj == null) || !this.GetType().Equals(obj.GetType())) - { - return false; - } - - var other = (VectorIndexConfiguration)obj; - return TransportStrategy.Equals(other.TransportStrategy) && - LoggerFactory.Equals(other.LoggerFactory); - } - - /// - public override int GetHashCode() - { - return base.GetHashCode(); - } - -} diff --git a/src/Momento.Sdk/Config/VectorIndexConfigurations.cs b/src/Momento.Sdk/Config/VectorIndexConfigurations.cs deleted file mode 100644 index 9a3c19d7..00000000 --- a/src/Momento.Sdk/Config/VectorIndexConfigurations.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; -using Momento.Sdk.Config.Transport; - -namespace Momento.Sdk.Config; - -/// -/// Provide pre-built vector index client configurations. -/// -public class VectorIndexConfigurations -{ - /// - /// Laptop config provides defaults suitable for a medium-to-high-latency dev environment. Permissive timeouts, retries, and - /// relaxed latency and throughput targets. - /// - public class Laptop : VectorIndexConfiguration - { - private Laptop(ILoggerFactory loggerFactory, IVectorIndexTransportStrategy transportStrategy) - : base(loggerFactory, transportStrategy) - { - - } - - /// - /// Provides the latest recommended configuration for a Laptop environment. - /// - /// - /// This configuration may change in future releases to take advantage of - /// improvements we identify for default configurations. - /// - /// - /// - public static IVectorIndexConfiguration Latest(ILoggerFactory? loggerFactory = null) - { - return V1(loggerFactory); - } - - /// - /// Provides version 1 configuration for a Laptop environment. - /// - /// - /// This configuration is guaranteed not to change in future - /// releases of the Momento .NET SDK. - /// - /// - /// - public static IVectorIndexConfiguration V1(ILoggerFactory? loggerFactory = null) - { - var finalLoggerFactory = loggerFactory ?? NullLoggerFactory.Instance; - IVectorIndexTransportStrategy transportStrategy = new StaticVectorIndexTransportStrategy( - loggerFactory: finalLoggerFactory, - grpcConfig: new StaticGrpcConfiguration(deadline: TimeSpan.FromMilliseconds(15000)) - ); - return new Laptop(finalLoggerFactory, transportStrategy); - } - } -} diff --git a/src/Momento.Sdk/IPreviewVectorIndexClient.cs b/src/Momento.Sdk/IPreviewVectorIndexClient.cs deleted file mode 100644 index c2c0d955..00000000 --- a/src/Momento.Sdk/IPreviewVectorIndexClient.cs +++ /dev/null @@ -1,286 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Momento.Sdk.Requests.Vector; -using Momento.Sdk.Responses.Vector; - -namespace Momento.Sdk; - -/// -/// PREVIEW Vector Index Client -/// WARNING: the API for this client is not yet stable and may change without notice. -/// -/// Includes control operations and data operations. -/// -public interface IPreviewVectorIndexClient : IDisposable -{ - /// - /// Creates a vector index if it does not exist. - /// - /// The vector index to be created. - /// The number of dimensions per vector - /// The metric used to quantify - /// the distance between vectors. Can be cosine similarity, - /// inner product, or euclidean similarity. Defaults to cosine similarity. - /// - /// Task representing the result of the create vector index operation. This result - /// is resolved to a type-safe object of one of - /// the following subtypes: - /// - /// CreateIndexResponse.Success - /// CreateIndexResponse.AlreadyExists - /// CreateIndexResponse.Error - /// - /// Pattern matching can be used to operate on the appropriate subtype. - /// For example: - /// - /// if (response is CreateIndexResponse.Error errorResponse) - /// { - /// // handle error as appropriate - /// } - /// - /// - /// Remark on the choice of similarity metric: - /// - /// - /// Cosine similarity is appropriate for most embedding models as they tend to be optimized for this metric. - /// - /// - /// If the vectors are unit normalized, cosine similarity is equivalent to inner product. - /// If your vectors are already unit normalized, you can use inner product to improve performance. - /// - /// - /// Euclidean similarity, the sum of squared differences, is appropriate for datasets where - /// this metric is meaningful. For example, if the vectors represent images, and the embedding - /// model is trained to optimize the euclidean distance between images, then euclidean - /// similarity is appropriate. - /// - /// - /// - /// - public Task CreateIndexAsync(string indexName, long numDimensions, SimilarityMetric similarityMetric = SimilarityMetric.CosineSimilarity); - - /// - /// Lists all vector indexes. - /// - /// - /// Task representing the result of the list vector index operation. The - /// response object is resolved to a type-safe object of one of - /// the following subtypes: - /// - /// ListIndexesResponse.Success - /// ListIndexesResponse.Error - /// - /// Pattern matching can be used to operate on the appropriate subtype. - /// For example: - /// - /// if (response is ListIndexesResponse.Error errorResponse) - /// { - /// // handle error as appropriate - /// } - /// - /// - public Task ListIndexesAsync(); - - /// - /// Deletes a vector index and all the vectors within it. - /// - /// The name of the vector index to delete. - /// - /// Task representing the result of the delete vector index operation. The - /// response object is resolved to a type-safe object of one of - /// the following subtypes: - /// - /// DeleteIndexResponse.Success - /// DeleteIndexResponse.Error - /// - /// Pattern matching can be used to operate on the appropriate subtype. - /// For example: - /// - /// if (response is DeleteIndexResponse.Error errorResponse) - /// { - /// // handle error as appropriate - /// } - /// - /// - public Task DeleteIndexAsync(string indexName); - - /// - /// Gets the number of items in a vector index. - /// - /// - /// In the event the index does not exist, the response will be an error. - /// A count of zero is reserved for an index that exists but has no items. - /// - /// The name of the vector index to get the item count from. - /// Task representing the result of the count items operation. - public Task CountItemsAsync(string indexName); - - /// - /// Upserts a batch of items into a vector index. - /// If an item with the same ID already exists in the index, it will be replaced. - /// Otherwise, it will be added to the index. - /// - /// The name of the vector index to upsert the items into. - /// The items to upsert into the index. - /// - /// Task representing the result of the upsert operation. The - /// response object is resolved to a type-safe object of one of - /// the following subtypes: - /// - /// UpsertItemBatchResponse.Success - /// UpsertItemBatchResponse.Error - /// - /// Pattern matching can be used to operate on the appropriate subtype. - /// For example: - /// - /// if (response is UpsertItemBatchResponse.Error errorResponse) - /// { - /// // handle error as appropriate - /// } - /// - /// - public Task UpsertItemBatchAsync(string indexName, - IEnumerable items); - - - /// - /// Gets a batch of items from a vector index by ID. - /// - /// The name of the vector index to get the items from. - /// The IDs of the items to get from the index. - /// - /// Task representing the result of the get item batch operation. The - /// response object is resolved to a type-safe object of one of - /// the following subtypes: - /// - /// GetItemBatchResponse.Success - /// GetItemBatchResponse.Error - /// - /// Pattern matching can be used to operate on the appropriate subtype. - /// For example: - /// - /// if (response is GetItemBatchResponse.Error errorResponse) - /// { - /// // handle error as appropriate - /// return; - /// } - /// - /// - public Task GetItemBatchAsync(string indexName, IEnumerable filter); - - /// - /// Gets metadata for a batch of items from a vector index by ID. - /// - /// The name of the vector index to get the item metadata from. - /// The IDs of the item metadata to get from the index. - /// - /// Task representing the result of the get item metadata batch operation. The - /// response object is resolved to a type-safe object of one of - /// the following subtypes: - /// - /// GetItemMetadataBatchResponse.Success - /// GetItemMetadataBatchResponse.Error - /// - /// Pattern matching can be used to operate on the appropriate subtype. - /// For example: - /// - /// if (response is GetItemMetadataBatchResponse.Error errorResponse) - /// { - /// // handle error as appropriate - /// return; - /// } - /// - /// - public Task GetItemMetadataBatchAsync(string indexName, IEnumerable filter); - - /// - /// Deletes all items with the given IDs from the index. Returns success if for items that do not exist. - /// - /// The name of the vector index to delete the items from. - /// The IDs of the items to delete from the index. - /// - /// Task representing the result of the upsert operation. The - /// response object is resolved to a type-safe object of one of - /// the following subtypes: - /// - /// DeleteItemBatchResponse.Success - /// DeleteItemBatchResponse.Error - /// - /// Pattern matching can be used to operate on the appropriate subtype. - /// For example: - /// - /// if (response is DeleteItemBatchResponse.Error errorResponse) - /// { - /// // handle error as appropriate - /// } - /// - /// - public Task DeleteItemBatchAsync(string indexName, IEnumerable filter); - - /// - /// Searches for the most similar vectors to the query vector in the index. - /// Ranks the vectors according to the similarity metric specified when the - /// index was created. - /// - /// The name of the vector index to search in. - /// The vector to search for. - /// The number of results to return. Defaults to 10. - /// A list of metadata fields to return with each result. - /// A score threshold to filter results by. For cosine - /// similarity and inner product, scores lower than the threshold are excluded. For - /// euclidean similarity, scores higher than the threshold are excluded. The threshold - /// is exclusive. Defaults to None, ie no threshold. - /// - /// Task representing the result of the upsert operation. The - /// response object is resolved to a type-safe object of one of - /// the following subtypes: - /// - /// SearchResponse.Success - /// SearchResponse.Error - /// - /// Pattern matching can be used to operate on the appropriate subtype. - /// For example: - /// - /// if (response is SearchResponse.Error errorResponse) - /// { - /// // handle error as appropriate - /// } - /// - /// - public Task SearchAsync(string indexName, IEnumerable queryVector, int topK = 10, - MetadataFields? metadataFields = null, float? scoreThreshold = null); - - /// - /// Searches for the most similar vectors to the query vector in the index. - /// Ranks the vectors according to the similarity metric specified when the - /// index was created. Also returns the vectors associated with each result. - /// - /// The name of the vector index to search in. - /// The vector to search for. - /// The number of results to return. Defaults to 10. - /// A list of metadata fields to return with each result. - /// A score threshold to filter results by. For cosine - /// similarity and inner product, scores lower than the threshold are excluded. For - /// euclidean similarity, scores higher than the threshold are excluded. The threshold - /// is exclusive. Defaults to None, ie no threshold. - /// - /// Task representing the result of the upsert operation. The - /// response object is resolved to a type-safe object of one of - /// the following subtypes: - /// - /// SearchResponse.Success - /// SearchResponse.Error - /// - /// Pattern matching can be used to operate on the appropriate subtype. - /// For example: - /// - /// if (response is SearchResponse.Error errorResponse) - /// { - /// // handle error as appropriate - /// } - /// - /// - public Task SearchAndFetchVectorsAsync(string indexName, IEnumerable queryVector, int topK = 10, - MetadataFields? metadataFields = null, float? scoreThreshold = null); -} diff --git a/src/Momento.Sdk/Internal/ExtensionMethods/DictionaryExtensions.cs b/src/Momento.Sdk/Internal/ExtensionMethods/DictionaryExtensions.cs index ca1cf535..216aca32 100644 --- a/src/Momento.Sdk/Internal/ExtensionMethods/DictionaryExtensions.cs +++ b/src/Momento.Sdk/Internal/ExtensionMethods/DictionaryExtensions.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Linq; -using Momento.Sdk.Messages.Vector; namespace Momento.Sdk.Internal.ExtensionMethods; @@ -85,52 +84,3 @@ public static void AddRange(this IDictionary diction } } - -/// -/// Defines extension methods to operate on dictionaries with -/// string keys and values. -/// -public static class MetadataDictionaryExtensions -{ - /// - /// Test whether two metadata dictionaries contain the same content. - /// - /// The LHS dictionary. - /// The RHS dictionary. - /// - public static bool MetadataEquals(this IDictionary dictionary, IDictionary other) - { - if (dictionary == null && other == null) - { - return true; - } - else if (dictionary == null || other == null) - { - return false; - } - else if (dictionary.Count != other.Count) - { - return false; - } - - var keySet = new HashSet(dictionary.Keys); - return other.All(kv => keySet.Contains(kv.Key) && dictionary[kv.Key].Equals(kv.Value)); - } - - /// - public static int MetadataHashCode(this IDictionary dictionary) - { - unchecked // Overflow is fine, just wrap - { - var hash = 17; - - foreach (var pair in dictionary) - { - hash = hash * 23 + pair.Key.GetHashCode(); - hash = hash * 23 + (pair.Value?.GetHashCode() ?? 0); - } - - return hash; - } - } -} diff --git a/src/Momento.Sdk/Internal/VectorIndexControlClient.cs b/src/Momento.Sdk/Internal/VectorIndexControlClient.cs deleted file mode 100644 index 2f77553d..00000000 --- a/src/Momento.Sdk/Internal/VectorIndexControlClient.cs +++ /dev/null @@ -1,149 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Grpc.Core; -using Microsoft.Extensions.Logging; -using Momento.Protos.ControlClient; -using Momento.Sdk.Exceptions; -using Momento.Sdk.Requests.Vector; -using Momento.Sdk.Responses.Vector; -using Momento.Sdk.Config; -using Momento.Sdk.Config.Transport; - -namespace Momento.Sdk.Internal; - -internal sealed class VectorIndexControlClient : IDisposable -{ - private readonly VectorIndexControlGrpcManager grpcManager; - private readonly TimeSpan deadline = TimeSpan.FromSeconds(60); - - private readonly ILogger _logger; - private readonly CacheExceptionMapper _exceptionMapper; - - public VectorIndexControlClient(IVectorIndexConfiguration config, string authToken, string endpoint) - { - // Override the sockets http handler options to disable keepalive - var overrideKeepalive = SocketsHttpHandlerOptions.Of( - pooledConnectionIdleTimeout: config.TransportStrategy.GrpcConfig.SocketsHttpHandlerOptions.PooledConnectionIdleTimeout, - enableMultipleHttp2Connections: config.TransportStrategy.GrpcConfig.SocketsHttpHandlerOptions.EnableMultipleHttp2Connections, - keepAlivePingTimeout: System.Threading.Timeout.InfiniteTimeSpan, - keepAlivePingDelay: System.Threading.Timeout.InfiniteTimeSpan, - keepAlivePermitWithoutCalls: false - ); - var controlConfig = config.WithTransportStrategy(config.TransportStrategy.WithSocketsHttpHandlerOptions(overrideKeepalive)); - - grpcManager = new VectorIndexControlGrpcManager(controlConfig, authToken, endpoint); - _logger = config.LoggerFactory.CreateLogger(); - _exceptionMapper = new CacheExceptionMapper(config.LoggerFactory); - } - - public async Task CreateIndexAsync(string indexName, long numDimensions, SimilarityMetric similarityMetric) - { - try - { - _logger.LogTraceVectorIndexRequest("createVectorIndex", indexName); - CheckValidIndexName(indexName); - var validatedNumDimensions = ValidateNumDimensions(numDimensions); - var request = new _CreateIndexRequest { IndexName = indexName, NumDimensions = validatedNumDimensions, SimilarityMetric = new _SimilarityMetric() }; - switch (similarityMetric) - { - case SimilarityMetric.CosineSimilarity: - request.SimilarityMetric.CosineSimilarity = new _SimilarityMetric.Types._CosineSimilarity(); - break; - case SimilarityMetric.InnerProduct: - request.SimilarityMetric.InnerProduct = new _SimilarityMetric.Types._InnerProduct(); - break; - case SimilarityMetric.EuclideanSimilarity: - request.SimilarityMetric.EuclideanSimilarity = new _SimilarityMetric.Types._EuclideanSimilarity(); - break; - default: - throw new InvalidArgumentException($"Unknown similarity metric {similarityMetric}"); - } - - await grpcManager.Client.CreateIndexAsync(request, new CallOptions(deadline: CalculateDeadline())); - return _logger.LogTraceVectorIndexRequestSuccess("createVectorIndex", indexName, new CreateIndexResponse.Success()); - } - catch (Exception e) - { - if (e is RpcException { StatusCode: StatusCode.AlreadyExists }) - { - return _logger.LogTraceVectorIndexRequestSuccess("createVectorIndex", indexName, new CreateIndexResponse.AlreadyExists()); - } - return _logger.LogTraceVectorIndexRequestError("createVectorIndex", indexName, new CreateIndexResponse.Error(_exceptionMapper.Convert(e))); - } - } - - public async Task ListIndexesAsync() - { - try - { - _logger.LogTraceExecutingGenericRequest("listVectorIndexes"); - var request = new _ListIndexesRequest(); - var response = await grpcManager.Client.ListIndexesAsync(request, new CallOptions(deadline: CalculateDeadline())); - return _logger.LogTraceGenericRequestSuccess("listVectorIndexes", - new ListIndexesResponse.Success( - new List(response.Indexes.Select(n => new IndexInfo(n.IndexName, (int)n.NumDimensions, Convert(n.SimilarityMetric)))) - )); - } - catch (Exception e) - { - return _logger.LogTraceGenericRequestError("listVectorIndexes", new ListIndexesResponse.Error(_exceptionMapper.Convert(e))); - } - } - - private static SimilarityMetric Convert(_SimilarityMetric similarityMetric) - { - return similarityMetric.SimilarityMetricCase switch - { - _SimilarityMetric.SimilarityMetricOneofCase.InnerProduct => SimilarityMetric.InnerProduct, - _SimilarityMetric.SimilarityMetricOneofCase.EuclideanSimilarity => SimilarityMetric.EuclideanSimilarity, - _SimilarityMetric.SimilarityMetricOneofCase.CosineSimilarity => SimilarityMetric.CosineSimilarity, - _ => throw new UnknownException($"Unknown similarity metric {similarityMetric}") - }; - } - - public async Task DeleteIndexAsync(string indexName) - { - try - { - _logger.LogTraceVectorIndexRequest("deleteVectorIndex", indexName); - CheckValidIndexName(indexName); - var request = new _DeleteIndexRequest() { IndexName = indexName }; - await grpcManager.Client.DeleteIndexAsync(request, new CallOptions(deadline: CalculateDeadline())); - return _logger.LogTraceVectorIndexRequestSuccess("createVectorIndex", indexName, new DeleteIndexResponse.Success()); - } - catch (Exception e) - { - return _logger.LogTraceVectorIndexRequestError("createVectorIndex", indexName, new DeleteIndexResponse.Error(_exceptionMapper.Convert(e))); - } - } - - private static void CheckValidIndexName(string indexName) - { - if (string.IsNullOrWhiteSpace(indexName)) - { - throw new InvalidArgumentException("Index name must be nonempty"); - } - } - - private static ulong ValidateNumDimensions(long numDimensions) - { - if (numDimensions <= 0) - { - throw new InvalidArgumentException("numDimensions must be greater than 0"); - } - - return (ulong)numDimensions; - } - - private DateTime CalculateDeadline() - { - return DateTime.UtcNow.Add(deadline); - } - - public void Dispose() - { - grpcManager.Dispose(); - } -} diff --git a/src/Momento.Sdk/Internal/VectorIndexControlGrpcManager.cs b/src/Momento.Sdk/Internal/VectorIndexControlGrpcManager.cs deleted file mode 100644 index ea581cd5..00000000 --- a/src/Momento.Sdk/Internal/VectorIndexControlGrpcManager.cs +++ /dev/null @@ -1,81 +0,0 @@ -#pragma warning disable 1591 -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Grpc.Core; -using Grpc.Net.Client; -#if USE_GRPC_WEB -using System.Net.Http; -using Grpc.Net.Client.Web; -#endif -using Microsoft.Extensions.Logging; -using Momento.Protos.CachePing; -using Momento.Protos.ControlClient; -using Momento.Sdk.Config; -using Momento.Sdk.Config.Middleware; -using Momento.Sdk.Config.Retry; -using Momento.Sdk.Internal.Middleware; -using static System.Reflection.Assembly; - -namespace Momento.Sdk.Internal; - -public interface IVectorIndexControlClient -{ - public Task<_CreateIndexResponse> CreateIndexAsync(_CreateIndexRequest request, CallOptions callOptions); - public Task<_ListIndexesResponse> ListIndexesAsync(_ListIndexesRequest request, CallOptions callOptions); - public Task<_DeleteIndexResponse> DeleteIndexAsync(_DeleteIndexRequest request, CallOptions callOptions); -} - - -// Ideally we would implement our middleware based on gRPC Interceptors. Unfortunately, -// the their method signatures are not asynchronous. Thus, for any middleware that may -// require asynchronous actions (such as our MaxConcurrentRequestsMiddleware), we would -// end up blocking threads to wait for the completion of the async task, which would have -// a big negative impact on performance. Instead, in this commit, we implement a thin -// middleware layer of our own that uses asynchronous signatures throughout. This has -// the nice side effect of making the user-facing API for writing Middlewares a bit less -// of a learning curve for anyone not super deep on gRPC internals. -public class VectorIndexControlClientWithMiddleware : IVectorIndexControlClient -{ - private readonly IList _middlewares; - private readonly ScsControl.ScsControlClient _generatedClient; - - public VectorIndexControlClientWithMiddleware(ScsControl.ScsControlClient generatedClient, IList middlewares) - { - _generatedClient = generatedClient; - _middlewares = middlewares; - } - - public async Task<_CreateIndexResponse> CreateIndexAsync(_CreateIndexRequest request, CallOptions callOptions) - { - var wrapped = await _middlewares.WrapRequest(request, callOptions, (r, o) => _generatedClient.CreateIndexAsync(r, o)); - return await wrapped.ResponseAsync; - } - - public async Task<_ListIndexesResponse> ListIndexesAsync(_ListIndexesRequest request, CallOptions callOptions) - { - var wrapped = await _middlewares.WrapRequest(request, callOptions, (r, o) => _generatedClient.ListIndexesAsync(r, o)); - return await wrapped.ResponseAsync; - } - - public async Task<_DeleteIndexResponse> DeleteIndexAsync(_DeleteIndexRequest request, CallOptions callOptions) - { - var wrapped = await _middlewares.WrapRequest(request, callOptions, (r, o) => _generatedClient.DeleteIndexAsync(r, o)); - return await wrapped.ResponseAsync; - } -} - -public class VectorIndexControlGrpcManager : GrpcManager -{ - public readonly IVectorIndexControlClient Client; - - internal VectorIndexControlGrpcManager(IVectorIndexConfiguration config, string authToken, string endpoint): base(config.TransportStrategy.GrpcConfig, config.LoggerFactory, authToken, endpoint, "VectorIndexControlGrpcManager") - { - var middlewares = new List { - new HeaderMiddleware(config.LoggerFactory, this.headers) - }; - - var client = new ScsControl.ScsControlClient(this.invoker); - Client = new VectorIndexControlClientWithMiddleware(client, middlewares); - } -} diff --git a/src/Momento.Sdk/Internal/VectorIndexDataClient.cs b/src/Momento.Sdk/Internal/VectorIndexDataClient.cs deleted file mode 100644 index 4b20f9d5..00000000 --- a/src/Momento.Sdk/Internal/VectorIndexDataClient.cs +++ /dev/null @@ -1,368 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Grpc.Core; -using Microsoft.Extensions.Logging; -using Momento.Sdk.Config; -using Momento.Sdk.Exceptions; -using Momento.Sdk.Messages.Vector; -using Momento.Sdk.Requests.Vector; -using Momento.Sdk.Responses.Vector; -using Vectorindex; - -namespace Momento.Sdk.Internal; - -internal sealed class VectorIndexDataClient : IDisposable -{ - private readonly VectorIndexDataGrpcManager grpcManager; - private readonly TimeSpan deadline = TimeSpan.FromSeconds(60); - - private readonly ILogger _logger; - private readonly CacheExceptionMapper _exceptionMapper; - - public VectorIndexDataClient(IVectorIndexConfiguration config, string authToken, string endpoint) - { - grpcManager = new VectorIndexDataGrpcManager(config, authToken, endpoint); - _logger = config.LoggerFactory.CreateLogger(); - _exceptionMapper = new CacheExceptionMapper(config.LoggerFactory); - } - - const string REQUEST_COUNT_ITEMS = "COUNT_ITEMS"; - public async Task CountItemsAsync(string indexName) - { - try - { - _logger.LogTraceVectorIndexRequest(REQUEST_COUNT_ITEMS, indexName); - CheckValidIndexName(indexName); - var request = new _CountItemsRequest() { IndexName = indexName, All = new _CountItemsRequest.Types.All() }; - - var response = - await grpcManager.Client.CountItemsAsync(request, new CallOptions(deadline: CalculateDeadline())); - // To maintain CLS compliance we use a long here instead of a ulong. - // The max value of a long is still over 9 quintillion so we should be good for a while. - var itemCount = checked((long)response.ItemCount); - return _logger.LogTraceVectorIndexRequestSuccess(REQUEST_COUNT_ITEMS, indexName, - new CountItemsResponse.Success(itemCount)); - } - catch (Exception e) - { - return _logger.LogTraceVectorIndexRequestError(REQUEST_COUNT_ITEMS, indexName, - new CountItemsResponse.Error(_exceptionMapper.Convert(e))); - } - } - - const string REQUEST_UPSERT_ITEM_BATCH = "UPSERT_ITEM_BATCH"; - public async Task UpsertItemBatchAsync(string indexName, - IEnumerable items) - { - try - { - _logger.LogTraceVectorIndexRequest(REQUEST_UPSERT_ITEM_BATCH, indexName); - CheckValidIndexName(indexName); - var request = new _UpsertItemBatchRequest() { IndexName = indexName, Items = { items.Select(Convert) } }; - - await grpcManager.Client.UpsertItemBatchAsync(request, new CallOptions(deadline: CalculateDeadline())); - return _logger.LogTraceVectorIndexRequestSuccess(REQUEST_UPSERT_ITEM_BATCH, indexName, - new UpsertItemBatchResponse.Success()); - } - catch (Exception e) - { - return _logger.LogTraceVectorIndexRequestError(REQUEST_UPSERT_ITEM_BATCH, indexName, - new UpsertItemBatchResponse.Error(_exceptionMapper.Convert(e))); - } - } - - const string REQUEST_GET_ITEM_BATCH = "GET_ITEM_BATCH"; - public async Task GetItemBatchAsync(string indexName, IEnumerable filter) - { - try - { - _logger.LogTraceVectorIndexRequest(REQUEST_GET_ITEM_BATCH, indexName); - CheckValidIndexName(indexName); - var request = new _GetItemBatchRequest() - { - IndexName = indexName, - Filter = idsToFilterExpression(filter), - MetadataFields = new _MetadataRequest { All = new _MetadataRequest.Types.All() } - }; - - var response = - await grpcManager.Client.GetItemBatchAsync(request, new CallOptions(deadline: CalculateDeadline())); - var items = response.ItemResponse.ToDictionary( - item => item.Id, item => new Item(item.Id, item.Vector.Elements.ToList(), Convert(item.Metadata))); - return _logger.LogTraceVectorIndexRequestSuccess(REQUEST_GET_ITEM_BATCH, indexName, - new GetItemBatchResponse.Success(items)); - } - catch (Exception e) - { - return _logger.LogTraceVectorIndexRequestError(REQUEST_GET_ITEM_BATCH, indexName, - new GetItemBatchResponse.Error(_exceptionMapper.Convert(e))); - } - } - - const string REQUEST_GET_ITEM_METADATA_BATCH = "GET_ITEM_METADATA_BATCH"; - public async Task GetItemMetadataBatchAsync(string indexName, IEnumerable filter) - { - try - { - _logger.LogTraceVectorIndexRequest(REQUEST_GET_ITEM_METADATA_BATCH, indexName); - CheckValidIndexName(indexName); - var request = new _GetItemMetadataBatchRequest() - { - IndexName = indexName, - Filter = idsToFilterExpression(filter), - MetadataFields = new _MetadataRequest { All = new _MetadataRequest.Types.All() } - }; - - var response = - await grpcManager.Client.GetItemMetadataBatchAsync(request, - new CallOptions(deadline: CalculateDeadline())); - var items = response.ItemMetadataResponse.ToDictionary( - item => item.Id, item => Convert(item.Metadata)); - return _logger.LogTraceVectorIndexRequestSuccess(REQUEST_GET_ITEM_METADATA_BATCH, indexName, - new GetItemMetadataBatchResponse.Success(items)); - } - catch (Exception e) - { - return _logger.LogTraceVectorIndexRequestError(REQUEST_GET_ITEM_METADATA_BATCH, indexName, - new GetItemMetadataBatchResponse.Error(_exceptionMapper.Convert(e))); - } - } - - /// - /// Convert a list of ids to an id-in-set filter expression. - /// - /// - /// - private static _FilterExpression idsToFilterExpression(IEnumerable ids) - { - return new _FilterExpression - { - IdInSetExpression = new _IdInSetExpression() - { - Ids = { ids } - } - }; - } - - const string REQUEST_DELETE_ITEM_BATCH = "DELETE_ITEM_BATCH"; - public async Task DeleteItemBatchAsync(string indexName, IEnumerable filter) - { - try - { - _logger.LogTraceVectorIndexRequest(REQUEST_DELETE_ITEM_BATCH, indexName); - CheckValidIndexName(indexName); - var request = new _DeleteItemBatchRequest() { IndexName = indexName, Filter = idsToFilterExpression(filter) }; - - await grpcManager.Client.DeleteItemBatchAsync(request, new CallOptions(deadline: CalculateDeadline())); - return _logger.LogTraceVectorIndexRequestSuccess(REQUEST_DELETE_ITEM_BATCH, indexName, - new DeleteItemBatchResponse.Success()); - } - catch (Exception e) - { - return _logger.LogTraceVectorIndexRequestError(REQUEST_DELETE_ITEM_BATCH, indexName, - new DeleteItemBatchResponse.Error(_exceptionMapper.Convert(e))); - } - } - - const string REQUEST_SEARCH = "SEARCH"; - public async Task SearchAsync(string indexName, IEnumerable queryVector, int topK, - MetadataFields? metadataFields, float? scoreThreshold) - { - try - { - _logger.LogTraceVectorIndexRequest(REQUEST_SEARCH, indexName); - CheckValidIndexName(indexName); - var validatedTopK = ValidateTopK(topK); - metadataFields ??= new List(); - var metadataRequest = metadataFields switch - { - MetadataFields.AllFields => new _MetadataRequest { All = new _MetadataRequest.Types.All() }, - MetadataFields.List list => new _MetadataRequest - { - Some = new _MetadataRequest.Types.Some { Fields = { list.Fields } } - }, - _ => throw new InvalidArgumentException($"Unknown metadata fields type {metadataFields.GetType()}") - }; - - var request = new _SearchRequest - { - IndexName = indexName, - QueryVector = new _Vector { Elements = { queryVector } }, - TopK = validatedTopK, - MetadataFields = metadataRequest, - }; - - if (scoreThreshold != null) - { - request.ScoreThreshold = scoreThreshold.Value; - } - else - { - request.NoScoreThreshold = new _NoScoreThreshold(); - } - - var response = - await grpcManager.Client.SearchAsync(request, new CallOptions(deadline: CalculateDeadline())); - var searchHits = response.Hits.Select(Convert).ToList(); - return _logger.LogTraceVectorIndexRequestSuccess(REQUEST_SEARCH, indexName, - new SearchResponse.Success(searchHits)); - } - catch (Exception e) - { - return _logger.LogTraceVectorIndexRequestError(REQUEST_SEARCH, indexName, - new SearchResponse.Error(_exceptionMapper.Convert(e))); - } - } - - const string REQUEST_SEARCH_AND_FETCH_VECTORS = "SEARCH_AND_FETCH_VECTORS"; - public async Task SearchAndFetchVectorsAsync(string indexName, - IEnumerable queryVector, int topK, MetadataFields? metadataFields, float? scoreThreshold) - { - try - { - _logger.LogTraceVectorIndexRequest(REQUEST_SEARCH_AND_FETCH_VECTORS, indexName); - CheckValidIndexName(indexName); - var validatedTopK = ValidateTopK(topK); - metadataFields ??= new List(); - var metadataRequest = metadataFields switch - { - MetadataFields.AllFields => new _MetadataRequest { All = new _MetadataRequest.Types.All() }, - MetadataFields.List list => new _MetadataRequest - { - Some = new _MetadataRequest.Types.Some { Fields = { list.Fields } } - }, - _ => throw new InvalidArgumentException($"Unknown metadata fields type {metadataFields.GetType()}") - }; - - var request = new _SearchAndFetchVectorsRequest() - { - IndexName = indexName, - QueryVector = new _Vector { Elements = { queryVector } }, - TopK = validatedTopK, - MetadataFields = metadataRequest, - }; - - if (scoreThreshold != null) - { - request.ScoreThreshold = scoreThreshold.Value; - } - else - { - request.NoScoreThreshold = new _NoScoreThreshold(); - } - - var response = - await grpcManager.Client.SearchAndFetchVectorsAsync(request, - new CallOptions(deadline: CalculateDeadline())); - var searchHits = response.Hits.Select(h => - new SearchAndFetchVectorsHit(h.Id, h.Score, h.Vector.Elements.ToList(), Convert(h.Metadata))).ToList(); - return _logger.LogTraceVectorIndexRequestSuccess(REQUEST_SEARCH_AND_FETCH_VECTORS, indexName, - new SearchAndFetchVectorsResponse.Success(searchHits)); - } - catch (Exception e) - { - return _logger.LogTraceVectorIndexRequestError(REQUEST_SEARCH_AND_FETCH_VECTORS, indexName, - new SearchAndFetchVectorsResponse.Error(_exceptionMapper.Convert(e))); - } - } - - private static _Item Convert(Item item) - { - return new _Item - { - Id = item.Id, - Vector = new _Vector { Elements = { item.Vector } }, - Metadata = { Convert(item.Metadata) } - }; - } - - private static IEnumerable<_Metadata> Convert(Dictionary metadata) - { - var convertedMetadataList = new List<_Metadata>(); - foreach (var metadataPair in metadata) - { - var convertedMetadata = metadataPair.Value switch - { - StringValue stringValue => new _Metadata { Field = metadataPair.Key, StringValue = stringValue.Value }, - LongValue longValue => new _Metadata { Field = metadataPair.Key, IntegerValue = longValue.Value }, - DoubleValue doubleValue => new _Metadata { Field = metadataPair.Key, DoubleValue = doubleValue.Value }, - BoolValue boolValue => new _Metadata { Field = metadataPair.Key, BooleanValue = boolValue.Value }, - StringListValue stringListValue => new _Metadata - { - Field = metadataPair.Key, - ListOfStringsValue = new _Metadata.Types._ListOfStrings { Values = { stringListValue.Value } } - }, - _ => throw new InvalidArgumentException($"Unknown metadata type {metadataPair.Value.GetType()}") - }; - - convertedMetadataList.Add(convertedMetadata); - } - - return convertedMetadataList; - } - - private static Dictionary Convert(IEnumerable<_Metadata> metadata) - { - return metadata.ToDictionary(m => m.Field, Convert); - } - - private static MetadataValue Convert(_Metadata metadata) - { - switch (metadata.ValueCase) - { - case _Metadata.ValueOneofCase.StringValue: - return new StringValue(metadata.StringValue); - case _Metadata.ValueOneofCase.IntegerValue: - return new LongValue(metadata.IntegerValue); - case _Metadata.ValueOneofCase.DoubleValue: - return new DoubleValue(metadata.DoubleValue); - case _Metadata.ValueOneofCase.BooleanValue: - return new BoolValue(metadata.BooleanValue); - case _Metadata.ValueOneofCase.ListOfStringsValue: - return new StringListValue(metadata.ListOfStringsValue.Values.ToList()); - case _Metadata.ValueOneofCase.None: - default: - throw new UnknownException($"Unknown metadata type {metadata.ValueCase}"); - } - } - - private static SearchHit Convert(_SearchHit hit) - { - return new SearchHit(hit.Id, hit.Score, Convert(hit.Metadata)); - } - - private static SearchAndFetchVectorsHit Convert(_SearchAndFetchVectorsHit hit) - { - return new SearchAndFetchVectorsHit(hit.Id, hit.Score, hit.Vector.Elements.ToList(), Convert(hit.Metadata)); - } - - private static void CheckValidIndexName(string indexName) - { - if (string.IsNullOrWhiteSpace(indexName)) - { - throw new InvalidArgumentException("Index name must be nonempty"); - } - } - - private static uint ValidateTopK(long topK) - { - if (topK <= 0) - { - throw new InvalidArgumentException("topK must be greater than 0"); - } - - return (uint)topK; - } - - private DateTime CalculateDeadline() - { - return DateTime.UtcNow.Add(deadline); - } - - public void Dispose() - { - grpcManager.Dispose(); - } -} diff --git a/src/Momento.Sdk/Internal/VectorIndexDataGrpcManager.cs b/src/Momento.Sdk/Internal/VectorIndexDataGrpcManager.cs deleted file mode 100644 index d4627c07..00000000 --- a/src/Momento.Sdk/Internal/VectorIndexDataGrpcManager.cs +++ /dev/null @@ -1,107 +0,0 @@ -#pragma warning disable 1591 -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Grpc.Core; -using Grpc.Net.Client; -#if USE_GRPC_WEB -using System.Net.Http; -using Grpc.Net.Client.Web; -#endif -using Microsoft.Extensions.Logging; -using Momento.Sdk.Config; -using Momento.Sdk.Config.Middleware; -using Momento.Sdk.Internal.Middleware; -using Vectorindex; -using static System.Reflection.Assembly; - -namespace Momento.Sdk.Internal; - -public interface IVectorIndexDataClient -{ - public Task<_CountItemsResponse> CountItemsAsync(_CountItemsRequest request, CallOptions callOptions); - public Task<_UpsertItemBatchResponse> UpsertItemBatchAsync(_UpsertItemBatchRequest request, CallOptions callOptions); - public Task<_SearchResponse> SearchAsync(_SearchRequest request, CallOptions callOptions); - public Task<_SearchAndFetchVectorsResponse> SearchAndFetchVectorsAsync(_SearchAndFetchVectorsRequest request, CallOptions callOptions); - public Task<_GetItemBatchResponse> GetItemBatchAsync(_GetItemBatchRequest request, CallOptions callOptions); - public Task<_GetItemMetadataBatchResponse> GetItemMetadataBatchAsync(_GetItemMetadataBatchRequest request, CallOptions callOptions); - public Task<_DeleteItemBatchResponse> DeleteItemBatchAsync(_DeleteItemBatchRequest request, CallOptions callOptions); -} - - -// Ideally we would implement our middleware based on gRPC Interceptors. Unfortunately, -// the their method signatures are not asynchronous. Thus, for any middleware that may -// require asynchronous actions (such as our MaxConcurrentRequestsMiddleware), we would -// end up blocking threads to wait for the completion of the async task, which would have -// a big negative impact on performance. Instead, in this commit, we implement a thin -// middleware layer of our own that uses asynchronous signatures throughout. This has -// the nice side effect of making the user-facing API for writing Middlewares a bit less -// of a learning curve for anyone not super deep on gRPC internals. -public class VectorIndexDataClientWithMiddleware : IVectorIndexDataClient -{ - private readonly IList _middlewares; - private readonly VectorIndex.VectorIndexClient _generatedClient; - - public VectorIndexDataClientWithMiddleware(VectorIndex.VectorIndexClient generatedClient, IList middlewares) - { - _generatedClient = generatedClient; - _middlewares = middlewares; - } - - public async Task<_CountItemsResponse> CountItemsAsync(_CountItemsRequest request, CallOptions callOptions) - { - var wrapped = await _middlewares.WrapRequest(request, callOptions, (r, o) => _generatedClient.CountItemsAsync(r, o)); - return await wrapped.ResponseAsync; - } - - public async Task<_UpsertItemBatchResponse> UpsertItemBatchAsync(_UpsertItemBatchRequest request, CallOptions callOptions) - { - var wrapped = await _middlewares.WrapRequest(request, callOptions, (r, o) => _generatedClient.UpsertItemBatchAsync(r, o)); - return await wrapped.ResponseAsync; - } - - public async Task<_SearchResponse> SearchAsync(_SearchRequest request, CallOptions callOptions) - { - var wrapped = await _middlewares.WrapRequest(request, callOptions, (r, o) => _generatedClient.SearchAsync(r, o)); - return await wrapped.ResponseAsync; - } - - public async Task<_SearchAndFetchVectorsResponse> SearchAndFetchVectorsAsync(_SearchAndFetchVectorsRequest request, CallOptions callOptions) - { - var wrapped = await _middlewares.WrapRequest(request, callOptions, (r, o) => _generatedClient.SearchAndFetchVectorsAsync(r, o)); - return await wrapped.ResponseAsync; - } - - public async Task<_GetItemBatchResponse> GetItemBatchAsync(_GetItemBatchRequest request, CallOptions callOptions) - { - var wrapped = await _middlewares.WrapRequest(request, callOptions, (r, o) => _generatedClient.GetItemBatchAsync(r, o)); - return await wrapped.ResponseAsync; - } - - public async Task<_GetItemMetadataBatchResponse> GetItemMetadataBatchAsync(_GetItemMetadataBatchRequest request, CallOptions callOptions) - { - var wrapped = await _middlewares.WrapRequest(request, callOptions, (r, o) => _generatedClient.GetItemMetadataBatchAsync(r, o)); - return await wrapped.ResponseAsync; - } - - public async Task<_DeleteItemBatchResponse> DeleteItemBatchAsync(_DeleteItemBatchRequest request, CallOptions callOptions) - { - var wrapped = await _middlewares.WrapRequest(request, callOptions, (r, o) => _generatedClient.DeleteItemBatchAsync(r, o)); - return await wrapped.ResponseAsync; - } -} - -public class VectorIndexDataGrpcManager : GrpcManager -{ - public readonly IVectorIndexDataClient Client; - - internal VectorIndexDataGrpcManager(IVectorIndexConfiguration config, string authToken, string endpoint): base(config.TransportStrategy.GrpcConfig, config.LoggerFactory, authToken, endpoint, "VectorIndexDataGrpcManager") - { - var middlewares = new List { - new HeaderMiddleware(config.LoggerFactory, this.headers) - }; - - var client = new VectorIndex.VectorIndexClient(this.invoker); - Client = new VectorIndexDataClientWithMiddleware(client, middlewares); - } -} diff --git a/src/Momento.Sdk/Messages/Vector/MetadataValue.cs b/src/Momento.Sdk/Messages/Vector/MetadataValue.cs deleted file mode 100644 index 997481d0..00000000 --- a/src/Momento.Sdk/Messages/Vector/MetadataValue.cs +++ /dev/null @@ -1,280 +0,0 @@ -using System.Collections.Generic; -using System.Globalization; -using System.Linq; - -namespace Momento.Sdk.Messages.Vector; - -/// -/// Container for a piece of vector metadata. -/// -public abstract class MetadataValue -{ - /// - public abstract override string ToString(); - - /// - /// Implicitly convert a string to a StringValue. - /// - /// The string to convert. - public static implicit operator MetadataValue(string value) => new StringValue(value); - - /// - /// Implicitly convert a long to a LongValue. - /// - /// The long to convert. - public static implicit operator MetadataValue(long value) => new LongValue(value); - - /// - /// Implicitly convert a double to a DoubleValue. - /// - /// The double to convert. - public static implicit operator MetadataValue(double value) => new DoubleValue(value); - - /// - /// Implicitly convert a bool to a BoolValue. - /// - /// The bool to convert. - public static implicit operator MetadataValue(bool value) => new BoolValue(value); - - /// - /// Implicitly convert a list of strings to a StringListValue. - /// - /// The list of strings to convert. - public static implicit operator MetadataValue(List value) => new StringListValue(value); -} - -/// -/// String vector metadata. -/// -public class StringValue : MetadataValue -{ - /// - /// Constructs a StringValue. - /// - /// the string to wrap. - public StringValue(string value) - { - Value = value; - } - - /// - /// The wrapped string. - /// - public string Value { get; } - - /// - public override string ToString() => Value; - - /// - /// Implicitly convert a string to a StringValue. - /// - /// The string to convert. - public static implicit operator StringValue(string value) => new StringValue(value); - - /// - /// Explicitly convert a StringValue to a string. - /// - /// The StringValue to convert. - public static explicit operator string(StringValue value) => value.Value; - - /// - public override bool Equals(object obj) - { - return obj is StringValue other && Value == other.Value; - } - - /// - public override int GetHashCode() - { - return Value.GetHashCode(); - } -} - -/// -/// Long vector metadata. -/// -public class LongValue : MetadataValue -{ - /// - /// Constructs a LongValue. - /// - /// the long to wrap. - public LongValue(long value) - { - Value = value; - } - - /// - /// The wrapped long. - /// - public long Value { get; } - - /// - public override string ToString() => Value.ToString(); - - /// - /// Implicitly convert a long to a LongValue. - /// - /// The long to convert. - public static implicit operator LongValue(long value) => new LongValue(value); - - /// - /// Explicitly convert a LongValue to a long. - /// - /// The LongValue to convert. - public static explicit operator long(LongValue value) => value.Value; - - /// - public override bool Equals(object obj) - { - return obj is LongValue other && Value == other.Value; - } - - /// - public override int GetHashCode() - { - return Value.GetHashCode(); - } -} - -/// -/// Double vector metadata. -/// -public class DoubleValue : MetadataValue -{ - /// - /// Constructs a DoubleValue. - /// - /// the double to wrap. - public DoubleValue(double value) - { - Value = value; - } - - /// - /// The wrapped double. - /// - public double Value { get; } - - /// - public override string ToString() => Value.ToString(CultureInfo.InvariantCulture); - - /// - /// Implicitly convert a double to a DoubleValue. - /// - /// The double to convert. - public static implicit operator DoubleValue(double value) => new DoubleValue(value); - - /// - /// Explicitly convert a DoubleValue to a double. - /// - /// The DoubleValue to convert. - public static explicit operator double(DoubleValue value) => value.Value; - - /// - public override bool Equals(object obj) - { - // ReSharper disable once CompareOfFloatsByEqualityOperator - return obj is DoubleValue other && Value == other.Value; - } - - /// - public override int GetHashCode() - { - return Value.GetHashCode(); - } -} - -/// -/// Boolean vector metadata. -/// -public class BoolValue : MetadataValue -{ - /// - /// Constructs a BoolValue. - /// - /// the bool to wrap. - public BoolValue(bool value) - { - Value = value; - } - - /// - /// The wrapped bool. - /// - public bool Value { get; } - - /// - public override string ToString() => Value.ToString(); - - /// - /// Implicitly convert a bool to a BoolValue. - /// - /// The bool to convert. - public static implicit operator BoolValue(bool value) => new BoolValue(value); - - /// - /// Explicitly convert a BoolValue to a bool. - /// - /// The BoolValue to convert. - public static explicit operator bool(BoolValue value) => value.Value; - - /// - public override bool Equals(object obj) - { - return obj is BoolValue other && Value == other.Value; - } - - /// - public override int GetHashCode() - { - return Value.GetHashCode(); - } -} - -/// -/// String list vector metadata. -/// -public class StringListValue : MetadataValue -{ - /// - /// Constructs a StringListValue. - /// - /// the list of strings to wrap. - public StringListValue(List value) - { - Value = value; - } - - /// - /// The wrapped string list. - /// - public List Value { get; } - - /// - public override string ToString() => string.Join(", ", Value); - - /// - /// Implicitly convert a list of strings to a StringListValue. - /// - /// The list of strings to convert. - public static implicit operator StringListValue(List value) => new StringListValue(value); - - /// - /// Explicitly convert a StringListValue to a list of strings. - /// - /// The StringListValue to convert. - public static explicit operator List(StringListValue value) => value.Value; - - /// - public override bool Equals(object obj) - { - return obj is StringListValue other && Value.SequenceEqual(other.Value); - } - - /// - public override int GetHashCode() - { - return Value.Aggregate(0, (acc, val) => acc ^ (val != null ? val.GetHashCode() : 0)); - } -} \ No newline at end of file diff --git a/src/Momento.Sdk/Momento.Sdk.csproj b/src/Momento.Sdk/Momento.Sdk.csproj index 53553cb8..33e6e90a 100644 --- a/src/Momento.Sdk/Momento.Sdk.csproj +++ b/src/Momento.Sdk/Momento.Sdk.csproj @@ -44,6 +44,7 @@ + diff --git a/src/Momento.Sdk/PreviewVectorIndexClient.cs b/src/Momento.Sdk/PreviewVectorIndexClient.cs deleted file mode 100644 index 62c84129..00000000 --- a/src/Momento.Sdk/PreviewVectorIndexClient.cs +++ /dev/null @@ -1,110 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Momento.Sdk.Auth; -using Momento.Sdk.Config; -using Momento.Sdk.Internal; -using Momento.Sdk.Requests.Vector; -using Momento.Sdk.Responses.Vector; - -namespace Momento.Sdk; - -/// -/// PREVIEW Vector Index Client implementation -/// WARNING: the API for this client is not yet stable and may change without notice. -/// -/// Includes control operations and data operations. -/// -public class PreviewVectorIndexClient : IPreviewVectorIndexClient -{ - private readonly VectorIndexControlClient controlClient; - private readonly VectorIndexDataClient dataClient; - - - /// - /// Client to perform operations against Momento Serverless Cache. - /// - /// Configuration to use for the transport, retries, middlewares. - /// See for out-of-the-box configuration choices, - /// eg - /// Momento auth provider. - public PreviewVectorIndexClient(IVectorIndexConfiguration config, ICredentialProvider authProvider) - { - var loggerFactory = config.LoggerFactory; - controlClient = - new VectorIndexControlClient(config, authProvider.AuthToken, authProvider.ControlEndpoint); - dataClient = new VectorIndexDataClient(config, authProvider.AuthToken, authProvider.CacheEndpoint); - } - - /// - public async Task CreateIndexAsync(string indexName, long numDimensions, - SimilarityMetric similarityMetric = SimilarityMetric.CosineSimilarity) - { - return await controlClient.CreateIndexAsync(indexName, numDimensions, similarityMetric); - } - - /// - public async Task ListIndexesAsync() - { - return await controlClient.ListIndexesAsync(); - } - - /// - public async Task DeleteIndexAsync(string indexName) - { - return await controlClient.DeleteIndexAsync(indexName); - } - - /// - public async Task CountItemsAsync(string indexName) - { - return await dataClient.CountItemsAsync(indexName); - } - - /// - public async Task UpsertItemBatchAsync(string indexName, - IEnumerable items) - { - return await dataClient.UpsertItemBatchAsync(indexName, items); - } - - /// - public async Task GetItemBatchAsync(string indexName, IEnumerable filter) - { - return await dataClient.GetItemBatchAsync(indexName, filter); - } - - /// - public async Task GetItemMetadataBatchAsync(string indexName, IEnumerable filter) - { - return await dataClient.GetItemMetadataBatchAsync(indexName, filter); - } - - /// - public async Task DeleteItemBatchAsync(string indexName, IEnumerable filter) - { - return await dataClient.DeleteItemBatchAsync(indexName, filter); - } - - /// - public async Task SearchAsync(string indexName, IEnumerable queryVector, - int topK = 10, MetadataFields? metadataFields = null, float? searchThreshold = null) - { - return await dataClient.SearchAsync(indexName, queryVector, topK, metadataFields, searchThreshold); - } - - /// - public async Task SearchAndFetchVectorsAsync(string indexName, - IEnumerable queryVector, int topK = 10, MetadataFields? metadataFields = null, - float? scoreThreshold = null) - { - return await dataClient.SearchAndFetchVectorsAsync(indexName, queryVector, topK, metadataFields, - scoreThreshold); - } - - /// - public void Dispose() - { - controlClient.Dispose(); - } -} diff --git a/src/Momento.Sdk/Requests/Vector/Item.cs b/src/Momento.Sdk/Requests/Vector/Item.cs deleted file mode 100644 index 7ebe0e30..00000000 --- a/src/Momento.Sdk/Requests/Vector/Item.cs +++ /dev/null @@ -1,79 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using Momento.Sdk.Messages.Vector; -using Momento.Sdk.Internal.ExtensionMethods; - -namespace Momento.Sdk.Requests.Vector; - -/// -/// An item in a vector index. Contains an ID, the vector, and any associated metadata. -/// -public class Item -{ - /// - /// Constructs a Item with no metadata. - /// - /// the ID of the vector. - /// the vector. - public Item(string id, List vector) - { - Id = id; - Vector = vector; - Metadata = new Dictionary(); - } - - /// - /// Constructs a Item. - /// - /// the ID of the vector. - /// the vector. - /// Metadata associated with the vector. - public Item(string id, List vector, Dictionary metadata) - { - Id = id; - Vector = vector; - Metadata = metadata; - } - - /// - /// The ID of the vector. - /// - public string Id { get; } - - /// - /// The vector. - /// - public List Vector { get; } - - /// - /// Metadata associated with the vector. - /// - public Dictionary Metadata { get; } - - /// - public override bool Equals(object obj) - { - if (ReferenceEquals(this, obj)) - { - return true; - } - - if (obj is null || GetType() != obj.GetType()) - { - return false; - } - - var other = (Item)obj; - return Id == other.Id && Vector.SequenceEqual(other.Vector) && Metadata.MetadataEquals(other.Metadata); - } - - /// - public override int GetHashCode() - { - int hash = 17; - hash = hash * 23 + Id.GetHashCode(); - hash = hash * 23 + Vector.GetHashCode(); - hash = hash * 23 + Metadata.MetadataHashCode(); - return hash; - } -} diff --git a/src/Momento.Sdk/Requests/Vector/MetadataFields.cs b/src/Momento.Sdk/Requests/Vector/MetadataFields.cs deleted file mode 100644 index b4c51763..00000000 --- a/src/Momento.Sdk/Requests/Vector/MetadataFields.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System.Collections.Generic; - -namespace Momento.Sdk.Requests.Vector; - -/// -/// Wrapper for a list of metadata fields. Used in vector methods that can either take a -/// list of metadata to look up, or a value specifying that all metadata should be returned. -/// -public abstract class MetadataFields -{ - /// - /// Static value representing all metadata fields. - /// - public static readonly AllFields All = new AllFields(); - - /// - /// Implicitly convert a list of strings to a MetadataFields. Allows for passing a bare list instead - /// of having to explicitly create a MetadataFields object. - /// - /// The fields to look up. - /// - public static implicit operator MetadataFields(List fields) => new List(fields); - - /// - /// MetadataFields implementation representing a list of specific fields. - /// - public class List : MetadataFields - { - /// - /// Constructs a MetadataFields.List with specific fields. - /// - /// The fields to look up. - public List(IEnumerable fields) - { - Fields = fields; - } - - /// - /// The fields to look up. - /// - public IEnumerable Fields { get; } - } - - /// - /// MetadataFields implementation representing all fields. - /// - public class AllFields : MetadataFields - { - /// - /// Constructs a MetadataFields.All. - /// - public AllFields() - { - } - } -} \ No newline at end of file diff --git a/src/Momento.Sdk/Requests/Vector/SimilarityMetric.cs b/src/Momento.Sdk/Requests/Vector/SimilarityMetric.cs deleted file mode 100644 index 5d1ed489..00000000 --- a/src/Momento.Sdk/Requests/Vector/SimilarityMetric.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace Momento.Sdk.Requests.Vector; - -/// -/// The similarity metric to use when comparing vectors in the index. -/// -public enum SimilarityMetric -{ - /// - /// The cosine similarity between two vectors, ie the cosine of the angle between them. - /// Bigger is better. Ranges from -1 to 1. - /// - CosineSimilarity, - - - /// - /// The inner product between two vectors, ie the sum of the element-wise products. - /// Bigger is better. Ranges from 0 to infinity. - /// - InnerProduct, - - - /// - /// The Euclidean distance squared between two vectors, ie the sum of squared differences between each element. - /// Smaller is better. Ranges from 0 to infinity. - /// - EuclideanSimilarity -} \ No newline at end of file diff --git a/src/Momento.Sdk/Responses/Vector/CountItemsResponse.cs b/src/Momento.Sdk/Responses/Vector/CountItemsResponse.cs deleted file mode 100644 index 9ce645c2..00000000 --- a/src/Momento.Sdk/Responses/Vector/CountItemsResponse.cs +++ /dev/null @@ -1,80 +0,0 @@ -using Momento.Sdk.Exceptions; - -namespace Momento.Sdk.Responses.Vector; - -/// -/// Parent response type for a count items request. The -/// response object is resolved to a type-safe object of one of -/// the following subtypes: -/// -/// CountItemsResponse.Success -/// CountItemsResponse.Error -/// -/// Pattern matching can be used to operate on the appropriate subtype. -/// For example: -/// -/// if (response is CountItemsResponse.Success successResponse) -/// { -/// return successResponse.ItemCount; -/// } -/// else if (response is CountItemsResponse.Error errorResponse) -/// { -/// // handle error as appropriate -/// } -/// else -/// { -/// // handle unexpected response -/// } -/// -/// -public abstract class CountItemsResponse -{ - /// - public class Success : CountItemsResponse - { - /// - /// The number of items in the vector index. - /// - public long ItemCount { get; } - - /// - /// The number of items in the vector index. - public Success(long itemCount) - { - ItemCount = itemCount; - } - - /// - public override string ToString() - { - return $"{base.ToString()}: {ItemCount}"; - } - - } - - /// - public class Error : CountItemsResponse, IError - { - /// - public Error(SdkException error) - { - InnerException = error; - } - - /// - public SdkException InnerException { get; } - - /// - public MomentoErrorCode ErrorCode => InnerException.ErrorCode; - - /// - public string Message => $"{InnerException.MessageWrapper}: {InnerException.Message}"; - - /// - public override string ToString() - { - return $"{base.ToString()}: {Message}"; - } - - } -} diff --git a/src/Momento.Sdk/Responses/Vector/CreateIndexResponse.cs b/src/Momento.Sdk/Responses/Vector/CreateIndexResponse.cs deleted file mode 100644 index fad70d85..00000000 --- a/src/Momento.Sdk/Responses/Vector/CreateIndexResponse.cs +++ /dev/null @@ -1,69 +0,0 @@ -namespace Momento.Sdk.Responses.Vector; - -using Exceptions; - -/// -/// Parent response type for a create vector index request. The -/// response object is resolved to a type-safe object of one of -/// the following subtypes: -/// -/// CreateIndexResponse.Success -/// CreateIndexResponse.AlreadyExists -/// CreateIndexResponse.Error -/// -/// Pattern matching can be used to operate on the appropriate subtype. -/// For example: -/// -/// if (response is CreateIndexResponse.Success successResponse) -/// { -/// // handle success if needed -/// } -/// else if (response is CreateIndexResponse.AlreadyExists alreadyExistsResponse) -/// { -/// // handle already exists as appropriate -/// } -/// else if (response is CreateIndexResponse.Error errorResponse) -/// { -/// // handle error as appropriate -/// } -/// -/// -public abstract class CreateIndexResponse -{ - - /// - public class Success : CreateIndexResponse { } - - /// - /// Class AlreadyExists indicates that an index with the requested name - /// has already been created in the requesting account. - /// - public class AlreadyExists : CreateIndexResponse { } - - /// - public class Error : CreateIndexResponse, IError - { - /// - public Error(SdkException error) - { - InnerException = error; - } - - /// - public SdkException InnerException { get; } - - /// - public MomentoErrorCode ErrorCode => InnerException.ErrorCode; - - /// - public string Message => $"{InnerException.MessageWrapper}: {InnerException.Message}"; - - /// - public override string ToString() - { - return $"{base.ToString()}: {Message}"; - } - - } - -} diff --git a/src/Momento.Sdk/Responses/Vector/DeleteIndexResponse.cs b/src/Momento.Sdk/Responses/Vector/DeleteIndexResponse.cs deleted file mode 100644 index 68295b8a..00000000 --- a/src/Momento.Sdk/Responses/Vector/DeleteIndexResponse.cs +++ /dev/null @@ -1,61 +0,0 @@ -namespace Momento.Sdk.Responses.Vector; - -using Momento.Sdk.Exceptions; - -/// -/// Parent response type for a delete vector index request. The -/// response object is resolved to a type-safe object of one of -/// the following subtypes: -/// -/// DeleteIndexResponse.Success -/// DeleteIndexResponse.Error -/// -/// Pattern matching can be used to operate on the appropriate subtype. -/// For example: -/// -/// if (response is DeleteIndexResponse.Success successResponse) -/// { -/// // handle success if needed -/// } -/// else if (response is DeleteIndexResponse.Error errorResponse) -/// { -/// // handle error as appropriate -/// } -/// else -/// { -/// // handle unexpected response -/// } -/// -/// -public abstract class DeleteIndexResponse -{ - - /// - public class Success : DeleteIndexResponse { } - - /// - public class Error : DeleteIndexResponse, IError - { - /// - public Error(SdkException error) - { - InnerException = error; - } - - /// - public SdkException InnerException { get; } - - /// - public MomentoErrorCode ErrorCode => InnerException.ErrorCode; - - /// - public string Message => $"{InnerException.MessageWrapper}: {InnerException.Message}"; - - /// - public override string ToString() - { - return $"{base.ToString()}: {Message}"; - } - - } -} diff --git a/src/Momento.Sdk/Responses/Vector/DeleteItemBatchResponse.cs b/src/Momento.Sdk/Responses/Vector/DeleteItemBatchResponse.cs deleted file mode 100644 index 05ef9b7d..00000000 --- a/src/Momento.Sdk/Responses/Vector/DeleteItemBatchResponse.cs +++ /dev/null @@ -1,58 +0,0 @@ -namespace Momento.Sdk.Responses.Vector; - -using Exceptions; - -/// -/// Parent response type for a vector delete item batch request. The -/// response object is resolved to a type-safe object of one of -/// the following subtypes: -/// -/// DeleteItemBatchResponse.Success -/// DeleteItemBatchResponse.Error -/// -/// Pattern matching can be used to operate on the appropriate subtype. -/// For example: -/// -/// if (response is DeleteItemBatchResponse.Success successResponse) -/// { -/// // handle success if needed -/// } -/// else if (response is DeleteItemBatchResponse.Error errorResponse) -/// { -/// // handle error as appropriate -/// } -/// -/// -public abstract class DeleteItemBatchResponse -{ - - /// - public class Success : DeleteItemBatchResponse { } - - /// - public class Error : DeleteItemBatchResponse, IError - { - /// - public Error(SdkException error) - { - InnerException = error; - } - - /// - public SdkException InnerException { get; } - - /// - public MomentoErrorCode ErrorCode => InnerException.ErrorCode; - - /// - public string Message => $"{InnerException.MessageWrapper}: {InnerException.Message}"; - - /// - public override string ToString() - { - return $"{base.ToString()}: {Message}"; - } - - } - -} diff --git a/src/Momento.Sdk/Responses/Vector/GetItemBatchResponse.cs b/src/Momento.Sdk/Responses/Vector/GetItemBatchResponse.cs deleted file mode 100644 index fa318b52..00000000 --- a/src/Momento.Sdk/Responses/Vector/GetItemBatchResponse.cs +++ /dev/null @@ -1,139 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using Momento.Sdk.Exceptions; -using Momento.Sdk.Requests.Vector; -using Momento.Sdk.Internal.ExtensionMethods; - -namespace Momento.Sdk.Responses.Vector; - -/// -/// Parent response type for a get item batch request. The -/// response object is resolved to a type-safe object of one of -/// the following subtypes: -/// -/// GetItemBatch.Success -/// GetItemBatch.Error -/// -/// Pattern matching can be used to operate on the appropriate subtype. -/// For example: -/// -/// if (response is GetItemBatch.Success successResponse) -/// { -/// return successResponse.Values; -/// } -/// else if (response is GetItemBatch.Error errorResponse) -/// { -/// // handle error as appropriate -/// } -/// else -/// { -/// // handle unexpected response -/// } -/// -/// -public abstract class GetItemBatchResponse -{ - /// - public class Success : GetItemBatchResponse - { - /// - /// The found items by ID. - /// - public Dictionary Values { get; } - - /// - /// the found items - public Success(Dictionary values) - { - Values = values; - } - - /// - public override string ToString() - { - var displayedHits = Values.Take(5).Select(value => $"{value.Value.ToString()}"); - return $"{base.ToString()}: {string.Join(", ", displayedHits)}..."; - } - - /// - public override bool Equals(object obj) - { - if (ReferenceEquals(this, obj)) - { - return true; - } - if (obj is null || GetType() != obj.GetType()) - { - return false; - } - var other = (Success)obj; - - if (Values == null && other.Values == null) - { - return true; - } - else if (Values == null || other.Values == null) - { - return false; - } - else if (Values.Count != other.Values.Count) - { - return false; - } - else - { - foreach (var item in Values) - { - if (!other.Values.ContainsKey(item.Key)) - { - return false; - } - else if (!item.Value.Equals(other.Values[item.Key])) - { - return false; - } - } - return true; - } - } - - /// - public override int GetHashCode() - { - int hash = 17; - foreach (var kv in Values) - { - hash = hash * 23 + kv.Key.GetHashCode(); - hash = hash * 23 + kv.Value.GetHashCode(); - } - return hash; - } - - } - - /// - public class Error : GetItemBatchResponse, IError - { - /// - public Error(SdkException error) - { - InnerException = error; - } - - /// - public SdkException InnerException { get; } - - /// - public MomentoErrorCode ErrorCode => InnerException.ErrorCode; - - /// - public string Message => $"{InnerException.MessageWrapper}: {InnerException.Message}"; - - /// - public override string ToString() - { - return $"{base.ToString()}: {Message}"; - } - - } -} diff --git a/src/Momento.Sdk/Responses/Vector/GetItemMetadataResponse.cs b/src/Momento.Sdk/Responses/Vector/GetItemMetadataResponse.cs deleted file mode 100644 index 746b7b2b..00000000 --- a/src/Momento.Sdk/Responses/Vector/GetItemMetadataResponse.cs +++ /dev/null @@ -1,117 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using Momento.Sdk.Exceptions; -using Momento.Sdk.Messages.Vector; -using Momento.Sdk.Internal.ExtensionMethods; - -namespace Momento.Sdk.Responses.Vector; - -/// -/// Parent response type for a get item metadata batch request. The -/// response object is resolved to a type-safe object of one of -/// the following subtypes: -/// -/// GetItemMetadataBatch.Success -/// GetItemMetadataBatch.Error -/// -/// Pattern matching can be used to operate on the appropriate subtype. -/// For example: -/// -/// if (response is GetItemMetadataBatch.Success successResponse) -/// { -/// return successResponse.Values; -/// } -/// else if (response is GetItemMetadataBatch.Error errorResponse) -/// { -/// // handle error as appropriate -/// } -/// else -/// { -/// // handle unexpected response -/// } -/// -/// -public abstract class GetItemMetadataBatchResponse -{ - /// - public class Success : GetItemMetadataBatchResponse - { - /// - /// The metadata for found items by ID. - /// - public Dictionary> Values { get; } - - /// - /// the found items - public Success(Dictionary> values) - { - Values = values; - } - - /// - public override string ToString() - { - var displayedHits = Values.Take(5).Select(value => $"{value.Key} {value.Value.ToString()}"); - return $"{base.ToString()}: {string.Join(", ", displayedHits)}..."; - } - - /// - public override bool Equals(object obj) - { - if (ReferenceEquals(this, obj)) - { - return true; - } - - if (!(obj is Success other)) - { - return false; - } - - if (Values.Count != other.Values.Count) - { - return false; - } - - return Values.All(kv => other.Values.ContainsKey(kv.Key) && kv.Value.MetadataEquals(other.Values[kv.Key])); - } - - /// - public override int GetHashCode() - { - int hash = 17; - foreach (var kv in Values) - { - hash = hash * 23 + kv.Key.GetHashCode(); - hash = hash * 23 + kv.Value.MetadataHashCode(); - } - return hash; - } - } - - /// - public class Error : GetItemMetadataBatchResponse, IError - { - /// - public Error(SdkException error) - { - InnerException = error; - } - - /// - public SdkException InnerException { get; } - - /// - public MomentoErrorCode ErrorCode => InnerException.ErrorCode; - - /// - public string Message => $"{InnerException.MessageWrapper}: {InnerException.Message}"; - - /// - public override string ToString() - { - return $"{base.ToString()}: {Message}"; - } - - } -} diff --git a/src/Momento.Sdk/Responses/Vector/IndexInfo.cs b/src/Momento.Sdk/Responses/Vector/IndexInfo.cs deleted file mode 100644 index fab1b34e..00000000 --- a/src/Momento.Sdk/Responses/Vector/IndexInfo.cs +++ /dev/null @@ -1,65 +0,0 @@ -using Momento.Sdk.Requests.Vector; - -namespace Momento.Sdk.Responses.Vector; - -/// -/// Information about a vector index. -/// -public class IndexInfo -{ - /// - /// The name of the index. - /// - public string Name { get; } - - /// - /// The number of dimensions in the index. - /// - public int NumDimensions { get; } - - /// - /// The similarity metric used by the index. - /// - public SimilarityMetric SimilarityMetric { get; } - - /// - /// Constructs an IndexInfo. - /// - /// The name of the index. - /// The number of dimensions in the index. - /// The similarity metric used by the index. - public IndexInfo(string name, int numDimensions, SimilarityMetric similarityMetric) - { - Name = name; - NumDimensions = numDimensions; - SimilarityMetric = similarityMetric; - } - - - /// - public override bool Equals(object obj) - { - return obj is IndexInfo other && Name == other.Name && NumDimensions == other.NumDimensions && SimilarityMetric == other.SimilarityMetric; - } - - /// - public override int GetHashCode() - { - // Overflow is standard here since the alternative is modulo arithmetic, which just wraps around the same. - unchecked - { - // see https://github.com/dotnet/coreclr/blob/master/src/System.Private.CoreLib/shared/System/HashCode.cs#L58 - var hash = 17; - hash = hash * 23 + Name.GetHashCode(); - hash = hash * 23 + NumDimensions.GetHashCode(); - hash = hash * 23 + SimilarityMetric.GetHashCode(); - return hash; - } - } - - /// - public override string ToString() - { - return $"IndexInfo {{ Name = {Name} NumDimensions = {NumDimensions} SimilarityMetric = {SimilarityMetric} }}"; - } -} diff --git a/src/Momento.Sdk/Responses/Vector/ListIndexesResponse.cs b/src/Momento.Sdk/Responses/Vector/ListIndexesResponse.cs deleted file mode 100644 index f6fc2bcf..00000000 --- a/src/Momento.Sdk/Responses/Vector/ListIndexesResponse.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System.Collections.Generic; -using Momento.Sdk.Exceptions; - -namespace Momento.Sdk.Responses.Vector; - -/// -/// Parent response type for a list vector indexes request. The -/// response object is resolved to a type-safe object of one of -/// the following subtypes: -/// -/// ListIndexesResponse.Success -/// ListIndexesResponse.Error -/// -/// Pattern matching can be used to operate on the appropriate subtype. -/// For example: -/// -/// if (response is ListIndexesResponse.Success successResponse) -/// { -/// return successResponse.Indexes; -/// } -/// else if (response is ListIndexesResponse.Error errorResponse) -/// { -/// // handle error as appropriate -/// } -/// else -/// { -/// // handle unexpected response -/// } -/// -/// -public abstract class ListIndexesResponse -{ - /// - public class Success : ListIndexesResponse - { - /// - /// The list of information about the vector indexes available to the user. - /// - public List Indexes { get; } - - /// - /// the list of index information - public Success(List indexes) - { - Indexes = indexes; - } - - /// - public override string ToString() - { - return $"{base.ToString()}: {string.Join(", ", Indexes)}"; - } - - } - - /// - public class Error : ListIndexesResponse, IError - { - /// - public Error(SdkException error) - { - InnerException = error; - } - - /// - public SdkException InnerException { get; } - - /// - public MomentoErrorCode ErrorCode => InnerException.ErrorCode; - - /// - public string Message => $"{InnerException.MessageWrapper}: {InnerException.Message}"; - - /// - public override string ToString() - { - return $"{base.ToString()}: {Message}"; - } - - } -} diff --git a/src/Momento.Sdk/Responses/Vector/SearchAndFetchVectorsResponse.cs b/src/Momento.Sdk/Responses/Vector/SearchAndFetchVectorsResponse.cs deleted file mode 100644 index 8798341d..00000000 --- a/src/Momento.Sdk/Responses/Vector/SearchAndFetchVectorsResponse.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using Momento.Sdk.Exceptions; - -namespace Momento.Sdk.Responses.Vector; - -/// -/// Parent response type for a list vector indexes request. The -/// response object is resolved to a type-safe object of one of -/// the following subtypes: -/// -/// SearchAndFetchVectorsResponse.Success -/// SearchAndFetchVectorsResponse.Error -/// -/// Pattern matching can be used to operate on the appropriate subtype. -/// For example: -/// -/// if (response is SearchAndFetchVectorsResponse.Success successResponse) -/// { -/// return successResponse.Hits; -/// } -/// else if (response is SearchAndFetchVectorsResponse.Error errorResponse) -/// { -/// // handle error as appropriate -/// } -/// else -/// { -/// // handle unexpected response -/// } -/// -/// -public abstract class SearchAndFetchVectorsResponse -{ - /// - public class Success : SearchAndFetchVectorsResponse - { - /// - /// The list of hits returned by the search. - /// - public List Hits { get; } - - /// - /// the search results - public Success(List hits) - { - Hits = hits; - } - - /// - public override string ToString() - { - var displayedHits = Hits.Take(5).Select(hit => $"{hit.Id} ({hit.Score})"); - return $"{base.ToString()}: {string.Join(", ", displayedHits)}..."; - } - - } - - /// - public class Error : SearchAndFetchVectorsResponse, IError - { - /// - public Error(SdkException error) - { - InnerException = error; - } - - /// - public SdkException InnerException { get; } - - /// - public MomentoErrorCode ErrorCode => InnerException.ErrorCode; - - /// - public string Message => $"{InnerException.MessageWrapper}: {InnerException.Message}"; - - /// - public override string ToString() - { - return $"{base.ToString()}: {Message}"; - } - - } -} diff --git a/src/Momento.Sdk/Responses/Vector/SearchHit.cs b/src/Momento.Sdk/Responses/Vector/SearchHit.cs deleted file mode 100644 index a86c2613..00000000 --- a/src/Momento.Sdk/Responses/Vector/SearchHit.cs +++ /dev/null @@ -1,150 +0,0 @@ -using System.Linq; -using Momento.Sdk.Messages.Vector; -using Momento.Sdk.Internal.ExtensionMethods; - -namespace Momento.Sdk.Responses.Vector; - -using System.Collections.Generic; - -/// -/// A hit from a vector search. Contains the ID of the vector, the search score, -/// and any requested metadata. -/// -public class SearchHit -{ - /// - /// The ID of the hit. - /// - public string Id { get; } - - /// - /// The similarity to the query vector. - /// - public double Score { get; } - - /// - /// Requested metadata associated with the hit. - /// - public Dictionary Metadata { get; } - - /// - /// Constructs a SearchHit with no metadata. - /// - /// The ID of the hit. - /// The similarity to the query vector. - public SearchHit(string id, double score) - { - Id = id; - Score = score; - Metadata = new Dictionary(); - } - - /// - /// Constructs a SearchHit. - /// - /// The ID of the hit. - /// The similarity to the query vector. - /// Requested metadata associated with the hit - public SearchHit(string id, double score, Dictionary metadata) - { - Id = id; - Score = score; - Metadata = metadata; - } - - /// - public override bool Equals(object obj) - { - if (ReferenceEquals(this, obj)) return true; - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - if (obj is null || GetType() != obj.GetType()) return false; - - var other = (SearchHit)obj; - - // ReSharper disable once CompareOfFloatsByEqualityOperator - if (Id != other.Id || Score != other.Score) return false; - - // Compare Metadata dictionaries - return Metadata.MetadataEquals(other.Metadata); - } - - /// - public override int GetHashCode() - { - unchecked // Overflow is fine, just wrap - { - var hash = 17; - - hash = hash * 23 + Id.GetHashCode(); - hash = hash * 23 + Score.GetHashCode(); - hash = hash * 23 + Metadata.MetadataHashCode(); - - return hash; - } - } -} - -/// -/// A hit from a vector search and fetch vectors. Contains the ID of the vector, the search score, -/// the vector, and any requested metadata. -/// -public class SearchAndFetchVectorsHit : SearchHit -{ - /// - /// The similarity to the query vector. - /// - public List Vector { get; } - - /// - /// Constructs a SearchAndFetchVectorsHit with no metadata. - /// - /// The ID of the hit. - /// The similarity to the query vector. - /// The vector of the hit. - public SearchAndFetchVectorsHit(string id, double score, List vector) : base(id, score) - { - Vector = vector; - } - - /// - /// Constructs a SearchAndFetchVectorsHit. - /// - /// The ID of the hit. - /// The similarity to the query vector. - /// The vector of the hit. - /// Requested metadata associated with the hit - public SearchAndFetchVectorsHit(string id, double score, List vector, - Dictionary metadata) : base(id, score, metadata) - { - Vector = vector; - } - - /// - /// Constructs a SearchAndFetchVectorsHit from a SearchHit. - /// - /// A SearchHit containing an ID, score, and metadata - /// The vector of the hit. - public SearchAndFetchVectorsHit(SearchHit searchHit, List vector) : base(searchHit.Id, searchHit.Score, - searchHit.Metadata) - { - Vector = vector; - } - - /// - public override bool Equals(object obj) - { - if (ReferenceEquals(this, obj)) return true; - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - if (obj is null || GetType() != obj.GetType()) return false; - - var other = (SearchAndFetchVectorsHit)obj; - - return base.Equals(other) && Vector.SequenceEqual(other.Vector); - } - - /// - public override int GetHashCode() - { - return base.GetHashCode() ^ Vector.GetHashCode(); - } -} diff --git a/src/Momento.Sdk/Responses/Vector/SearchResponse.cs b/src/Momento.Sdk/Responses/Vector/SearchResponse.cs deleted file mode 100644 index 0072f380..00000000 --- a/src/Momento.Sdk/Responses/Vector/SearchResponse.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using Momento.Sdk.Exceptions; - -namespace Momento.Sdk.Responses.Vector; - -/// -/// Parent response type for a list vector indexes request. The -/// response object is resolved to a type-safe object of one of -/// the following subtypes: -/// -/// SearchResponse.Success -/// SearchResponse.Error -/// -/// Pattern matching can be used to operate on the appropriate subtype. -/// For example: -/// -/// if (response is SearchResponse.Success successResponse) -/// { -/// return successResponse.Hits; -/// } -/// else if (response is SearchResponse.Error errorResponse) -/// { -/// // handle error as appropriate -/// } -/// else -/// { -/// // handle unexpected response -/// } -/// -/// -public abstract class SearchResponse -{ - /// - public class Success : SearchResponse - { - /// - /// The list of hits returned by the search. - /// - public List Hits { get; } - - /// - /// the search results - public Success(List hits) - { - Hits = hits; - } - - /// - public override string ToString() - { - var displayedHits = Hits.Take(5).Select(hit => $"{hit.Id} ({hit.Score})"); - return $"{base.ToString()}: {string.Join(", ", displayedHits)}..."; - } - - } - - /// - public class Error : SearchResponse, IError - { - /// - public Error(SdkException error) - { - InnerException = error; - } - - /// - public SdkException InnerException { get; } - - /// - public MomentoErrorCode ErrorCode => InnerException.ErrorCode; - - /// - public string Message => $"{InnerException.MessageWrapper}: {InnerException.Message}"; - - /// - public override string ToString() - { - return $"{base.ToString()}: {Message}"; - } - - } -} diff --git a/src/Momento.Sdk/Responses/Vector/UpsertItemBatchResponse.cs b/src/Momento.Sdk/Responses/Vector/UpsertItemBatchResponse.cs deleted file mode 100644 index 45d15725..00000000 --- a/src/Momento.Sdk/Responses/Vector/UpsertItemBatchResponse.cs +++ /dev/null @@ -1,58 +0,0 @@ -namespace Momento.Sdk.Responses.Vector; - -using Exceptions; - -/// -/// Parent response type for a vector upsert item batch request. The -/// response object is resolved to a type-safe object of one of -/// the following subtypes: -/// -/// UpsertItemBatchResponse.Success -/// UpsertItemBatchResponse.Error -/// -/// Pattern matching can be used to operate on the appropriate subtype. -/// For example: -/// -/// if (response is UpsertItemBatchResponse.Success successResponse) -/// { -/// // handle success if needed -/// } -/// else if (response is UpsertItemBatchResponse.Error errorResponse) -/// { -/// // handle error as appropriate -/// } -/// -/// -public abstract class UpsertItemBatchResponse -{ - - /// - public class Success : UpsertItemBatchResponse { } - - /// - public class Error : UpsertItemBatchResponse, IError - { - /// - public Error(SdkException error) - { - InnerException = error; - } - - /// - public SdkException InnerException { get; } - - /// - public MomentoErrorCode ErrorCode => InnerException.ErrorCode; - - /// - public string Message => $"{InnerException.MessageWrapper}: {InnerException.Message}"; - - /// - public override string ToString() - { - return $"{base.ToString()}: {Message}"; - } - - } - -} diff --git a/tests/Integration/Momento.Sdk.Tests/Fixtures.cs b/tests/Integration/Momento.Sdk.Tests/Fixtures.cs index abcf9772..214ab2e5 100644 --- a/tests/Integration/Momento.Sdk.Tests/Fixtures.cs +++ b/tests/Integration/Momento.Sdk.Tests/Fixtures.cs @@ -124,39 +124,3 @@ public class AuthClientCollection : ICollectionFixture { } - -public class VectorIndexClientFixture : IDisposable -{ - public IPreviewVectorIndexClient Client { get; private set; } - public ICredentialProvider AuthProvider { get; private set; } - - public VectorIndexClientFixture() - { - AuthProvider = new EnvMomentoTokenProvider("TEST_AUTH_TOKEN"); - Client = new PreviewVectorIndexClient(VectorIndexConfigurations.Laptop.Latest(LoggerFactory.Create(builder => - { - builder.AddSimpleConsole(options => - { - options.IncludeScopes = true; - options.SingleLine = true; - options.TimestampFormat = "hh:mm:ss "; - }); - builder.AddFilter("Grpc.Net.Client", LogLevel.Error); - builder.SetMinimumLevel(LogLevel.Information); - })), AuthProvider); - } - - public void Dispose() - { - Client.Dispose(); - } -} - -/// -/// Register the fixture in xUnit. -/// -[CollectionDefinition("VectorIndexClient")] -public class VectorIndexClientCollection : ICollectionFixture -{ - -} diff --git a/tests/Integration/Momento.Sdk.Tests/Utils.cs b/tests/Integration/Momento.Sdk.Tests/Utils.cs index 19f7bffd..accb413b 100644 --- a/tests/Integration/Momento.Sdk.Tests/Utils.cs +++ b/tests/Integration/Momento.Sdk.Tests/Utils.cs @@ -1,6 +1,3 @@ -using System; -using Momento.Sdk.Requests.Vector; -using Momento.Sdk.Responses.Vector; namespace Momento.Sdk.Tests.Integration; /// @@ -44,49 +41,4 @@ public static void CreateCacheForTest(ICacheClient cacheClient, string cacheName throw new Exception($"Error when creating cache: {result}"); } } - - public static _WithVectorIndex WithVectorIndex(IPreviewVectorIndexClient vectorIndexClient, IndexInfo indexInfo) - { - return new _WithVectorIndex(vectorIndexClient, indexInfo); - } - - public static _WithVectorIndex WithVectorIndex(IPreviewVectorIndexClient vectorIndexClient, string indexName, int numDimensions, SimilarityMetric similarityMetric = SimilarityMetric.CosineSimilarity) - { - return WithVectorIndex(vectorIndexClient, new IndexInfo(indexName, numDimensions, similarityMetric)); - } - - public class _WithVectorIndex : IDisposable - { - public IPreviewVectorIndexClient VectorIndexClient { get; } - - public IndexInfo IndexInfo { get; } - - public _WithVectorIndex(IPreviewVectorIndexClient vectorIndexClient, IndexInfo indexInfo) - { - VectorIndexClient = vectorIndexClient; - IndexInfo = indexInfo; - - // This usually isn't kosher in a constructor; because we want this class to encapsulate - // index creation and deletion in a using block, the class has to do RAII. - var createResponse = vectorIndexClient.CreateIndexAsync(indexInfo.Name, indexInfo.NumDimensions, indexInfo.SimilarityMetric).Result; - if (createResponse is not (CreateIndexResponse.Success or CreateIndexResponse.AlreadyExists)) - { - throw new Exception($"Error when creating index: {createResponse}"); - } - } - - public _WithVectorIndex(IPreviewVectorIndexClient vectorIndexClient, string indexName, int numDimensions, SimilarityMetric similarityMetric = SimilarityMetric.CosineSimilarity) - : this(vectorIndexClient, new IndexInfo(indexName, numDimensions, similarityMetric)) - { - } - - public void Dispose() - { - var deleteResponse = VectorIndexClient.DeleteIndexAsync(IndexInfo.Name).Result; - if (deleteResponse is not DeleteIndexResponse.Success) - { - throw new Exception($"Error when deleting index: {deleteResponse}"); - } - } - } } diff --git a/tests/Integration/Momento.Sdk.Tests/VectorIndexControlTest.cs b/tests/Integration/Momento.Sdk.Tests/VectorIndexControlTest.cs deleted file mode 100644 index 5b72de83..00000000 --- a/tests/Integration/Momento.Sdk.Tests/VectorIndexControlTest.cs +++ /dev/null @@ -1,88 +0,0 @@ -using System.Linq; -using System.Threading.Tasks; -using Momento.Sdk.Responses.Vector; -using Momento.Sdk.Requests.Vector; -using System.Collections.Generic; - -namespace Momento.Sdk.Tests.Integration; - -public class VectorIndexControlTest : IClassFixture -{ - private readonly IPreviewVectorIndexClient vectorIndexClient; - - public VectorIndexControlTest(VectorIndexClientFixture vectorIndexFixture) - { - vectorIndexClient = vectorIndexFixture.Client; - } - - public static IEnumerable CreateAndListIndexTestData - { - get - { - return new List - { - new object[] { new IndexInfo(Utils.TestVectorIndexName("control-create-and-list-1"), 3, SimilarityMetric.CosineSimilarity) }, - new object[] { new IndexInfo(Utils.TestVectorIndexName("control-create-and-list-2"), 3, SimilarityMetric.InnerProduct) }, - new object[] { new IndexInfo(Utils.TestVectorIndexName("control-create-and-list-3"), 3, SimilarityMetric.EuclideanSimilarity) } - }; - } - } - - [Theory] - [MemberData(nameof(CreateAndListIndexTestData))] - public async Task CreateListDelete_HappyPath(IndexInfo indexInfo) - { - using (Utils.WithVectorIndex(vectorIndexClient, indexInfo)) - { - var listResponse = await vectorIndexClient.ListIndexesAsync(); - Assert.True(listResponse is ListIndexesResponse.Success, $"Unexpected response: {listResponse}"); - var listOk = (ListIndexesResponse.Success)listResponse; - Assert.Contains(indexInfo, listOk.Indexes); - } - } - - [Fact] - public async Task CreateIndexAsync_AlreadyExistsError() - { - var indexName = Utils.TestVectorIndexName("control-create-index-already-exists"); - const int numDimensions = 3; - using (Utils.WithVectorIndex(vectorIndexClient, indexName, numDimensions)) - { - var createAgainResponse = await vectorIndexClient.CreateIndexAsync(indexName, numDimensions); - Assert.True(createAgainResponse is CreateIndexResponse.AlreadyExists, $"Unexpected response: {createAgainResponse}"); - } - } - - [Fact] - public async Task CreateIndexAsync_InvalidIndexName() - { - var createResponse = await vectorIndexClient.CreateIndexAsync(null!, 3); - Assert.True(createResponse is CreateIndexResponse.Error, $"Unexpected response: {createResponse}"); - var createErr = (CreateIndexResponse.Error)createResponse; - Assert.Equal(MomentoErrorCode.INVALID_ARGUMENT_ERROR, createErr.InnerException.ErrorCode); - - createResponse = await vectorIndexClient.CreateIndexAsync("", 3); - Assert.True(createResponse is CreateIndexResponse.Error, $"Unexpected response: {createResponse}"); - createErr = (CreateIndexResponse.Error)createResponse; - Assert.Equal(MomentoErrorCode.INVALID_ARGUMENT_ERROR, createErr.InnerException.ErrorCode); - } - - [Fact] - public async Task CreateIndexAsync_InvalidNumDimensions() - { - var createResponse = await vectorIndexClient.CreateIndexAsync("index", 0); - Assert.True(createResponse is CreateIndexResponse.Error, $"Unexpected response: {createResponse}"); - var createErr = (CreateIndexResponse.Error)createResponse; - Assert.Equal(MomentoErrorCode.INVALID_ARGUMENT_ERROR, createErr.InnerException.ErrorCode); - } - - [Fact] - public async Task DeleteIndexAsync_DoesntExistError() - { - var indexName = Utils.TestVectorIndexName("control-delete-index-doesnt-exist"); - var deleteResponse = await vectorIndexClient.DeleteIndexAsync(indexName); - Assert.True(deleteResponse is DeleteIndexResponse.Error, $"Unexpected response: {deleteResponse}"); - var deleteErr = (DeleteIndexResponse.Error)deleteResponse; - Assert.Equal(MomentoErrorCode.NOT_FOUND_ERROR, deleteErr.InnerException.ErrorCode); - } -} diff --git a/tests/Integration/Momento.Sdk.Tests/VectorIndexDataTest.cs b/tests/Integration/Momento.Sdk.Tests/VectorIndexDataTest.cs deleted file mode 100644 index dc66b6cd..00000000 --- a/tests/Integration/Momento.Sdk.Tests/VectorIndexDataTest.cs +++ /dev/null @@ -1,586 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Momento.Sdk.Messages.Vector; -using Momento.Sdk.Requests.Vector; -using Momento.Sdk.Responses.Vector; - -namespace Momento.Sdk.Tests.Integration; - -public class VectorIndexDataTest : IClassFixture -{ - private readonly IPreviewVectorIndexClient vectorIndexClient; - - public VectorIndexDataTest(VectorIndexClientFixture vectorIndexFixture) - { - vectorIndexClient = vectorIndexFixture.Client; - } - - [Fact] - public async Task UpsertItemBatchAsync_InvalidIndexName() - { - var response = await vectorIndexClient.UpsertItemBatchAsync(null!, new List()); - Assert.True(response is UpsertItemBatchResponse.Error, $"Unexpected response: {response}"); - var error = (UpsertItemBatchResponse.Error)response; - Assert.Equal(MomentoErrorCode.INVALID_ARGUMENT_ERROR, error.InnerException.ErrorCode); - - response = await vectorIndexClient.UpsertItemBatchAsync("", new List()); - Assert.True(response is UpsertItemBatchResponse.Error, $"Unexpected response: {response}"); - error = (UpsertItemBatchResponse.Error)response; - Assert.Equal(MomentoErrorCode.INVALID_ARGUMENT_ERROR, error.InnerException.ErrorCode); - } - - [Fact] - public async Task DeleteItemBatchAsync_InvalidIndexName() - { - var response = await vectorIndexClient.DeleteItemBatchAsync(null!, new List()); - Assert.True(response is DeleteItemBatchResponse.Error, $"Unexpected response: {response}"); - var error = (DeleteItemBatchResponse.Error)response; - Assert.Equal(MomentoErrorCode.INVALID_ARGUMENT_ERROR, error.InnerException.ErrorCode); - - response = await vectorIndexClient.DeleteItemBatchAsync("", new List()); - Assert.True(response is DeleteItemBatchResponse.Error, $"Unexpected response: {response}"); - error = (DeleteItemBatchResponse.Error)response; - Assert.Equal(MomentoErrorCode.INVALID_ARGUMENT_ERROR, error.InnerException.ErrorCode); - } - - [Fact] - public async Task SearchAsync_InvalidIndexName() - { - var response = await vectorIndexClient.SearchAsync(null!, new List { 1.0f }); - Assert.True(response is SearchResponse.Error, $"Unexpected response: {response}"); - var error = (SearchResponse.Error)response; - Assert.Equal(MomentoErrorCode.INVALID_ARGUMENT_ERROR, error.InnerException.ErrorCode); - - response = await vectorIndexClient.SearchAsync("", new List { 1.0f }); - Assert.True(response is SearchResponse.Error, $"Unexpected response: {response}"); - error = (SearchResponse.Error)response; - Assert.Equal(MomentoErrorCode.INVALID_ARGUMENT_ERROR, error.InnerException.ErrorCode); - } - - public delegate Task SearchDelegate(IPreviewVectorIndexClient client, string indexName, - IEnumerable queryVector, int topK = 10, - MetadataFields? metadataFields = null, float? scoreThreshold = null); - - public delegate void AssertOnSearchResponse(T response, List expectedHits, - List> expectedVectors); - - public static IEnumerable UpsertAndSearchTestData - { - get - { - return new List - { - new object[] - { - new SearchDelegate( - (client, indexName, queryVector, topK, metadata, scoreThreshold) => - client.SearchAsync(indexName, queryVector, topK, metadata, scoreThreshold)), - new AssertOnSearchResponse((response, expectedHits, _) => - { - Assert.True(response is SearchResponse.Success, $"Unexpected response: {response}"); - var successResponse = (SearchResponse.Success)response; - Assert.Equal(expectedHits, successResponse.Hits); - }) - }, - new object[] - { - new SearchDelegate( - (client, indexName, queryVector, topK, metadata, scoreThreshold) => - client.SearchAndFetchVectorsAsync(indexName, queryVector, topK, metadata, - scoreThreshold)), - new AssertOnSearchResponse( - (response, expectedHits, expectedVectors) => - { - Assert.True(response is SearchAndFetchVectorsResponse.Success, - $"Unexpected response: {response}"); - var successResponse = (SearchAndFetchVectorsResponse.Success)response; - var expectedHitsAndVectors = expectedHits.Zip(expectedVectors, - (h, v) => new SearchAndFetchVectorsHit(h, v)); - Assert.Equal(expectedHitsAndVectors, successResponse.Hits); - }) - } - }; - } - } - - [Theory] - [MemberData(nameof(UpsertAndSearchTestData))] - public async Task UpsertAndSearch_InnerProduct(SearchDelegate searchDelegate, - AssertOnSearchResponse assertOnSearchResponse) - { - var indexName = Utils.TestVectorIndexName("data-upsert-and-search-inner-product"); - using (Utils.WithVectorIndex(vectorIndexClient, indexName, 2, SimilarityMetric.InnerProduct)) - { - var items = new List - { - new("test_item", new List { 1.0f, 2.0f }) - }; - - var upsertResponse = await vectorIndexClient.UpsertItemBatchAsync(indexName, items); - Assert.True(upsertResponse is UpsertItemBatchResponse.Success, - $"Unexpected response: {upsertResponse}"); - - await Task.Delay(2_000); - - var searchResponse = - await searchDelegate.Invoke(vectorIndexClient, indexName, new List { 1.0f, 2.0f }); - assertOnSearchResponse.Invoke(searchResponse, new List - { - new("test_item", 5.0f) - }, items.Select(i => i.Vector).ToList()); - } - } - - [Theory] - [MemberData(nameof(UpsertAndSearchTestData))] - public async Task UpsertAndSearch_CosineSimilarity(SearchDelegate searchDelegate, - AssertOnSearchResponse assertOnSearchResponse) - { - var indexName = Utils.TestVectorIndexName("data-upsert-and-search-cosine-similarity"); - using (Utils.WithVectorIndex(vectorIndexClient, indexName, 2)) - { - var items = new List - { - new("test_item_1", new List { 1.0f, 1.0f }), - new("test_item_2", new List { -1.0f, 1.0f }), - new("test_item_3", new List { -1.0f, -1.0f }) - }; - var upsertResponse = await vectorIndexClient.UpsertItemBatchAsync(indexName, items); - Assert.True(upsertResponse is UpsertItemBatchResponse.Success, - $"Unexpected response: {upsertResponse}"); - - await Task.Delay(2_000); - - var searchResponse = - await searchDelegate.Invoke(vectorIndexClient, indexName, new List { 2.0f, 2.0f }); - assertOnSearchResponse.Invoke(searchResponse, new List - { - new("test_item_1", 1.0f), - new("test_item_2", 0.0f), - new("test_item_3", -1.0f) - }, items.Select(i => i.Vector).ToList()); - } - } - - [Theory] - [MemberData(nameof(UpsertAndSearchTestData))] - public async Task UpsertAndSearch_EuclideanSimilarity(SearchDelegate searchDelegate, - AssertOnSearchResponse assertOnSearchResponse) - { - var indexName = Utils.TestVectorIndexName("data-upsert-and-search-euclidean-similarity"); - using (Utils.WithVectorIndex(vectorIndexClient, indexName, 2, SimilarityMetric.EuclideanSimilarity)) - { - var items = new List - { - new("test_item_1", new List { 1.0f, 1.0f }), - new("test_item_2", new List { -1.0f, 1.0f }), - new("test_item_3", new List { -1.0f, -1.0f }) - }; - var upsertResponse = await vectorIndexClient.UpsertItemBatchAsync(indexName, items); - Assert.True(upsertResponse is UpsertItemBatchResponse.Success, - $"Unexpected response: {upsertResponse}"); - - await Task.Delay(2_000); - - var searchResponse = - await searchDelegate.Invoke(vectorIndexClient, indexName, new List { 1.0f, 1.0f }); - assertOnSearchResponse.Invoke(searchResponse, new List - { - new("test_item_1", 0.0f), - new("test_item_2", 4.0f), - new("test_item_3", 8.0f) - }, items.Select(i => i.Vector).ToList()); - } - } - - [Theory] - [MemberData(nameof(UpsertAndSearchTestData))] - public async Task UpsertAndSearch_TopKLimit(SearchDelegate searchDelegate, - AssertOnSearchResponse assertOnSearchResponse) - { - var indexName = Utils.TestVectorIndexName("data-upsert-and-search-top-k-limit"); - using (Utils.WithVectorIndex(vectorIndexClient, indexName, 2, SimilarityMetric.InnerProduct)) - { - var items = new List - { - new("test_item_1", new List { 1.0f, 2.0f }), - new("test_item_2", new List { 3.0f, 4.0f }), - new("test_item_3", new List { 5.0f, 6.0f }) - }; - var upsertResponse = await vectorIndexClient.UpsertItemBatchAsync(indexName, items); - Assert.True(upsertResponse is UpsertItemBatchResponse.Success, - $"Unexpected response: {upsertResponse}"); - - await Task.Delay(2_000); - - var searchResponse = - await searchDelegate.Invoke(vectorIndexClient, indexName, new List { 1.0f, 2.0f }, topK: 2); - assertOnSearchResponse.Invoke(searchResponse, new List - { - new("test_item_3", 17.0f), - new("test_item_2", 11.0f) - }, new List> - { - new() { 5.0f, 6.0f }, - new() { 3.0f, 4.0f } - }); - } - } - - [Theory] - [MemberData(nameof(UpsertAndSearchTestData))] - public async Task UpsertAndSearch_WithMetadata(SearchDelegate searchDelegate, - AssertOnSearchResponse assertOnSearchResponse) - { - var indexName = Utils.TestVectorIndexName("data-upsert-and-search-with-metadata"); - using (Utils.WithVectorIndex(vectorIndexClient, indexName, 2, SimilarityMetric.InnerProduct)) - { - var items = new List - { - new("test_item_1", new List { 1.0f, 2.0f }, - new Dictionary { { "key1", "value1" } }), - new("test_item_2", new List { 3.0f, 4.0f }, - new Dictionary { { "key2", "value2" } }), - new("test_item_3", new List { 5.0f, 6.0f }, - new Dictionary - { { "key1", "value3" }, { "key3", "value3" } }) - }; - var upsertResponse = await vectorIndexClient.UpsertItemBatchAsync(indexName, items); - Assert.True(upsertResponse is UpsertItemBatchResponse.Success, - $"Unexpected response: {upsertResponse}"); - - await Task.Delay(2_000); - - var expectedVectors = new List> - { - new() { 5.0f, 6.0f }, - new() { 3.0f, 4.0f }, - new() { 1.0f, 2.0f } - }; - var searchResponse = - await searchDelegate.Invoke(vectorIndexClient, indexName, new List { 1.0f, 2.0f }, 3, - new List { "key1" }); - assertOnSearchResponse.Invoke(searchResponse, new List - { - new("test_item_3", 17.0f, - new Dictionary { { "key1", "value3" } }), - new("test_item_2", 11.0f, new Dictionary()), - new("test_item_1", 5.0f, - new Dictionary { { "key1", "value1" } }) - }, expectedVectors); - - searchResponse = - await searchDelegate.Invoke(vectorIndexClient, indexName, new List { 1.0f, 2.0f }, 3, - new List { "key1", "key2", "key3", "key4" }); - assertOnSearchResponse.Invoke(searchResponse, new List - { - new("test_item_3", 17.0f, - new Dictionary - { { "key1", "value3" }, { "key3", "value3" } }), - new("test_item_2", 11.0f, - new Dictionary { { "key2", "value2" } }), - new("test_item_1", 5.0f, - new Dictionary { { "key1", "value1" } }) - }, expectedVectors); - - searchResponse = - await searchDelegate.Invoke(vectorIndexClient, indexName, new List { 1.0f, 2.0f }, 3, - MetadataFields.All); - assertOnSearchResponse.Invoke(searchResponse, new List - { - new("test_item_3", 17.0f, - new Dictionary - { { "key1", "value3" }, { "key3", "value3" } }), - new("test_item_2", 11.0f, - new Dictionary { { "key2", "value2" } }), - new("test_item_1", 5.0f, - new Dictionary { { "key1", "value1" } }) - }, expectedVectors); - } - } - - [Theory] - [MemberData(nameof(UpsertAndSearchTestData))] - public async Task UpsertAndSearch_WithDiverseMetadata(SearchDelegate searchDelegate, - AssertOnSearchResponse assertOnSearchResponse) - { - var indexName = Utils.TestVectorIndexName("data-upsert-and-search-with-diverse-metadata"); - using (Utils.WithVectorIndex(vectorIndexClient, indexName, 2, SimilarityMetric.InnerProduct)) - { - var metadata = new Dictionary - { - { "string_key", "string_value" }, - { "long_key", 123 }, - { "double_key", 3.14 }, - { "bool_key", true }, - { "list_key", new List { "a", "b", "c" } }, - { "empty_list_key", new List() } - }; - var items = new List - { - new("test_item_1", new List { 1.0f, 2.0f }, metadata) - }; - - var upsertResponse = await vectorIndexClient.UpsertItemBatchAsync(indexName, items); - Assert.True(upsertResponse is UpsertItemBatchResponse.Success, - $"Unexpected response: {upsertResponse}"); - - await Task.Delay(2_000); - - var searchResponse = - await searchDelegate.Invoke(vectorIndexClient, indexName, new List { 1.0f, 2.0f }, 1, - MetadataFields.All); - assertOnSearchResponse.Invoke(searchResponse, new List - { - new("test_item_1", 5.0f, metadata) - }, items.Select(i => i.Vector).ToList()); - } - } - - public static IEnumerable SearchThresholdTestCases => - new List - { - // similarity metric, scores, thresholds - new object[] - { - SimilarityMetric.CosineSimilarity, - new List { 1.0f, 0.0f, -1.0f }, - new List { 0.5f, -1.01f, 1.0f } - }, - new object[] - { - SimilarityMetric.InnerProduct, - new List { 4.0f, 0.0f, -4.0f }, - new List { 0.0f, -4.01f, 4.0f } - }, - new object[] - { - SimilarityMetric.EuclideanSimilarity, - new List { 2.0f, 10.0f, 18.0f }, - new List { 3.0f, 20.0f, -0.01f } - } - }; - - // Combine the search threshold parameters and the search/search with vectors parameters - public static IEnumerable UpsertAndSearchThresholdTestCases => - SearchThresholdTestCases.SelectMany( - _ => UpsertAndSearchTestData, - (firstArray, secondArray) => firstArray.Concat(secondArray).ToArray()); - - [Theory] - [MemberData(nameof(UpsertAndSearchThresholdTestCases))] - public async Task Search_PruneBasedOnThreshold(SimilarityMetric similarityMetric, List scores, - List thresholds, SearchDelegate searchDelegate, AssertOnSearchResponse assertOnSearchResponse) - { - var indexName = Utils.TestVectorIndexName("data-search-prune-based-on-threshold"); - using (Utils.WithVectorIndex(vectorIndexClient, indexName, 2, similarityMetric)) - { - var items = new List - { - new("test_item_1", new List { 1.0f, 1.0f }), - new("test_item_2", new List { -1.0f, 1.0f }), - new("test_item_3", new List { -1.0f, -1.0f }) - }; - var upsertResponse = await vectorIndexClient.UpsertItemBatchAsync(indexName, items); - Assert.True(upsertResponse is UpsertItemBatchResponse.Success, - $"Unexpected response: {upsertResponse}"); - - await Task.Delay(2_000); - - var queryVector = new List { 2.0f, 2.0f }; - var searchHits = new List - { - new("test_item_1", scores[0]), - new("test_item_2", scores[1]), - new("test_item_3", scores[2]) - }; - - // Test threshold to get only the top result - var searchResponse = - await searchDelegate.Invoke(vectorIndexClient, indexName, queryVector, 3, scoreThreshold: thresholds[0]); - assertOnSearchResponse.Invoke(searchResponse, new List - { - searchHits[0] - }, items.FindAll(i => i.Id == "test_item_1").Select(i => i.Vector).ToList()); - - // Test threshold to get all results - searchResponse = - await searchDelegate.Invoke(vectorIndexClient, indexName, queryVector, 3, scoreThreshold: thresholds[1]); - assertOnSearchResponse.Invoke(searchResponse, searchHits, items.Select(i => i.Vector).ToList()); - - // Test threshold to get no results - searchResponse = - await searchDelegate.Invoke(vectorIndexClient, indexName, queryVector, 3, scoreThreshold: thresholds[2]); - assertOnSearchResponse.Invoke(searchResponse, new List(), new List>()); - } - } - - public delegate Task GetItemDelegate(IPreviewVectorIndexClient client, string indexName, IEnumerable ids); - public static GetItemDelegate GetItemBatchDelegate = new( - (client, indexName, ids) => client.GetItemBatchAsync(indexName, ids)); - public static GetItemDelegate GetItemMetadataBatchDelegate = new( - (client, indexName, ids) => client.GetItemMetadataBatchAsync(indexName, ids)); - public delegate void AssertOnGetItemResponse(T response, Object expected); - public static AssertOnGetItemResponse AssertOnGetItemBatchResponse = new( - (response, expected) => - { - Assert.True(response is GetItemBatchResponse.Success, $"Unexpected response: {response}"); - var successResponse = (GetItemBatchResponse.Success)response; - Assert.Equal(expected, successResponse.Values); - }); - public static AssertOnGetItemResponse AssertOnGetItemMetadataBatchResponse = new( - (response, expected) => - { - Assert.True(response is GetItemMetadataBatchResponse.Success, $"Unexpected response: {response}"); - var successResponse = (GetItemMetadataBatchResponse.Success)response; - Assert.Equal(expected, successResponse.Values); - }); - public static IEnumerable GetItemAndGetItemMetadataTestData - { - get - { - return new List - { - new object[] - { - GetItemBatchDelegate, - AssertOnGetItemBatchResponse, - new List { }, - new Dictionary() - }, - new object[] - { - GetItemMetadataBatchDelegate, - AssertOnGetItemMetadataBatchResponse, - new List { }, - new Dictionary>() - }, - new object[] - { - GetItemBatchDelegate, - AssertOnGetItemBatchResponse, - new List { "missing_id" }, - new Dictionary() - }, - new object[] - { - GetItemMetadataBatchDelegate, - AssertOnGetItemMetadataBatchResponse, - new List { "missing_id" }, - new Dictionary>() - }, - new object[] - { - GetItemBatchDelegate, - AssertOnGetItemBatchResponse, - new List { "test_item_1", "missing_id", "test_item_2" }, - new Dictionary - { - { "test_item_1", new Item("test_item_1", new List { 1.0f, 2.0f }, new Dictionary { { "key1", "value1" } }) }, - { "test_item_2", new Item("test_item_2", new List { 3.0f, 4.0f }) } - } - }, - new object[] - { - GetItemMetadataBatchDelegate, - AssertOnGetItemMetadataBatchResponse, - new List { "test_item_1", "missing_id", "test_item_2" }, - new Dictionary> - { - { "test_item_1", new Dictionary { { "key1", "value1" } } }, - { "test_item_2", new Dictionary() } - } - } - }; - } - } - - [Theory] - [MemberData(nameof(GetItemAndGetItemMetadataTestData))] - public async Task GetItemAndGetItemMetadata_HappyPath(GetItemDelegate getItemDelegate, AssertOnGetItemResponse assertOnGetItemResponse, IEnumerable ids, Object expected) - { - var indexName = Utils.TestVectorIndexName("data-get-item-and-get-item-metadata-happy-path"); - using (Utils.WithVectorIndex(vectorIndexClient, indexName, 2, SimilarityMetric.InnerProduct)) - { - var items = new List - { - new("test_item_1", new List { 1.0f, 2.0f }, new Dictionary { { "key1", "value1" } }), - new("test_item_2", new List { 3.0f, 4.0f }), - new("test_item_3", new List { 5.0f, 6.0f }), - }; - - var upsertResponse = await vectorIndexClient.UpsertItemBatchAsync(indexName, items); - Assert.True(upsertResponse is UpsertItemBatchResponse.Success, - $"Unexpected response: {upsertResponse}"); - - await Task.Delay(2_000); - - var getResponse = await getItemDelegate.Invoke(vectorIndexClient, indexName, ids); - assertOnGetItemResponse.Invoke(getResponse, expected); - } - } - - [Fact] - public async Task CountItemsAsync_OnMissingIndex_ReturnsError() - { - var indexName = Utils.NewGuidString(); - var response = await vectorIndexClient.CountItemsAsync(indexName); - Assert.True(response is CountItemsResponse.Error, $"Unexpected response: {response}"); - var error = (CountItemsResponse.Error)response; - Assert.Equal(MomentoErrorCode.NOT_FOUND_ERROR, error.InnerException.ErrorCode); - } - - [Fact] - public async Task CountItemsAsync_OnEmptyIndex_ReturnsZero() - { - var indexName = Utils.TestVectorIndexName("data-count-items-on-empty-index"); - using (Utils.WithVectorIndex(vectorIndexClient, indexName, 2, SimilarityMetric.InnerProduct)) - { - var response = await vectorIndexClient.CountItemsAsync(indexName); - Assert.True(response is CountItemsResponse.Success, $"Unexpected response: {response}"); - var successResponse = (CountItemsResponse.Success)response; - Assert.Equal(0, successResponse.ItemCount); - } - } - - [Fact] - public async Task CountItemsAsync_HasItems_CountsCorrectly() - { - var indexName = Utils.TestVectorIndexName("data-count-items-has-items-counts-correctly"); - using (Utils.WithVectorIndex(vectorIndexClient, indexName, 2, SimilarityMetric.InnerProduct)) - { - var items = new List - { - new("test_item_1", new List { 1.0f, 2.0f }), - new("test_item_2", new List { 3.0f, 4.0f }), - new("test_item_3", new List { 5.0f, 6.0f }), - new("test_item_4", new List { 7.0f, 8.0f }), - new("test_item_5", new List { 9.0f, 10.0f }), - }; - - var upsertResponse = await vectorIndexClient.UpsertItemBatchAsync(indexName, items); - Assert.True(upsertResponse is UpsertItemBatchResponse.Success, - $"Unexpected response: {upsertResponse}"); - - await Task.Delay(2_000); - - var response = await vectorIndexClient.CountItemsAsync(indexName); - Assert.True(response is CountItemsResponse.Success, $"Unexpected response: {response}"); - var successResponse = (CountItemsResponse.Success)response; - Assert.Equal(5, successResponse.ItemCount); - - // Delete two items - var deleteResponse = await vectorIndexClient.DeleteItemBatchAsync(indexName, - new List { "test_item_1", "test_item_2" }); - Assert.True(deleteResponse is DeleteItemBatchResponse.Success, $"Unexpected response: {deleteResponse}"); - - await Task.Delay(2_000); - - response = await vectorIndexClient.CountItemsAsync(indexName); - Assert.True(response is CountItemsResponse.Success, $"Unexpected response: {response}"); - successResponse = (CountItemsResponse.Success)response; - Assert.Equal(3, successResponse.ItemCount); - } - } -}