diff --git a/dotnet/Directory.Packages.props b/dotnet/Directory.Packages.props
index adf5582061dd..75599a2d4751 100644
--- a/dotnet/Directory.Packages.props
+++ b/dotnet/Directory.Packages.props
@@ -27,7 +27,6 @@
-
@@ -105,9 +104,7 @@
-
-
@@ -171,10 +168,8 @@
-
-
@@ -186,20 +181,9 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/dotnet/MEVD.slnx b/dotnet/MEVD.slnx
index 9f782e02e062..17ad49e1937e 100644
--- a/dotnet/MEVD.slnx
+++ b/dotnet/MEVD.slnx
@@ -20,47 +20,11 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/dotnet/src/VectorData/AzureAISearch/AssemblyInfo.cs b/dotnet/src/VectorData/AzureAISearch/AssemblyInfo.cs
deleted file mode 100644
index cbb67c1c8afd..000000000000
--- a/dotnet/src/VectorData/AzureAISearch/AssemblyInfo.cs
+++ /dev/null
@@ -1 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
diff --git a/dotnet/src/VectorData/AzureAISearch/AzureAISearch.csproj b/dotnet/src/VectorData/AzureAISearch/AzureAISearch.csproj
deleted file mode 100644
index 54258372dc2a..000000000000
--- a/dotnet/src/VectorData/AzureAISearch/AzureAISearch.csproj
+++ /dev/null
@@ -1,41 +0,0 @@
-
-
-
- Microsoft.SemanticKernel.Connectors.AzureAISearch
- Microsoft.SemanticKernel.Connectors.AzureAISearch
- net10.0;net8.0;netstandard2.0;net462
- preview
-
-
-
-
-
-
-
-
- Microsoft.SemanticKernel.Connectors.AzureAISearch
- Azure AI Search provider for Microsoft.Extensions.VectorData
- Azure AI Search provider for Microsoft.Extensions.VectorData by Semantic Kernel
- VECTORDATA-CONNECTORS-NUGET.md
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/dotnet/src/VectorData/AzureAISearch/AzureAISearchCollection.cs b/dotnet/src/VectorData/AzureAISearch/AzureAISearchCollection.cs
deleted file mode 100644
index 16ed75d6c6ee..000000000000
--- a/dotnet/src/VectorData/AzureAISearch/AzureAISearchCollection.cs
+++ /dev/null
@@ -1,816 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Diagnostics.CodeAnalysis;
-using System.Linq;
-using System.Linq.Expressions;
-using System.Runtime.CompilerServices;
-using System.Text.Json;
-using System.Text.Json.Nodes;
-using System.Threading;
-using System.Threading.Tasks;
-using Azure;
-using Azure.Search.Documents;
-using Azure.Search.Documents.Indexes;
-using Azure.Search.Documents.Indexes.Models;
-using Azure.Search.Documents.Models;
-using Microsoft.Extensions.AI;
-using Microsoft.Extensions.VectorData;
-using Microsoft.Extensions.VectorData.ProviderServices;
-using MEAI = Microsoft.Extensions.AI;
-
-namespace Microsoft.SemanticKernel.Connectors.AzureAISearch;
-
-///
-/// Service for storing and retrieving vector records, that uses Azure AI Search as the underlying storage.
-///
-/// The data type of the record key. Must be .
-/// The data model to use for adding, updating and retrieving data from storage.
-#pragma warning disable CA1711 // Identifiers should not have incorrect suffix
-public class AzureAISearchCollection : VectorStoreCollection, IKeywordHybridSearchable
- where TKey : notnull
- where TRecord : class
-#pragma warning restore CA1711 // Identifiers should not have incorrect suffix
-{
- /// Metadata about vector store record collection.
- private readonly VectorStoreCollectionMetadata _collectionMetadata;
-
- /// The default options for vector search.
- private static readonly VectorSearchOptions s_defaultVectorSearchOptions = new();
-
- /// The default options for hybrid vector search.
- private static readonly HybridSearchOptions s_defaultKeywordVectorizedHybridSearchOptions = new();
-
- /// Azure AI Search client that can be used to manage the list of indices in an Azure AI Search Service.
- private readonly SearchIndexClient _searchIndexClient;
-
- /// Azure AI Search client that can be used to manage data in an Azure AI Search Service index.
- private readonly SearchClient _searchClient;
-
- /// A mapper to use for converting between the data model and the Azure AI Search record.
- private readonly IAzureAISearchMapper _mappper;
-
- /// The model for this collection.
- private readonly CollectionModel _model;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// Azure AI Search client that can be used to manage the list of indices in an Azure AI Search Service.
- /// The name of the collection that this will access.
- /// Optional configuration options for this class.
- /// Thrown when is null.
- /// Thrown when options are misconfigured.
- [RequiresUnreferencedCode("The Azure AI Search provider is currently incompatible with trimming.")]
- [RequiresDynamicCode("The Azure AI Search provider is currently incompatible with NativeAOT.")]
- public AzureAISearchCollection(SearchIndexClient searchIndexClient, string name, AzureAISearchCollectionOptions? options = default)
- : this(
- searchIndexClient,
- name,
- static options => typeof(TRecord) == typeof(Dictionary)
- ? throw new NotSupportedException(VectorDataStrings.NonDynamicCollectionWithDictionaryNotSupported(typeof(AzureAISearchDynamicCollection)))
- : new AzureAISearchModelBuilder()
- .Build(typeof(TRecord), typeof(TKey), options.Definition, options.EmbeddingGenerator, options.JsonSerializerOptions ?? JsonSerializerOptions.Default),
- options)
- {
- }
-
- internal AzureAISearchCollection(SearchIndexClient searchIndexClient, string name, Func modelFactory, AzureAISearchCollectionOptions? options)
- {
- // Verify.
- Verify.NotNull(searchIndexClient);
- Verify.NotNullOrWhiteSpace(name);
-
- if (typeof(TKey) != typeof(string) && typeof(TKey) != typeof(Guid) && typeof(TKey) != typeof(object))
- {
- throw new NotSupportedException("Only string and Guid keys are supported.");
- }
-
- options ??= AzureAISearchCollectionOptions.Default;
-
- // Assign.
- this.Name = name;
- this._model = modelFactory(options);
- this._searchIndexClient = searchIndexClient;
- this._searchClient = this._searchIndexClient.GetSearchClient(name);
-
- this._mappper = typeof(TRecord) == typeof(Dictionary) ?
- (IAzureAISearchMapper)(object)new AzureAISearchDynamicMapper(this._model, options.JsonSerializerOptions) :
- new AzureAISearchMapper(this._model, options.JsonSerializerOptions);
-
- this._collectionMetadata = new()
- {
- VectorStoreSystemName = AzureAISearchConstants.VectorStoreSystemName,
- VectorStoreName = searchIndexClient.ServiceName,
- CollectionName = name
- };
- }
-
- ///
- public override string Name { get; }
-
- ///
- public override async Task CollectionExistsAsync(CancellationToken cancellationToken = default)
- {
- try
- {
- await this._searchIndexClient.GetIndexAsync(this.Name, cancellationToken).ConfigureAwait(false);
- return true;
- }
- catch (RequestFailedException ex) when (ex.Status == 404)
- {
- return false;
- }
- catch (RequestFailedException ex)
- {
- throw new VectorStoreException("Call to vector store failed.", ex)
- {
- VectorStoreSystemName = AzureAISearchConstants.VectorStoreSystemName,
- VectorStoreName = this._collectionMetadata.VectorStoreName,
- CollectionName = this.Name,
- OperationName = "GetIndex"
- };
- }
- }
-
- ///
- public override async Task EnsureCollectionExistsAsync(CancellationToken cancellationToken = default)
- {
- const string OperationName = "CreateIndex";
-
- // Don't even try to create if the collection already exists.
- if (await this.CollectionExistsAsync(cancellationToken).ConfigureAwait(false))
- {
- return;
- }
-
- var vectorSearchConfig = new VectorSearch();
- var searchFields = new List();
-
- // Loop through all properties and create the search fields.
- foreach (var property in this._model.Properties)
- {
- switch (property)
- {
- case KeyPropertyModel p:
- searchFields.Add(AzureAISearchCollectionCreateMapping.MapKeyField(p));
- break;
-
- case DataPropertyModel p:
- searchFields.Add(AzureAISearchCollectionCreateMapping.MapDataField(p));
- break;
-
- case VectorPropertyModel p:
- (VectorSearchField vectorSearchField, VectorSearchAlgorithmConfiguration algorithmConfiguration, VectorSearchProfile vectorSearchProfile) = AzureAISearchCollectionCreateMapping.MapVectorField(p);
-
- // Add the search field, plus its profile and algorithm configuration to the search config.
- searchFields.Add(vectorSearchField);
- vectorSearchConfig.Algorithms.Add(algorithmConfiguration);
- vectorSearchConfig.Profiles.Add(vectorSearchProfile);
- break;
-
- default:
- throw new UnreachableException();
- }
- }
-
- // Create the index definition.
- var searchIndex = new SearchIndex(this.Name, searchFields);
- searchIndex.VectorSearch = vectorSearchConfig;
-
- try
- {
- await this._searchIndexClient.CreateIndexAsync(searchIndex, cancellationToken).ConfigureAwait(false);
- }
- catch (RequestFailedException ex) when (ex.ErrorCode == "ResourceNameAlreadyInUse")
- {
- // Index already exists, ignore.
- }
- catch (RequestFailedException ex)
- {
- throw new VectorStoreException("Call to vector store failed.", ex)
- {
- VectorStoreSystemName = AzureAISearchConstants.VectorStoreSystemName,
- VectorStoreName = this._collectionMetadata.VectorStoreName,
- CollectionName = this.Name,
- OperationName = OperationName
- };
- }
- catch (AggregateException ex) when (ex.InnerException is RequestFailedException innerEx)
- {
- throw new VectorStoreException("Call to vector store failed.", ex)
- {
- VectorStoreSystemName = AzureAISearchConstants.VectorStoreSystemName,
- VectorStoreName = this._collectionMetadata.VectorStoreName,
- CollectionName = this.Name,
- OperationName = OperationName
- };
- }
- }
-
- ///
- public override Task EnsureCollectionDeletedAsync(CancellationToken cancellationToken = default)
- {
- return this.RunOperationAsync(
- "DeleteIndex",
- async () =>
- {
- try
- {
- return await this._searchIndexClient.DeleteIndexAsync(this.Name, cancellationToken).ConfigureAwait(false);
- }
- catch (RequestFailedException ex) when (ex.Status == 404)
- {
- return null!;
- }
- });
- }
-
- ///
- public override Task GetAsync(TKey key, RecordRetrievalOptions? options = default, CancellationToken cancellationToken = default)
- {
- // Create Options.
- var innerOptions = this.ConvertGetDocumentOptions(options);
- var includeVectors = options?.IncludeVectors ?? false;
- if (includeVectors && this._model.EmbeddingGenerationRequired)
- {
- throw new NotSupportedException(VectorDataStrings.IncludeVectorsNotSupportedWithEmbeddingGeneration);
- }
-
- // Get record.
- return this.GetDocumentAndMapToDataModelAsync(key, includeVectors, innerOptions, cancellationToken);
- }
-
- ///
- public override async IAsyncEnumerable GetAsync(IEnumerable keys, RecordRetrievalOptions? options = default, [EnumeratorCancellation] CancellationToken cancellationToken = default)
- {
- Verify.NotNull(keys);
-
- // Create Options
- var innerOptions = this.ConvertGetDocumentOptions(options);
- var includeVectors = options?.IncludeVectors ?? false;
- if (includeVectors && this._model.EmbeddingGenerationRequired)
- {
- throw new NotSupportedException(VectorDataStrings.IncludeVectorsNotSupportedWithEmbeddingGeneration);
- }
-
- foreach (var key in keys)
- {
- var record = await this.GetDocumentAndMapToDataModelAsync(key, includeVectors, innerOptions, cancellationToken).ConfigureAwait(false);
-
- if (record is not null)
- {
- yield return record;
- }
- }
- }
-
- ///
- public override Task DeleteAsync(TKey key, CancellationToken cancellationToken = default)
- {
- var stringKey = GetStringKey(key);
-
- // Remove record.
- return this.RunOperationAsync(
- "DeleteDocuments",
- () => this._searchClient.DeleteDocumentsAsync(this._model.KeyProperty.StorageName, [stringKey], new IndexDocumentsOptions(), cancellationToken));
- }
-
- ///
- public override Task DeleteAsync(IEnumerable keys, CancellationToken cancellationToken = default)
- {
- Verify.NotNull(keys);
- if (!keys.Any())
- {
- return Task.CompletedTask;
- }
-
- var stringKeys = keys is IEnumerable k ? k : keys.Select(GetStringKey);
-
- // Remove records.
- return this.RunOperationAsync(
- "DeleteDocuments",
- () => this._searchClient.DeleteDocumentsAsync(this._model.KeyProperty.StorageName, stringKeys, new IndexDocumentsOptions(), cancellationToken));
- }
-
- ///
- public override async Task UpsertAsync(TRecord record, CancellationToken cancellationToken = default)
- {
- Verify.NotNull(record);
-
- // Create options.
- var innerOptions = new IndexDocumentsOptions { ThrowOnAnyError = true };
-
- // Upsert record.
- await this.MapToStorageModelAndUploadDocumentAsync([record], innerOptions, cancellationToken).ConfigureAwait(false);
- }
-
- ///
- public override async Task UpsertAsync(IEnumerable records, CancellationToken cancellationToken = default)
- {
- Verify.NotNull(records);
- if (!records.Any())
- {
- return;
- }
-
- // Create Options
- var innerOptions = new IndexDocumentsOptions { ThrowOnAnyError = true };
-
- // Upsert records
- await this.MapToStorageModelAndUploadDocumentAsync(records, innerOptions, cancellationToken).ConfigureAwait(false);
- }
-
- ///
- public override IAsyncEnumerable GetAsync(Expression> filter, int top,
- FilteredRecordRetrievalOptions? options = null, CancellationToken cancellationToken = default)
- {
- Verify.NotNull(filter);
- Verify.NotLessThan(top, 1);
-
- options ??= new();
-
- var includeVectors = options.IncludeVectors;
- if (includeVectors && this._model.EmbeddingGenerationRequired)
- {
- throw new NotSupportedException(VectorDataStrings.IncludeVectorsNotSupportedWithEmbeddingGeneration);
- }
-
- SearchOptions searchOptions = new()
- {
- VectorSearch = new(),
- Size = top,
- Skip = options.Skip,
- Filter = new AzureAISearchFilterTranslator().Translate(filter, this._model)
- };
-
- // Filter out vector fields if requested.
- if (!options.IncludeVectors)
- {
- searchOptions.Select.Add(this._model.KeyProperty.StorageName);
-
- foreach (var dataProperty in this._model.DataProperties)
- {
- searchOptions.Select.Add(dataProperty.StorageName);
- }
- }
-
- if (options.OrderBy is not null)
- {
- foreach (var pair in options.OrderBy(new()).Values)
- {
- PropertyModel property = this._model.GetDataOrKeyProperty(pair.PropertySelector);
- string name = property.StorageName;
- // From https://learn.microsoft.com/dotnet/api/azure.search.documents.searchoptions.orderby:
- // "Each expression can be followed by asc to indicate ascending, or desc to indicate descending".
- // "The default is ascending order."
- if (!pair.Ascending)
- {
- name += " desc";
- }
-
- searchOptions.OrderBy.Add(name);
- }
- }
-
- return this.SearchAndMapToDataModelAsync(null, searchOptions, options.IncludeVectors, cancellationToken)
- .Select(result => result.Record);
- }
-
- #region Search
-
- ///
- public override async IAsyncEnumerable> SearchAsync(
- TInput searchValue,
- int top,
- VectorSearchOptions? options = null,
- [EnumeratorCancellation] CancellationToken cancellationToken = default)
- {
- Verify.NotNull(searchValue);
- Verify.NotLessThan(top, 1);
-
- options ??= s_defaultVectorSearchOptions;
- var vectorProperty = this._model.GetVectorPropertyOrSingle(options);
- var floatVector = await GetSearchVectorAsync(searchValue, vectorProperty, cancellationToken).ConfigureAwait(false);
-
- var searchOptions = BuildSearchOptions(
- this._model,
- options,
- top,
- floatVector is null
- ? new VectorizableTextQuery((string)(object)searchValue) { KNearestNeighborsCount = top + options.Skip, Fields = { vectorProperty.StorageName } }
- : new VectorizedQuery(floatVector.Value) { KNearestNeighborsCount = top + options.Skip, Fields = { vectorProperty.StorageName } });
-
- await foreach (var record in this.SearchAndMapToDataModelAsync(null, searchOptions, options.IncludeVectors, cancellationToken).ConfigureAwait(false))
- {
- // Azure AI Search threshold filtering is in preview:
- // https://learn.microsoft.com/azure/search/vector-search-how-to-query#set-thresholds-to-exclude-low-scoring-results-preview
- // See https://github.com/microsoft/semantic-kernel/issues/13500.
- // For now, perform post-filtering on the client-side.
- if (options.ScoreThreshold.HasValue && record.Score < options.ScoreThreshold.Value)
- {
- continue;
- }
-
- yield return record;
- }
- }
-
- ///
- public async IAsyncEnumerable> HybridSearchAsync(
- TInput searchValue,
- ICollection keywords,
- int top,
- HybridSearchOptions? options = null,
- [EnumeratorCancellation] CancellationToken cancellationToken = default)
- where TInput : notnull
- {
- Verify.NotNull(keywords);
- Verify.NotLessThan(top, 1);
-
- // Resolve options.
- options ??= s_defaultKeywordVectorizedHybridSearchOptions;
- var vectorProperty = this._model.GetVectorPropertyOrSingle(new() { VectorProperty = options.VectorProperty });
- var floatVector = await GetSearchVectorAsync(searchValue, vectorProperty, cancellationToken).ConfigureAwait(false);
-
- var textDataProperty = this._model.GetFullTextDataPropertyOrSingle(options.AdditionalProperty);
-
- // Build search options.
- var searchOptions = BuildSearchOptions(
- this._model,
- new()
- {
- Filter = options.Filter,
- VectorProperty = options.VectorProperty,
- Skip = options.Skip,
- },
- top,
- floatVector is null
- ? new VectorizableTextQuery((string)(object)searchValue) { KNearestNeighborsCount = top + options.Skip, Fields = { vectorProperty.StorageName } }
- : new VectorizedQuery(floatVector.Value) { KNearestNeighborsCount = top + options.Skip, Fields = { vectorProperty.StorageName } });
-
- searchOptions.SearchFields.Add(textDataProperty.StorageName);
- var keywordsCombined = string.Join(" ", keywords);
-
- await foreach (var record in this.SearchAndMapToDataModelAsync(keywordsCombined, searchOptions, options.IncludeVectors, cancellationToken).ConfigureAwait(false))
- {
- // Azure AI Search returns scores where higher values indicate more relevant results.
- if (options.ScoreThreshold.HasValue && record.Score < options.ScoreThreshold.Value)
- {
- continue;
- }
-
- yield return record;
- }
- }
-
- private static async ValueTask?> GetSearchVectorAsync(TInput searchValue, VectorPropertyModel vectorProperty, CancellationToken cancellationToken)
- where TInput : notnull
- => searchValue switch
- {
- ReadOnlyMemory r => r,
- float[] f => new ReadOnlyMemory(f),
- Embedding e => e.Vector,
- _ when vectorProperty.EmbeddingGenerationDispatcher is not null
- => ((Embedding)await vectorProperty.GenerateEmbeddingAsync(searchValue, cancellationToken).ConfigureAwait(false)).Vector,
-
- // A string was passed without an embedding generator being configured; send the string to Azure AI Search for backend embedding generation.
- string when vectorProperty.EmbeddingGenerator is null => (ReadOnlyMemory?)null,
-
- _ => vectorProperty.EmbeddingGenerator is null
- ? throw new NotSupportedException(VectorDataStrings.InvalidSearchInputAndNoEmbeddingGeneratorWasConfigured(searchValue.GetType(), AzureAISearchModelBuilder.SupportedVectorTypes))
- : throw new InvalidOperationException(VectorDataStrings.IncompatibleEmbeddingGeneratorWasConfiguredForInputType(typeof(TInput), vectorProperty.EmbeddingGenerator.GetType()))
- };
-
- #endregion Search
-
- ///
- public override object? GetService(Type serviceType, object? serviceKey = null)
- {
- Verify.NotNull(serviceType);
-
- return
- serviceKey is not null ? null :
- serviceType == typeof(VectorStoreCollectionMetadata) ? this._collectionMetadata :
- serviceType == typeof(SearchIndexClient) ? this._searchIndexClient :
- serviceType == typeof(SearchClient) ? this._searchClient :
- serviceType.IsInstanceOfType(this) ? this :
- null;
- }
-
- ///
- /// Get the document with the given key and map it to the data model using the configured mapper type.
- ///
- /// The key of the record to get.
- /// A value indicating whether to include vectors in the result or not.
- /// The Azure AI Search sdk options for getting a document.
- /// The to monitor for cancellation requests. The default is .
- /// The retrieved document, mapped to the consumer data model.
- private async Task GetDocumentAndMapToDataModelAsync(
- TKey key,
- bool includeVectors,
- GetDocumentOptions innerOptions,
- CancellationToken cancellationToken)
- {
- const string OperationName = "GetDocument";
-
- var stringKey = GetStringKey(key);
-
- var jsonObject = await this.RunOperationAsync(
- OperationName,
- () => this.GetDocumentWithNotFoundHandlingAsync(this._searchClient, stringKey, innerOptions, cancellationToken)).ConfigureAwait(false);
-
- if (jsonObject is null)
- {
- return default;
- }
-
- return (TRecord)(object)this._mappper!.MapFromStorageToDataModel(jsonObject, includeVectors);
- }
-
- ///
- /// Search for the documents matching the given options and map them to the data model using the configured mapper type.
- ///
- /// Text to use if doing a hybrid search. Null for non-hybrid search.
- /// The options controlling the behavior of the search operation.
- /// A value indicating whether to include vectors in the result or not.
- /// The to monitor for cancellation requests. The default is .
- /// The mapped search results.
- private async IAsyncEnumerable> SearchAndMapToDataModelAsync(
- string? searchText,
- SearchOptions searchOptions,
- bool includeVectors,
- [EnumeratorCancellation] CancellationToken cancellationToken)
- {
- const string OperationName = "Search";
-
- var jsonObjectResults = await this.RunOperationAsync(
- OperationName,
- () => this._searchClient.SearchAsync(searchText, searchOptions, cancellationToken)).ConfigureAwait(false);
-
- await foreach (var result in this.MapSearchResultsAsync(jsonObjectResults.Value.GetResultsAsync(), OperationName, includeVectors).ConfigureAwait(false))
- {
- yield return result;
- }
- }
-
- ///
- /// Map the data model to the storage model and upload the document.
- ///
- /// The records to upload.
- /// The Azure AI Search sdk options for uploading a document.
- /// The to monitor for cancellation requests. The default is .
- /// The document upload result.
- private async Task> MapToStorageModelAndUploadDocumentAsync(
- IEnumerable records,
- IndexDocumentsOptions innerOptions,
- CancellationToken cancellationToken)
- {
- const string OperationName = "UploadDocuments";
-
- (records, var generatedEmbeddings) = await ProcessEmbeddingsAsync(this._model, records, cancellationToken).ConfigureAwait(false);
-
- // Handle auto-generated keys (client-side for Azure AI Search, which doesn't support server-side auto-generation)
- var keyProperty = this._model.KeyProperty;
- var jsonObjects = new List();
- var recordIndex = 0;
- foreach (var record in records)
- {
- if (keyProperty.IsAutoGenerated && keyProperty.GetValue(record) == Guid.Empty)
- {
- keyProperty.SetValue(record, Guid.NewGuid());
- }
-
- jsonObjects.Add(this._mappper!.MapFromDataToStorageModel(record, recordIndex++, generatedEmbeddings));
- }
-
- return await this.RunOperationAsync(
- OperationName,
- () => this._searchClient.UploadDocumentsAsync(jsonObjects, innerOptions, cancellationToken)).ConfigureAwait(false);
- }
-
- ///
- /// Map the search results from to objects using the configured mapper type.
- ///
- /// The search results to map.
- /// The name of the current operation for telemetry purposes.
- /// A value indicating whether to include vectors in the resultset or not.
- /// The mapped results.
- private async IAsyncEnumerable> MapSearchResultsAsync(IAsyncEnumerable> results, string operationName, bool includeVectors)
- {
- await foreach (var result in results.ConfigureAwait(false))
- {
- var document = (TRecord)(object)this._mappper!.MapFromStorageToDataModel(result.Document, includeVectors);
- yield return new VectorSearchResult(document, result.Score);
- }
- }
-
- ///
- /// Map the search results from to objects.
- ///
- /// The search results to map.
- /// The mapped results.
- private async IAsyncEnumerable> MapSearchResultsAsync(IAsyncEnumerable> results)
- {
- await foreach (var result in results.ConfigureAwait(false))
- {
- yield return new VectorSearchResult(result.Document, result.Score);
- }
- }
-
- ///
- /// Convert the public options model to the Azure AI Search options model.
- ///
- /// The public options model.
- /// The Azure AI Search options model.
- private GetDocumentOptions ConvertGetDocumentOptions(RecordRetrievalOptions? options)
- {
- var innerOptions = new GetDocumentOptions();
- if (options?.IncludeVectors is not true)
- {
- innerOptions.SelectedFields.Add(this._model.KeyProperty.StorageName);
-
- foreach (var dataProperty in this._model.DataProperties)
- {
- innerOptions.SelectedFields.Add(dataProperty.StorageName);
- }
- }
-
- return innerOptions;
- }
-
- ///
- /// Build the search options for a vector search, where the type of vector search can be provided as input.
- /// E.g. VectorizedQuery or VectorizableTextQuery.
- ///
- private static SearchOptions BuildSearchOptions(CollectionModel model, VectorSearchOptions options, int top, VectorQuery? vectorQuery)
- {
- if (model.VectorProperties.Count == 0)
- {
- throw new InvalidOperationException("The collection does not have any vector fields, so vector search is not possible.");
- }
-
- if (options.IncludeVectors && model.EmbeddingGenerationRequired)
- {
- throw new NotSupportedException(VectorDataStrings.IncludeVectorsNotSupportedWithEmbeddingGeneration);
- }
-
- // Build filter object.
- var filter = options.Filter is not null
- ? new AzureAISearchFilterTranslator().Translate(options.Filter, model)
- : null;
-
- // Build search options.
- var searchOptions = new SearchOptions
- {
- VectorSearch = new(),
- Size = top,
- Skip = options.Skip,
- };
-
- if (filter is not null)
- {
- searchOptions.Filter = filter;
- }
-
- searchOptions.VectorSearch.Queries.Add(vectorQuery);
-
- // Filter out vector fields if requested.
- if (!options.IncludeVectors)
- {
- searchOptions.Select.Add(model.KeyProperty.StorageName);
-
- foreach (var dataProperty in model.DataProperties)
- {
- searchOptions.Select.Add(dataProperty.StorageName);
- }
- }
-
- return searchOptions;
- }
-
- private static async ValueTask<(IEnumerable records, IReadOnlyList?[]?)> ProcessEmbeddingsAsync(
- CollectionModel model,
- IEnumerable records,
- CancellationToken cancellationToken)
- {
- IReadOnlyList? recordsList = null;
-
- // If an embedding generator is defined, invoke it once per property for all records.
- IReadOnlyList?[]? generatedEmbeddings = null;
-
- var vectorPropertyCount = model.VectorProperties.Count;
- for (var i = 0; i < vectorPropertyCount; i++)
- {
- var vectorProperty = model.VectorProperties[i];
-
- if (AzureAISearchModelBuilder.IsVectorPropertyTypeValidCore(vectorProperty.Type, out _))
- {
- continue;
- }
-
- // We have a vector property whose type isn't natively supported - we need to generate embeddings.
- Debug.Assert(vectorProperty.EmbeddingGenerator is not null);
-
- // We have a property with embedding generation; materialize the records' enumerable if needed, to
- // prevent multiple enumeration.
- if (recordsList is null)
- {
- recordsList = records is IReadOnlyList r ? r : records.ToList();
-
- if (recordsList.Count == 0)
- {
- return (records, null);
- }
-
- records = recordsList;
- }
-
- // TODO: Ideally we'd group together vector properties using the same generator (and with the same input and output properties),
- // and generate embeddings for them in a single batch. That's some more complexity though.
- generatedEmbeddings ??= new IReadOnlyList?[vectorPropertyCount];
- generatedEmbeddings[i] = await vectorProperty.GenerateEmbeddingsAsync(records.Select(r => vectorProperty.GetValueAsObject(r)), cancellationToken).ConfigureAwait(false);
- }
-
- return (records, generatedEmbeddings);
- }
-
- ///
- /// Get a document with the given key, and return null if it is not found.
- ///
- /// The type to deserialize the document to.
- /// The search client to use when fetching the document.
- /// The key of the record to get.
- /// The Azure AI Search sdk options for getting a document.
- /// The to monitor for cancellation requests. The default is .
- /// The retrieved document, mapped to the consumer data model, or null if not found.
- private async Task GetDocumentWithNotFoundHandlingAsync(
- SearchClient searchClient,
- string key,
- GetDocumentOptions innerOptions,
- CancellationToken cancellationToken)
- {
- const string OperationName = "GetDocument";
-
- try
- {
- return await searchClient.GetDocumentAsync(key, innerOptions, cancellationToken).ConfigureAwait(false);
- }
- catch (RequestFailedException ex) when (ex.Status == 404)
- {
- return default;
- }
- catch (AggregateException ex) when (ex.InnerException is RequestFailedException innerEx)
- {
- throw new VectorStoreException("Call to vector store failed.", ex)
- {
- VectorStoreSystemName = AzureAISearchConstants.VectorStoreSystemName,
- VectorStoreName = this._collectionMetadata.VectorStoreName,
- CollectionName = this.Name,
- OperationName = OperationName
- };
- }
- catch (RequestFailedException ex)
- {
- throw new VectorStoreException("Call to vector store failed.", ex)
- {
- VectorStoreSystemName = AzureAISearchConstants.VectorStoreSystemName,
- VectorStoreName = this._collectionMetadata.VectorStoreName,
- CollectionName = this.Name,
- OperationName = OperationName
- };
- }
- }
-
- ///
- /// Run the given operation and wrap any with ."/>
- ///
- /// The response type of the operation.
- /// The type of database operation being run.
- /// The operation to run.
- /// The result of the operation.
- private Task RunOperationAsync(string operationName, Func> operation) =>
- VectorStoreErrorHandler.RunOperationAsync(
- this._collectionMetadata,
- operationName,
- operation);
-
- private static string GetStringKey(TKey key)
- {
- Verify.NotNull(key);
-
- var stringKey = key switch
- {
- string s => s,
- Guid g => g.ToString(),
-
- _ => throw new UnreachableException("string key should have been validated during model building")
- };
-
- Verify.NotNullOrWhiteSpace(stringKey, nameof(key));
-
- return stringKey;
- }
-}
diff --git a/dotnet/src/VectorData/AzureAISearch/AzureAISearchCollectionCreateMapping.cs b/dotnet/src/VectorData/AzureAISearch/AzureAISearchCollectionCreateMapping.cs
deleted file mode 100644
index c21053e15dda..000000000000
--- a/dotnet/src/VectorData/AzureAISearch/AzureAISearchCollectionCreateMapping.cs
+++ /dev/null
@@ -1,160 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-using System;
-using System.Collections.Generic;
-using Azure.Search.Documents.Indexes.Models;
-using Microsoft.Extensions.VectorData;
-using Microsoft.Extensions.VectorData.ProviderServices;
-
-namespace Microsoft.SemanticKernel.Connectors.AzureAISearch;
-
-///
-/// Contains mapping helpers to use when creating a Azure AI Search vector collection.
-///
-internal static class AzureAISearchCollectionCreateMapping
-{
- ///
- /// Map from a to an Azure AI Search .
- ///
- /// The key property definition.
- /// The for the provided property definition.
- public static SearchableField MapKeyField(KeyPropertyModel keyProperty)
- {
- return new SearchableField(keyProperty.StorageName) { IsKey = true, IsFilterable = true };
- }
-
- ///
- /// Map from a to an Azure AI Search .
- ///
- /// The data property definition.
- /// The for the provided property definition.
- /// Throws when the definition is missing required information.
- public static SimpleField MapDataField(DataPropertyModel dataProperty)
- {
- if (dataProperty.IsFullTextIndexed)
- {
- if (dataProperty.Type != typeof(string))
- {
- throw new InvalidOperationException($"Property {nameof(dataProperty.IsFullTextIndexed)} on {nameof(VectorStoreDataProperty)} '{dataProperty.ModelName}' is set to true, but the property type is not a string. The Azure AI Search VectorStore supports {nameof(dataProperty.IsFullTextIndexed)} on string properties only.");
- }
-
- return new SearchableField(dataProperty.StorageName)
- {
- IsFilterable = dataProperty.IsIndexed,
- // Sometimes the users ask to also OrderBy given filterable property, so we make it sortable.
- IsSortable = dataProperty.IsIndexed
- };
- }
-
- var fieldType = AzureAISearchCollectionCreateMapping.GetSDKFieldDataType(dataProperty.Type);
- return new SimpleField(dataProperty.StorageName, fieldType)
- {
- IsFilterable = dataProperty.IsIndexed,
- // Sometimes the users ask to also OrderBy given filterable property, so we make it sortable.
- IsSortable = dataProperty.IsIndexed && !fieldType.IsCollection
- };
- }
-
- ///
- /// Map form a to an Azure AI Search and generate the required index configuration.
- ///
- /// The vector property definition.
- /// The and required index configuration.
- /// Throws when the definition is missing required information, or unsupported options are configured.
- public static (VectorSearchField vectorSearchField, VectorSearchAlgorithmConfiguration algorithmConfiguration, VectorSearchProfile vectorSearchProfile) MapVectorField(VectorPropertyModel vectorProperty)
- {
- // Build a name for the profile and algorithm configuration based on the property name
- // since we'll just create a separate one for each vector property.
- var vectorSearchProfileName = $"{vectorProperty.StorageName}Profile";
- var algorithmConfigName = $"{vectorProperty.StorageName}AlgoConfig";
-
- // Read the vector index settings from the property definition and create the right index configuration.
- var indexKind = AzureAISearchCollectionCreateMapping.GetSKIndexKind(vectorProperty);
- var algorithmMetric = AzureAISearchCollectionCreateMapping.GetSDKDistanceAlgorithm(vectorProperty);
-
- VectorSearchAlgorithmConfiguration algorithmConfiguration = indexKind switch
- {
- IndexKind.Hnsw => new HnswAlgorithmConfiguration(algorithmConfigName) { Parameters = new HnswParameters { Metric = algorithmMetric } },
- IndexKind.Flat => new ExhaustiveKnnAlgorithmConfiguration(algorithmConfigName) { Parameters = new ExhaustiveKnnParameters { Metric = algorithmMetric } },
-
- _ => throw new NotSupportedException($"Index kind '{indexKind}' on {nameof(VectorStoreVectorProperty)} '{vectorProperty.ModelName}' is not supported by the Azure AI Search VectorStore.")
- };
-
- var vectorSearchProfile = new VectorSearchProfile(vectorSearchProfileName, algorithmConfigName);
-
- return (new VectorSearchField(vectorProperty.StorageName, vectorProperty.Dimensions, vectorSearchProfileName), algorithmConfiguration, vectorSearchProfile);
- }
-
- ///
- /// Get the configured from the given .
- /// If none is configured the default is .
- ///
- /// The vector property definition.
- /// The configured or default .
- public static string GetSKIndexKind(VectorPropertyModel vectorProperty)
- => vectorProperty.IndexKind ?? IndexKind.Hnsw;
-
- ///
- /// Get the configured from the given .
- /// If none is configured, the default is .
- ///
- /// The vector property definition.
- /// The chosen .
- /// Thrown if a distance function is chosen that isn't supported by Azure AI Search.
- public static VectorSearchAlgorithmMetric GetSDKDistanceAlgorithm(VectorPropertyModel vectorProperty)
- => vectorProperty.DistanceFunction switch
- {
- DistanceFunction.CosineSimilarity or null => VectorSearchAlgorithmMetric.Cosine,
- DistanceFunction.DotProductSimilarity => VectorSearchAlgorithmMetric.DotProduct,
- DistanceFunction.EuclideanDistance => VectorSearchAlgorithmMetric.Euclidean,
-
- _ => throw new NotSupportedException($"Distance function '{vectorProperty.DistanceFunction}' for {nameof(VectorStoreVectorProperty)} '{vectorProperty.ModelName}' is not supported by the Azure AI Search VectorStore.")
- };
-
- ///
- /// Maps the given property type to the corresponding .
- ///
- /// The property type to map.
- /// The that corresponds to the given property type."
- /// Thrown if the given type is not supported.
- public static SearchFieldDataType GetSDKFieldDataType(Type propertyType)
- => (Nullable.GetUnderlyingType(propertyType) ?? propertyType) switch
- {
- Type t when t == typeof(string) => SearchFieldDataType.String,
- Type t when t == typeof(bool) => SearchFieldDataType.Boolean,
- Type t when t == typeof(int) => SearchFieldDataType.Int32,
- Type t when t == typeof(long) => SearchFieldDataType.Int64,
- // We don't map float to SearchFieldDataType.Single, because Azure AI Search doesn't support it.
- // Half is also listed by the SDK, but currently not supported.
- Type t when t == typeof(float) => SearchFieldDataType.Double,
- Type t when t == typeof(double) => SearchFieldDataType.Double,
- Type t when t == typeof(DateTime) => SearchFieldDataType.DateTimeOffset,
- Type t when t == typeof(DateTimeOffset) => SearchFieldDataType.DateTimeOffset,
-#if NET
- Type t when t == typeof(DateOnly) => SearchFieldDataType.DateTimeOffset,
-#endif
-
- Type t when t == typeof(string[]) => SearchFieldDataType.Collection(SearchFieldDataType.String),
- Type t when t == typeof(List) => SearchFieldDataType.Collection(SearchFieldDataType.String),
- Type t when t == typeof(bool[]) => SearchFieldDataType.Collection(SearchFieldDataType.Boolean),
- Type t when t == typeof(List) => SearchFieldDataType.Collection(SearchFieldDataType.Boolean),
- Type t when t == typeof(int[]) => SearchFieldDataType.Collection(SearchFieldDataType.Int32),
- Type t when t == typeof(List) => SearchFieldDataType.Collection(SearchFieldDataType.Int32),
- Type t when t == typeof(long[]) => SearchFieldDataType.Collection(SearchFieldDataType.Int64),
- Type t when t == typeof(List) => SearchFieldDataType.Collection(SearchFieldDataType.Int64),
- Type t when t == typeof(float[]) => SearchFieldDataType.Collection(SearchFieldDataType.Double),
- Type t when t == typeof(List) => SearchFieldDataType.Collection(SearchFieldDataType.Double),
- Type t when t == typeof(double[]) => SearchFieldDataType.Collection(SearchFieldDataType.Double),
- Type t when t == typeof(List) => SearchFieldDataType.Collection(SearchFieldDataType.Double),
- Type t when t == typeof(DateTime[]) => SearchFieldDataType.Collection(SearchFieldDataType.DateTimeOffset),
- Type t when t == typeof(List) => SearchFieldDataType.Collection(SearchFieldDataType.DateTimeOffset),
- Type t when t == typeof(DateTimeOffset[]) => SearchFieldDataType.Collection(SearchFieldDataType.DateTimeOffset),
- Type t when t == typeof(List) => SearchFieldDataType.Collection(SearchFieldDataType.DateTimeOffset),
-#if NET
- Type t when t == typeof(DateOnly[]) => SearchFieldDataType.Collection(SearchFieldDataType.DateTimeOffset),
- Type t when t == typeof(List) => SearchFieldDataType.Collection(SearchFieldDataType.DateTimeOffset),
-#endif
-
- _ => throw new NotSupportedException($"Data type '{propertyType}' for {nameof(VectorStoreDataProperty)} is not supported by the Azure AI Search VectorStore.")
- };
-}
diff --git a/dotnet/src/VectorData/AzureAISearch/AzureAISearchCollectionOptions.cs b/dotnet/src/VectorData/AzureAISearch/AzureAISearchCollectionOptions.cs
deleted file mode 100644
index 4197fbad7ddb..000000000000
--- a/dotnet/src/VectorData/AzureAISearch/AzureAISearchCollectionOptions.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-using System.Text.Json;
-using Azure.Search.Documents.Indexes;
-using Microsoft.Extensions.VectorData;
-
-namespace Microsoft.SemanticKernel.Connectors.AzureAISearch;
-
-///
-/// Options when creating a .
-///
-public sealed class AzureAISearchCollectionOptions : VectorStoreCollectionOptions
-{
- internal static readonly AzureAISearchCollectionOptions Default = new();
-
- ///
- /// Initializes a new instance of the class.
- ///
- public AzureAISearchCollectionOptions()
- {
- }
-
- internal AzureAISearchCollectionOptions(AzureAISearchCollectionOptions? source) : base(source)
- {
- this.JsonSerializerOptions = source?.JsonSerializerOptions;
- }
-
- ///
- /// Gets or sets the JSON serializer options to use when converting between the data model and the Azure AI Search record.
- /// Note that when using the default mapper and you are constructing your own , you will need
- /// to provide the same set of both here and when constructing the .
- ///
- public JsonSerializerOptions? JsonSerializerOptions { get; set; }
-}
diff --git a/dotnet/src/VectorData/AzureAISearch/AzureAISearchConstants.cs b/dotnet/src/VectorData/AzureAISearch/AzureAISearchConstants.cs
deleted file mode 100644
index fccfa847a0a3..000000000000
--- a/dotnet/src/VectorData/AzureAISearch/AzureAISearchConstants.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-namespace Microsoft.SemanticKernel.Connectors.AzureAISearch;
-
-internal static class AzureAISearchConstants
-{
- internal const string VectorStoreSystemName = "azure.aisearch";
-}
diff --git a/dotnet/src/VectorData/AzureAISearch/AzureAISearchDynamicCollection.cs b/dotnet/src/VectorData/AzureAISearch/AzureAISearchDynamicCollection.cs
deleted file mode 100644
index 934bf9bc34d0..000000000000
--- a/dotnet/src/VectorData/AzureAISearch/AzureAISearchDynamicCollection.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-using System;
-using System.Collections.Generic;
-using System.Diagnostics.CodeAnalysis;
-using Azure.Search.Documents.Indexes;
-
-namespace Microsoft.SemanticKernel.Connectors.AzureAISearch;
-
-///
-/// Represents a collection of vector store records in a AzureAISearch database, mapped to a dynamic Dictionary<string, object?>.
-///
-#pragma warning disable CA1711 // Identifiers should not have incorrect suffix
-public sealed class AzureAISearchDynamicCollection : AzureAISearchCollection