Skip to content

Commit

Permalink
feat: add mvi list indexes detail (#519)
Browse files Browse the repository at this point in the history
Add MVI list indexes detail to IndexInfo object and update integration tests.
  • Loading branch information
malandis authored Nov 15, 2023
1 parent 34940fb commit 041d537
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 36 deletions.
16 changes: 14 additions & 2 deletions src/Momento.Sdk/Internal/VectorIndexControlClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,26 @@ public async Task<ListIndexesResponse> ListIndexesAsync()
var response = await grpcManager.Client.ListIndexesAsync(request, new CallOptions(deadline: CalculateDeadline()));
return _logger.LogTraceGenericRequestSuccess("listVectorIndexes",
new ListIndexesResponse.Success(
new List<IndexInfo>(response.Indexes.Select(n => new IndexInfo(n.IndexName)))));
new List<IndexInfo>(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<DeleteIndexResponse> DeleteIndexAsync(string indexName)
{
try
Expand All @@ -102,7 +114,7 @@ private static void CheckValidIndexName(string indexName)
throw new InvalidArgumentException("Index name must be nonempty");
}
}

private static ulong ValidateNumDimensions(long numDimensions)
{
if (numDimensions <= 0)
Expand Down
36 changes: 31 additions & 5 deletions src/Momento.Sdk/Responses/Vector/IndexInfo.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using Momento.Sdk.Requests.Vector;

namespace Momento.Sdk.Responses.Vector;

/// <summary>
Expand All @@ -10,30 +12,54 @@ public class IndexInfo
/// </summary>
public string Name { get; }

/// <summary>
/// The number of dimensions in the index.
/// </summary>
public int NumDimensions { get; }

/// <summary>
/// The similarity metric used by the index.
/// </summary>
public SimilarityMetric SimilarityMetric { get; }

/// <summary>
/// Constructs an IndexInfo.
/// </summary>
/// <param name="name">The name of the index.</param>
public IndexInfo(string name)
/// <param name="numDimensions">The number of dimensions in the index.</param>
/// <param name="similarityMetric">The similarity metric used by the index.</param>
public IndexInfo(string name, int numDimensions, SimilarityMetric similarityMetric)
{
Name = name;
NumDimensions = numDimensions;
SimilarityMetric = similarityMetric;
}


/// <inheritdoc />
public override bool Equals(object obj)
{
return obj is IndexInfo other && Name == other.Name;
return obj is IndexInfo other && Name == other.Name && NumDimensions == other.NumDimensions && SimilarityMetric == other.SimilarityMetric;
}

/// <inheritdoc />
public override int GetHashCode()
{
return Name.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;
}
}

/// <inheritdoc />
public override string ToString()
{
return $"IndexInfo {{ Name = {Name} }}";
return $"IndexInfo {{ Name = {Name} NumDimensions = {NumDimensions} SimilarityMetric = {SimilarityMetric} }}";
}
}
}
8 changes: 5 additions & 3 deletions tests/Integration/Momento.Sdk.Tests/Utils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ namespace Momento.Sdk.Tests.Integration;
/// </summary>
public static class Utils
{

public static string TestCacheName() => "dotnet-integration-" + NewGuidString();


public static string TestVectorIndexName() => "dotnet-integration-" + NewGuidString();

public static string NewGuidString() => Guid.NewGuid().ToString();

public static byte[] NewGuidByteArray() => Guid.NewGuid().ToByteArray();
Expand All @@ -28,4 +30,4 @@ public static void CreateCacheForTest(ICacheClient cacheClient, string cacheName
throw new Exception($"Error when creating cache: {result}");
}
}
}
}
47 changes: 30 additions & 17 deletions tests/Integration/Momento.Sdk.Tests/VectorIndexControlTest.cs
Original file line number Diff line number Diff line change
@@ -1,68 +1,81 @@
using System.Linq;
using System.Threading.Tasks;
using Momento.Sdk.Responses.Vector;
using Momento.Sdk.Responses.Vector;
using Momento.Sdk.Requests.Vector;
using System.Collections.Generic;

namespace Momento.Sdk.Tests.Integration;

