Skip to content

Commit

Permalink
.Net: Refactored integration tests for vector stores (#9905)
Browse files Browse the repository at this point in the history
### Motivation and Context

<!-- Thank you for your contribution to the semantic-kernel repo!
Please help reviewers and future users, providing the following
information:
  1. Why is this change required?
  2. What problem does it solve?
  3. What scenario does it contribute to?
  4. If it fixes an open issue, please link to the issue here.
-->

Resolves: #6459

This PR adds a base class for vector store integration tests and updates
all integration tests for concrete vector store to use common workflow.

### Contribution Checklist

<!-- Before submitting this PR, please make sure: -->

- [x] The code builds clean without any errors or warnings
- [x] The PR follows the [SK Contribution
Guidelines](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md)
and the [pre-submission formatting
script](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md#development-scripts)
raises no violations
- [x] All unit tests pass, and I have added new tests where possible
- [x] I didn't break anyone 😄
  • Loading branch information
dmytrostruk authored Dec 10, 2024
1 parent 88635e1 commit 43235b8
Show file tree
Hide file tree
Showing 25 changed files with 453 additions and 501 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright (c) Microsoft. All rights reserved.

using System;
using System.Text.Json.Serialization;
using Azure.Search.Documents.Indexes;
using Azure.Search.Documents.Indexes.Models;
using Microsoft.Extensions.VectorData;

namespace SemanticKernel.IntegrationTests.Connectors.Memory.AzureAISearch;

#pragma warning disable CS8618

public class AzureAISearchHotel
{
[SimpleField(IsKey = true, IsFilterable = true)]
[VectorStoreRecordKey]
public string HotelId { get; set; }

[SearchableField(IsFilterable = true, IsSortable = true)]
[VectorStoreRecordData(IsFilterable = true, IsFullTextSearchable = true)]
public string HotelName { get; set; }

[SearchableField(AnalyzerName = LexicalAnalyzerName.Values.EnLucene)]
[VectorStoreRecordData]
public string Description { get; set; }

[VectorStoreRecordVector(1536)]
public ReadOnlyMemory<float>? DescriptionEmbedding { get; set; }

[SearchableField(IsFilterable = true, IsFacetable = true)]
[VectorStoreRecordData(IsFilterable = true)]
#pragma warning disable CA1819 // Properties should not return arrays
public string[] Tags { get; set; }
#pragma warning restore CA1819 // Properties should not return arrays

[JsonPropertyName("parking_is_included")]
[SimpleField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
[VectorStoreRecordData(IsFilterable = true)]
public bool? ParkingIncluded { get; set; }

[SimpleField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
[VectorStoreRecordData(IsFilterable = true)]
public DateTimeOffset? LastRenovationDate { get; set; }

[SimpleField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
[VectorStoreRecordData]
public double? Rating { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
using SemanticKernel.IntegrationTests.Data;
using SemanticKernel.IntegrationTests.TestSettings;
using Xunit;
using static SemanticKernel.IntegrationTests.Connectors.Memory.AzureAISearch.AzureAISearchVectorStoreFixture;

namespace SemanticKernel.IntegrationTests.Connectors.Memory.AzureAISearch;

Expand Down Expand Up @@ -82,11 +81,11 @@ public override Task<ITextSearch> CreateTextSearchAsync()
this.VectorStore = new AzureAISearchVectorStore(fixture.SearchIndexClient);
}

var vectorSearch = this.VectorStore.GetCollection<string, Hotel>(fixture.TestIndexName);
var vectorSearch = this.VectorStore.GetCollection<string, AzureAISearchHotel>(fixture.TestIndexName);
var stringMapper = new HotelTextSearchStringMapper();
var resultMapper = new HotelTextSearchResultMapper();

var result = new VectorStoreTextSearch<Hotel>(vectorSearch, this.EmbeddingGenerator!, stringMapper, resultMapper);
var result = new VectorStoreTextSearch<AzureAISearchHotel>(vectorSearch, this.EmbeddingGenerator!, stringMapper, resultMapper);
return Task.FromResult<ITextSearch>(result);
}

Expand All @@ -105,7 +104,7 @@ public override bool VerifySearchResults(object[] results, string query, TextSea
foreach (var result in results)
{
Assert.NotNull(result);
Assert.IsType<Hotel>(result);
Assert.IsType<AzureAISearchHotel>(result);
}

return true;
Expand All @@ -119,7 +118,7 @@ protected sealed class HotelTextSearchStringMapper : ITextSearchStringMapper
/// <inheritdoc />
public string MapFromResultToString(object result)
{
if (result is Hotel hotel)
if (result is AzureAISearchHotel hotel)
{
return $"{hotel.HotelName} {hotel.Description}";
}
Expand All @@ -135,7 +134,7 @@ protected sealed class HotelTextSearchResultMapper : ITextSearchResultMapper
/// <inheritdoc />
public TextSearchResult MapFromResultToTextSearchResult(object result)
{
if (result is Hotel hotel)
if (result is AzureAISearchHotel hotel)
{
return new TextSearchResult(value: hotel.Description) { Name = hotel.HotelName, Link = $"id://{hotel.HotelId}" };
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Azure;
Expand Down Expand Up @@ -149,7 +148,7 @@ public static async Task CreateIndexAsync(string indexName, SearchIndexClient ad
// Build the list of fields from the model, and then replace the DescriptionEmbedding field with a vector field, to work around
// issue where the field is not recognized as an array on parsing on the server side when apply the VectorSearchFieldAttribute.
FieldBuilder fieldBuilder = new();
var searchFields = fieldBuilder.Build(typeof(Hotel));
var searchFields = fieldBuilder.Build(typeof(AzureAISearchHotel));
var embeddingfield = searchFields.First(x => x.Name == "DescriptionEmbedding");
searchFields.Remove(embeddingfield);
searchFields.Add(new VectorSearchField("DescriptionEmbedding", 1536, "my-vector-profile"));
Expand Down Expand Up @@ -185,9 +184,9 @@ public static async Task UploadDocumentsAsync(SearchClient searchClient, ITextEm
{
var embedding = await embeddingGenerator.GenerateEmbeddingAsync("This is a great hotel");

IndexDocumentsBatch<Hotel> batch = IndexDocumentsBatch.Create(
IndexDocumentsBatch<AzureAISearchHotel> batch = IndexDocumentsBatch.Create(
IndexDocumentsAction.Upload(
new Hotel()
new AzureAISearchHotel()
{
HotelId = "BaseSet-1",
HotelName = "Hotel 1",
Expand All @@ -199,7 +198,7 @@ public static async Task UploadDocumentsAsync(SearchClient searchClient, ITextEm
Rating = 3.6
}),
IndexDocumentsAction.Upload(
new Hotel()
new AzureAISearchHotel()
{
HotelId = "BaseSet-2",
HotelName = "Hotel 2",
Expand All @@ -211,7 +210,7 @@ public static async Task UploadDocumentsAsync(SearchClient searchClient, ITextEm
Rating = 3.60
}),
IndexDocumentsAction.Upload(
new Hotel()
new AzureAISearchHotel()
{
HotelId = "BaseSet-3",
HotelName = "Hotel 3",
Expand All @@ -223,7 +222,7 @@ public static async Task UploadDocumentsAsync(SearchClient searchClient, ITextEm
Rating = 4.80
}),
IndexDocumentsAction.Upload(
new Hotel()
new AzureAISearchHotel()
{
HotelId = "BaseSet-4",
HotelName = "Hotel 4",
Expand All @@ -241,43 +240,4 @@ public static async Task UploadDocumentsAsync(SearchClient searchClient, ITextEm
// Add some delay to allow time for the documents to get indexed and show up in search.
await Task.Delay(5000);
}

#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
public class Hotel
{
[SimpleField(IsKey = true, IsFilterable = true)]
[VectorStoreRecordKey]
public string HotelId { get; set; }

[SearchableField(IsFilterable = true, IsSortable = true)]
[VectorStoreRecordData(IsFilterable = true, IsFullTextSearchable = true)]
public string HotelName { get; set; }

[SearchableField(AnalyzerName = LexicalAnalyzerName.Values.EnLucene)]
[VectorStoreRecordData]
public string Description { get; set; }

[VectorStoreRecordVector(1536)]
public ReadOnlyMemory<float>? DescriptionEmbedding { get; set; }

[SearchableField(IsFilterable = true, IsFacetable = true)]
[VectorStoreRecordData(IsFilterable = true)]
#pragma warning disable CA1819 // Properties should not return arrays
public string[] Tags { get; set; }
#pragma warning restore CA1819 // Properties should not return arrays

[JsonPropertyName("parking_is_included")]
[SimpleField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
[VectorStoreRecordData(IsFilterable = true)]
public bool? ParkingIncluded { get; set; }

[SimpleField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
[VectorStoreRecordData(IsFilterable = true)]
public DateTimeOffset? LastRenovationDate { get; set; }

[SimpleField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
[VectorStoreRecordData]
public double? Rating { get; set; }
}
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
}
Loading

0 comments on commit 43235b8

Please sign in to comment.