Skip to content

.Net MEVD Replace VectorSearchResults with IAsyncEnumerable #11486

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Apr 12, 2025
4 changes: 2 additions & 2 deletions dotnet/samples/Concepts/Caching/SemanticCachingWithFilters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -199,8 +199,8 @@ public async Task OnPromptRenderAsync(PromptRenderContext context, Func<PromptRe
await collection.CreateCollectionIfNotExistsAsync();

// Search for similar prompts in cache.
var searchResults = await collection.VectorizedSearchAsync(promptEmbedding, top: 1, cancellationToken: context.CancellationToken);
var searchResult = (await searchResults.Results.FirstOrDefaultAsync())?.Record;
var searchResults = collection.VectorizedSearchAsync(promptEmbedding, top: 1, cancellationToken: context.CancellationToken);
var searchResult = (await searchResults.FirstOrDefaultAsync())?.Record;

// If result exists, return it.
if (searchResult is not null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ public async IAsyncEnumerable<TKey> UpsertAsync(IEnumerable<TRecord> records, [E
}

/// <inheritdoc />
public Task<VectorSearchResults<TRecord>> VectorizedSearchAsync<TVector>(TVector vector, int top, VectorSearchOptions<TRecord>? options = null, CancellationToken cancellationToken = default)
public IAsyncEnumerable<VectorSearchResult<TRecord>> VectorizedSearchAsync<TVector>(TVector vector, int top, VectorSearchOptions<TRecord>? options = null, CancellationToken cancellationToken = default)
{
return this._decoratedVectorStoreRecordCollection.VectorizedSearchAsync(vector, top, options, cancellationToken);
}
Expand All @@ -135,7 +135,7 @@ public IAsyncEnumerable<TRecord> GetAsync(Expression<Func<TRecord, bool>> filter
public async Task<VectorSearchResults<TRecord>> VectorizableTextSearchAsync(string searchText, int top, VectorSearchOptions<TRecord>? options = null, CancellationToken cancellationToken = default)
{
var embeddingValue = await this._textEmbeddingGenerationService.GenerateEmbeddingAsync(searchText, cancellationToken: cancellationToken).ConfigureAwait(false);
return await this.VectorizedSearchAsync(embeddingValue, top, options, cancellationToken).ConfigureAwait(false);
return new VectorSearchResults<TRecord>(this.VectorizedSearchAsync(embeddingValue, top, options, cancellationToken));
}

/// <inheritdoc />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ public async Task IngestDataAndUseHybridSearch()
// Search the collection using a vector search.
var searchString = "What is an Application Programming Interface";
var searchVector = await textEmbeddingGenerationService.GenerateEmbeddingAsync(searchString);
var searchResult = await hybridSearchCollection.HybridSearchAsync(searchVector, ["Application", "Programming", "Interface"], top: 1);
var resultRecords = await searchResult.Results.ToListAsync();
var searchResult = hybridSearchCollection.HybridSearchAsync(searchVector, ["Application", "Programming", "Interface"], top: 1);
var resultRecords = await searchResult.ToListAsync();

Console.WriteLine("Search string: " + searchString);
Console.WriteLine("Result: " + resultRecords.First().Record.Definition);
Expand All @@ -66,8 +66,8 @@ public async Task IngestDataAndUseHybridSearch()
// Search the collection using a vector search.
searchString = "What is Retrieval Augmented Generation";
searchVector = await textEmbeddingGenerationService.GenerateEmbeddingAsync(searchString);
searchResult = await hybridSearchCollection.HybridSearchAsync(searchVector, ["Retrieval", "Augmented", "Generation"], top: 1);
resultRecords = await searchResult.Results.ToListAsync();
searchResult = hybridSearchCollection.HybridSearchAsync(searchVector, ["Retrieval", "Augmented", "Generation"], top: 1);
resultRecords = await searchResult.ToListAsync();

Console.WriteLine("Search string: " + searchString);
Console.WriteLine("Result: " + resultRecords.First().Record.Definition);
Expand All @@ -76,8 +76,8 @@ public async Task IngestDataAndUseHybridSearch()
// Search the collection using a vector search with pre-filtering.
searchString = "What is Retrieval Augmented Generation";
searchVector = await textEmbeddingGenerationService.GenerateEmbeddingAsync(searchString);
searchResult = await hybridSearchCollection.HybridSearchAsync(searchVector, ["Retrieval", "Augmented", "Generation"], top: 3, new() { Filter = g => g.Category == "External Definitions" });
resultRecords = await searchResult.Results.ToListAsync();
searchResult = hybridSearchCollection.HybridSearchAsync(searchVector, ["Retrieval", "Augmented", "Generation"], top: 3, new() { Filter = g => g.Category == "External Definitions" });
resultRecords = await searchResult.ToListAsync();

Console.WriteLine("Search string: " + searchString);
Console.WriteLine("Number of results: " + resultRecords.Count);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,8 @@ private async Task ReadDataFromCollectionAsync(IVectorStore vectorStore, string
// Search the data set.
var searchString = "I'm looking for an animal that is loyal and will make a great companion";
var searchVector = await textEmbeddingGenerationService.GenerateEmbeddingAsync(searchString);
var searchResult = await collection.VectorizedSearchAsync(searchVector, top: 1);
var resultRecords = await searchResult.Results.ToListAsync();
var searchResult = collection.VectorizedSearchAsync(searchVector, top: 1);
var resultRecords = await searchResult.ToListAsync();

this.Output.WriteLine("Search string: " + searchString);
this.Output.WriteLine("Source: " + resultRecords.First().Record.Source);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ public async Task IngestDataAndSearchAsync<TKey>(string collectionName, Func<TKe
// Search the collection using a vector search.
var searchString = "What is an Application Programming Interface";
var searchVector = await textEmbeddingGenerationService.GenerateEmbeddingAsync(searchString);
var searchResult = await collection.VectorizedSearchAsync(searchVector, top: 1);
var resultRecords = await searchResult.Results.ToListAsync();
var searchResult = collection.VectorizedSearchAsync(searchVector, top: 1);
var resultRecords = await searchResult.ToListAsync();

output.WriteLine("Search string: " + searchString);
output.WriteLine("Result: " + resultRecords.First().Record.Definition);
Expand All @@ -61,8 +61,8 @@ public async Task IngestDataAndSearchAsync<TKey>(string collectionName, Func<TKe
// Search the collection using a vector search.
searchString = "What is Retrieval Augmented Generation";
searchVector = await textEmbeddingGenerationService.GenerateEmbeddingAsync(searchString);
searchResult = await collection.VectorizedSearchAsync(searchVector, top: 1);
resultRecords = await searchResult.Results.ToListAsync();
searchResult = collection.VectorizedSearchAsync(searchVector, top: 1);
resultRecords = await searchResult.ToListAsync();

output.WriteLine("Search string: " + searchString);
output.WriteLine("Result: " + resultRecords.First().Record.Definition);
Expand All @@ -71,8 +71,8 @@ public async Task IngestDataAndSearchAsync<TKey>(string collectionName, Func<TKe
// Search the collection using a vector search with pre-filtering.
searchString = "What is Retrieval Augmented Generation";
searchVector = await textEmbeddingGenerationService.GenerateEmbeddingAsync(searchString);
searchResult = await collection.VectorizedSearchAsync(searchVector, top: 3, new() { Filter = g => g.Category == "External Definitions" });
resultRecords = await searchResult.Results.ToListAsync();
searchResult = collection.VectorizedSearchAsync(searchVector, top: 3, new() { Filter = g => g.Category == "External Definitions" });
resultRecords = await searchResult.ToListAsync();

output.WriteLine("Search string: " + searchString);
output.WriteLine("Number of results: " + resultRecords.Count);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,12 @@ public async Task VectorSearchWithMultiVectorRecordAsync()
// Search the store using the description embedding.
var searchString = "I am looking for a reasonably priced coffee maker";
var searchVector = await textEmbeddingGenerationService.GenerateEmbeddingAsync(searchString);
var searchResult = await collection.VectorizedSearchAsync(
var searchResult = collection.VectorizedSearchAsync(
searchVector, top: 1, new()
{
VectorProperty = r => r.DescriptionEmbedding
});
var resultRecords = await searchResult.Results.ToListAsync();
var resultRecords = await searchResult.ToListAsync();

WriteLine("Search string: " + searchString);
WriteLine("Result: " + resultRecords.First().Record.Description);
Expand All @@ -69,14 +69,14 @@ public async Task VectorSearchWithMultiVectorRecordAsync()
// Search the store using the feature list embedding.
searchString = "I am looking for a handheld vacuum cleaner that will remove pet hair";
searchVector = await textEmbeddingGenerationService.GenerateEmbeddingAsync(searchString);
searchResult = await collection.VectorizedSearchAsync(
searchResult = collection.VectorizedSearchAsync(
searchVector,
top: 1,
new()
{
VectorProperty = r => r.FeatureListEmbedding
});
resultRecords = await searchResult.Results.ToListAsync();
resultRecords = await searchResult.ToListAsync();

WriteLine("Search string: " + searchString);
WriteLine("Result: " + resultRecords.First().Record.Description);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public async Task VectorSearchWithPagingAsync()
while (moreResults)
{
// Get the next page of results by asking for 10 results, and using 'Skip' to skip the results from the previous pages.
var currentPageResults = await collection.VectorizedSearchAsync(
var currentPageResults = collection.VectorizedSearchAsync(
searchVector,
top: 10,
new()
Expand All @@ -57,7 +57,7 @@ public async Task VectorSearchWithPagingAsync()

// Print the results.
var pageCount = 0;
await foreach (var result in currentPageResults.Results)
await foreach (var result in currentPageResults)
{
Console.WriteLine($"Key: {result.Record.Key}, Text: {result.Record.Text}");
pageCount++;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ public async Task ExampleAsync()
// Search the collection using a vector search.
var searchString = "What is an Application Programming Interface";
var searchVector = await textEmbeddingGenerationService.GenerateEmbeddingAsync(searchString);
var searchResult = await collection.VectorizedSearchAsync(searchVector, top: 1);
var resultRecords = await searchResult.Results.ToListAsync();
var searchResult = collection.VectorizedSearchAsync(searchVector, top: 1);
var resultRecords = await searchResult.ToListAsync();

Console.WriteLine("Search string: " + searchString);
Console.WriteLine("Result: " + resultRecords.First().Record.Definition);
Expand All @@ -60,8 +60,8 @@ public async Task ExampleAsync()
// Search the collection using a vector search.
searchString = "What is Retrieval Augmented Generation";
searchVector = await textEmbeddingGenerationService.GenerateEmbeddingAsync(searchString);
searchResult = await collection.VectorizedSearchAsync(searchVector, top: 1);
resultRecords = await searchResult.Results.ToListAsync();
searchResult = collection.VectorizedSearchAsync(searchVector, top: 1);
resultRecords = await searchResult.ToListAsync();

Console.WriteLine("Search string: " + searchString);
Console.WriteLine("Result: " + resultRecords.First().Record.Definition);
Expand All @@ -70,8 +70,8 @@ public async Task ExampleAsync()
// Search the collection using a vector search with pre-filtering.
searchString = "What is Retrieval Augmented Generation";
searchVector = await textEmbeddingGenerationService.GenerateEmbeddingAsync(searchString);
searchResult = await collection.VectorizedSearchAsync(searchVector, top: 3, new() { Filter = g => g.Category == "External Definitions" });
resultRecords = await searchResult.Results.ToListAsync();
searchResult = collection.VectorizedSearchAsync(searchVector, top: 3, new() { Filter = g => g.Category == "External Definitions" });
resultRecords = await searchResult.ToListAsync();

Console.WriteLine("Search string: " + searchString);
Console.WriteLine("Number of results: " + resultRecords.Count);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ static DataModel CreateRecord(string text, ReadOnlyMemory<float> embedding)
// Search the collection using a vector search.
var searchString = "What is the Semantic Kernel?";
var searchVector = await embeddingGenerationService.GenerateEmbeddingAsync(searchString);
var searchResult = await vectorSearch!.VectorizedSearchAsync(searchVector, top: 1);
var resultRecords = await searchResult.Results.ToListAsync();
var searchResult = vectorSearch!.VectorizedSearchAsync(searchVector, top: 1);
var resultRecords = await searchResult.ToListAsync();

Console.WriteLine("Search string: " + searchString);
Console.WriteLine("Result: " + resultRecords.First().Record.Text);
Expand Down Expand Up @@ -116,8 +116,8 @@ static DataModel CreateRecord(TextSearchResult searchResult, ReadOnlyMemory<floa
// Search the collection using a vector search.
var searchString = "What is the Semantic Kernel?";
var searchVector = await embeddingGenerationService.GenerateEmbeddingAsync(searchString);
var searchResult = await vectorSearch!.VectorizedSearchAsync(searchVector, top: 1);
var resultRecords = await searchResult.Results.ToListAsync();
var searchResult = vectorSearch!.VectorizedSearchAsync(searchVector, top: 1);
var resultRecords = await searchResult.ToListAsync();

Console.WriteLine("Search string: " + searchString);
Console.WriteLine("Result: " + resultRecords.First().Record.Text);
Expand Down
4 changes: 2 additions & 2 deletions dotnet/samples/Concepts/Optimization/FrugalGPTWithFilters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -216,8 +216,8 @@ public async Task OnPromptRenderAsync(PromptRenderContext context, Func<PromptRe
var requestEmbedding = await textEmbeddingGenerationService.GenerateEmbeddingAsync(request, cancellationToken: context.CancellationToken);

// Find top N examples which are similar to original request.
var searchResults = await collection.VectorizedSearchAsync(requestEmbedding, top: TopN, cancellationToken: context.CancellationToken);
var topNExamples = (await searchResults.Results.ToListAsync(context.CancellationToken)).Select(l => l.Record).ToList();
var searchResults = collection.VectorizedSearchAsync(requestEmbedding, top: TopN, cancellationToken: context.CancellationToken);
var topNExamples = (await searchResults.ToListAsync(context.CancellationToken)).Select(l => l.Record).ToList();

// Override arguments to use only top N examples, which will be sent to LLM.
context.Arguments["Examples"] = topNExamples.Select(l => l.Example);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -298,8 +298,8 @@ public async Task<List<KernelFunction>> GetBestFunctionsAsync(
await collection.CreateCollectionIfNotExistsAsync(cancellationToken);

// Find best functions to call for original request.
var searchResults = await collection.VectorizedSearchAsync(requestEmbedding, top: numberOfBestFunctions, cancellationToken: cancellationToken);
var recordKeys = (await searchResults.Results.ToListAsync(cancellationToken)).Select(l => l.Record.Id);
var searchResults = collection.VectorizedSearchAsync(requestEmbedding, top: numberOfBestFunctions, cancellationToken: cancellationToken);
var recordKeys = (await searchResults.ToListAsync(cancellationToken)).Select(l => l.Record.Id);

return plugins
.SelectMany(plugin => plugin)
Expand Down
2 changes: 1 addition & 1 deletion dotnet/samples/Concepts/Search/VectorStore_TextSearch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ public async Task<VectorSearchResults<TRecord>> VectorizableTextSearchAsync(stri
{
var vectorizedQuery = await textEmbeddingGeneration!.GenerateEmbeddingAsync(searchText, cancellationToken: cancellationToken).ConfigureAwait(false);

return await vectorizedSearch.VectorizedSearchAsync(vectorizedQuery, top, options, cancellationToken);
return new VectorSearchResults<TRecord>(vectorizedSearch.VectorizedSearchAsync(vectorizedQuery, top, options, cancellationToken));
}

/// <inheritdoc />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,12 +119,12 @@ static TextDataModel CreateRecord(string text, ReadOnlyMemory<float> embedding)
ReadOnlyMemory<float> promptEmbedding = await embeddingGenerationService.GenerateEmbeddingAsync(prompt, cancellationToken: cancellationToken);

// Retrieve top three matching records from the vector store
VectorSearchResults<TextDataModel> result = await vsCollection.VectorizedSearchAsync(promptEmbedding, top: 3, cancellationToken: cancellationToken);
var result = vsCollection.VectorizedSearchAsync(promptEmbedding, top: 3, cancellationToken: cancellationToken);

// Return the records as resource contents
List<ResourceContents> contents = [];

await foreach (var record in result.Results)
await foreach (var record in result)
{
contents.Add(new TextResourceContents()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@ internal static async Task<VectorSearchResult<Glossary>> SearchVectorStoreAsync(
var searchVector = await textEmbeddingGenerationService.GenerateEmbeddingAsync(searchString);

// Search the store and get the single most relevant result.
var searchResult = await collection.VectorizedSearchAsync(
var searchResult = collection.VectorizedSearchAsync(
searchVector,
top: 1);
var searchResultItems = await searchResult.Results.ToListAsync();
var searchResultItems = await searchResult.ToListAsync();
return searchResultItems.First();
}

Expand All @@ -63,14 +63,14 @@ public async Task SearchAnInMemoryVectorStoreWithFilteringAsync()
var searchVector = await fixture.TextEmbeddingGenerationService.GenerateEmbeddingAsync(searchString);

// Search the store with a filter and get the single most relevant result.
var searchResult = await collection.VectorizedSearchAsync(
var searchResult = collection.VectorizedSearchAsync(
searchVector,
top: 1,
new()
{
Filter = g => g.Category == "AI"
});
var searchResultItems = await searchResult.Results.ToListAsync();
var searchResultItems = await searchResult.ToListAsync();

// Write the search result with its score to the console.
Console.WriteLine(searchResultItems.First().Record.Definition);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,10 @@ public async Task SearchAVectorStoreWithGenericDataModelAsync()
var searchVector = await fixture.TextEmbeddingGenerationService.GenerateEmbeddingAsync(searchString);

// Search the generic data model collection and get the single most relevant result.
var searchResult = await dynamicDataModelCollection.VectorizedSearchAsync(
var searchResult = dynamicDataModelCollection.VectorizedSearchAsync(
searchVector,
top: 1);
var searchResultItems = await searchResult.Results.ToListAsync();
var searchResultItems = await searchResult.ToListAsync();

// Write the search result with its score to the console.
// Note that here we can loop through all the properties
Expand Down
Loading
Loading