public class VectorIndexControlTest : IClassFixture<VectorIndexClientFixture>
{
private readonly IPreviewVectorIndexClient vectorIndexClient;

public VectorIndexControlTest(VectorIndexClientFixture vectorIndexFixture)
{
vectorIndexClient = vectorIndexFixture.Client;
}

[Fact]
public async Task CreateListDelete_HappyPath()
public static IEnumerable<object[]> CreateAndListIndexTestData
{
var indexName = $"dotnet-integration-{Utils.NewGuidString()}";
const int numDimensions = 3;
get
{
return new List<object[]>
{
new object[] { new IndexInfo(Utils.TestVectorIndexName(), 3, SimilarityMetric.CosineSimilarity) },
new object[] { new IndexInfo(Utils.TestVectorIndexName(), 3, SimilarityMetric.InnerProduct) },
new object[] { new IndexInfo(Utils.TestVectorIndexName(), 3, SimilarityMetric.EuclideanSimilarity) }
};
}
}

[Theory]
[MemberData(nameof(CreateAndListIndexTestData))]
public async Task CreateListDelete_HappyPath(IndexInfo indexInfo)
{
try
{
var createResponse = await vectorIndexClient.CreateIndexAsync(indexName, numDimensions);
var createResponse = await vectorIndexClient.CreateIndexAsync(indexInfo.Name, indexInfo.NumDimensions, indexInfo.SimilarityMetric);
Assert.True(createResponse is CreateIndexResponse.Success, $"Unexpected response: {createResponse}");

var listResponse = await vectorIndexClient.ListIndexesAsync();
Assert.True(listResponse is ListIndexesResponse.Success, $"Unexpected response: {listResponse}");
var listOk = (ListIndexesResponse.Success)listResponse;
Assert.Contains(listOk.Indexes.Select(i => i.Name), name => name == indexName);
Assert.Contains(indexInfo, listOk.Indexes);
}
finally
{
var deleteResponse = await vectorIndexClient.DeleteIndexAsync(indexName);
var deleteResponse = await vectorIndexClient.DeleteIndexAsync(indexInfo.Name);
Assert.True(deleteResponse is DeleteIndexResponse.Success, $"Unexpected response: {deleteResponse}");
}
}

[Fact]
public async Task CreateIndexAsync_AlreadyExistsError()
{
var indexName = $"index-{Utils.NewGuidString()}";
var indexName = Utils.TestVectorIndexName();
const int numDimensions = 3;

var createResponse = await vectorIndexClient.CreateIndexAsync(indexName, numDimensions);
Assert.True(createResponse is CreateIndexResponse.Success, $"Unexpected response: {createResponse}");

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()
{
Expand All @@ -75,10 +88,10 @@ public async Task CreateIndexAsync_InvalidNumDimensions()
[Fact]
public async Task DeleteIndexAsync_DoesntExistError()
{
var indexName = $"index-{Utils.NewGuidString()}";
var indexName = Utils.TestVectorIndexName();
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);
}
}
}
18 changes: 9 additions & 9 deletions tests/Integration/Momento.Sdk.Tests/VectorIndexDataTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ public static IEnumerable<object[]> UpsertAndSearchTestData
public async Task UpsertAndSearch_InnerProduct<T>(SearchDelegate<T> searchDelegate,
AssertOnSearchResponse<T> assertOnSearchResponse)
{
var indexName = $"index-{Utils.NewGuidString()}";
var indexName = Utils.TestVectorIndexName();

var createResponse = await vectorIndexClient.CreateIndexAsync(indexName, 2, SimilarityMetric.InnerProduct);
Assert.True(createResponse is CreateIndexResponse.Success, $"Unexpected response: {createResponse}");
Expand Down Expand Up @@ -145,7 +145,7 @@ public async Task UpsertAndSearch_InnerProduct<T>(SearchDelegate<T> searchDelega
public async Task UpsertAndSearch_CosineSimilarity<T>(SearchDelegate<T> searchDelegate,
AssertOnSearchResponse<T> assertOnSearchResponse)
{
var indexName = $"index-{Utils.NewGuidString()}";
var indexName = Utils.TestVectorIndexName();

var createResponse = await vectorIndexClient.CreateIndexAsync(indexName, 2);
Assert.True(createResponse is CreateIndexResponse.Success, $"Unexpected response: {createResponse}");
Expand Down Expand Up @@ -184,7 +184,7 @@ public async Task UpsertAndSearch_CosineSimilarity<T>(SearchDelegate<T> searchDe
public async Task UpsertAndSearch_EuclideanSimilarity<T>(SearchDelegate<T> searchDelegate,
AssertOnSearchResponse<T> assertOnSearchResponse)
{
var indexName = $"index-{Utils.NewGuidString()}";
var indexName = Utils.TestVectorIndexName();

var createResponse =
await vectorIndexClient.CreateIndexAsync(indexName, 2, SimilarityMetric.EuclideanSimilarity);
Expand Down Expand Up @@ -224,7 +224,7 @@ public async Task UpsertAndSearch_EuclideanSimilarity<T>(SearchDelegate<T> searc
public async Task UpsertAndSearch_TopKLimit<T>(SearchDelegate<T> searchDelegate,
AssertOnSearchResponse<T> assertOnSearchResponse)
{
var indexName = $"index-{Utils.NewGuidString()}";
var indexName = Utils.TestVectorIndexName();

var createResponse = await vectorIndexClient.CreateIndexAsync(indexName, 2, SimilarityMetric.InnerProduct);
Assert.True(createResponse is CreateIndexResponse.Success, $"Unexpected response: {createResponse}");
Expand Down Expand Up @@ -266,7 +266,7 @@ public async Task UpsertAndSearch_TopKLimit<T>(SearchDelegate<T> searchDelegate,
public async Task UpsertAndSearch_WithMetadata<T>(SearchDelegate<T> searchDelegate,
AssertOnSearchResponse<T> assertOnSearchResponse)
{
var indexName = $"index-{Utils.NewGuidString()}";
var indexName = Utils.TestVectorIndexName();

var createResponse = await vectorIndexClient.CreateIndexAsync(indexName, 2, SimilarityMetric.InnerProduct);
Assert.True(createResponse is CreateIndexResponse.Success, $"Unexpected response: {createResponse}");
Expand Down Expand Up @@ -346,7 +346,7 @@ public async Task UpsertAndSearch_WithMetadata<T>(SearchDelegate<T> searchDelega
public async Task UpsertAndSearch_WithDiverseMetadata<T>(SearchDelegate<T> searchDelegate,
AssertOnSearchResponse<T> assertOnSearchResponse)
{
var indexName = $"index-{Utils.NewGuidString()}";
var indexName = Utils.TestVectorIndexName();

var createResponse = await vectorIndexClient.CreateIndexAsync(indexName, 2, SimilarityMetric.InnerProduct);
Assert.True(createResponse is CreateIndexResponse.Success, $"Unexpected response: {createResponse}");
Expand Down Expand Up @@ -410,7 +410,7 @@ public async Task UpsertAndSearch_WithDiverseMetadata<T>(SearchDelegate<T> searc
new List<float> { 3.0f, 20.0f, -0.01f }
}
};

// Combine the search threshold parameters and the search/search with vectors parameters
public static IEnumerable<object[]> UpsertAndSearchThresholdTestCases =>
SearchThresholdTestCases.SelectMany(
Expand All @@ -422,7 +422,7 @@ public async Task UpsertAndSearch_WithDiverseMetadata<T>(SearchDelegate<T> searc
public async Task Search_PruneBasedOnThreshold<T>(SimilarityMetric similarityMetric, List<float> scores,
List<float> thresholds, SearchDelegate<T> searchDelegate, AssertOnSearchResponse<T> assertOnSearchResponse)
{
var indexName = $"index-{Utils.NewGuidString()}";
var indexName = Utils.TestVectorIndexName();

var createResponse = await vectorIndexClient.CreateIndexAsync(indexName, 2, similarityMetric);
Assert.True(createResponse is CreateIndexResponse.Success, $"Unexpected response: {createResponse}");
Expand Down Expand Up @@ -472,4 +472,4 @@ public async Task Search_PruneBasedOnThreshold<T>(SimilarityMetric similarityMet
await vectorIndexClient.DeleteIndexAsync(indexName);
}
}
}
}

0 comments on commit 041d537

Please sign in to comment.