Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Erfahren Sie, wie Sie die Vektorsuche in Azure DocumentDB mit dem .NET MongoDB-Treiber verwenden, um Vektordaten effizient zu speichern und abzufragen.
Diese Schnellstartanleitung bietet eine geführte Tour durch wichtige Vektorsuchtechniken mithilfe einer .NET-Beispiel-App auf GitHub.
Die App verwendet ein Beispiel-Hotel-Dataset in einer JSON-Datei mit vorab berechneten Vektoren aus dem text-embedding-ada-002 Modell, aber Sie können die Vektoren auch selbst generieren. Die Hoteldaten umfassen Hotelnamen, Standorte, Beschreibungen und Vektoreinbettungen.
Voraussetzungen
Ein Azure-Abonnement
- Wenn Sie nicht über ein Azure-Abonnement verfügen, erstellen Sie ein kostenloses Konto
Ein vorhandener Azure DocumentDB-Cluster
- Wenn Sie keinen Cluster haben, erstellen Sie einen neuen Cluster.
Rollenbasierte Zugriffssteuerung (Role Based Access Control, RBAC) aktiviert
Firewall für den Zugriff auf Ihre Client-IP-Adresse konfiguriert
-
Rollenbasierte Zugriffssteuerung (Role Based Access Control, RBAC) aktiviert
text-embedding-ada-002bereitgestelltes Modell
Verwenden Sie die Bash-Umgebung in Azure Cloud Shell. Weitere Informationen finden Sie unter "Erste Schritte mit Azure Cloud Shell".
Wenn Sie CLI-Referenzbefehle lieber lokal ausführen möchten, installieren Sie die Azure CLI. Wenn Sie mit Windows oder macOS arbeiten, sollten Sie die Azure CLI in einem Docker-Container ausführen. Weitere Informationen finden Sie unter Ausführen der Azure CLI in einem Docker-Container.
Wenn Sie eine lokale Installation verwenden, melden Sie sich mithilfe des Befehls az login bei der Azure CLI an. Um den Authentifizierungsprozess abzuschließen, führen Sie die schritte aus, die in Ihrem Terminal angezeigt werden. Weitere Anmeldeoptionen finden Sie unter Authentifizieren bei Azure mithilfe der Azure CLI.
Wenn Sie dazu aufgefordert werden, installieren Sie die Azure CLI-Erweiterung bei der ersten Verwendung. Weitere Informationen zu Erweiterungen finden Sie unter Verwenden und Verwalten von Erweiterungen mit der Azure CLI.
Führen Sie az version aus, um die installierte Version und die abhängigen Bibliotheken zu ermitteln. Führen Sie az upgrade aus, um auf die neueste Version zu aktualisieren.
App-Abhängigkeiten
Die App verwendet die folgenden NuGet-Pakete:
-
Azure.Identity: Azure Identity-Bibliothek für kennwortlose Authentifizierung mit Microsoft Entra-ID -
Azure.AI.OpenAI: Azure OpenAI-Clientbibliothek für die Kommunikation mit KI-Modellen und Erstellen von Vektoreinbettungen -
Microsoft.Extensions.Configuration: Konfigurationsverwaltung für App-Einstellungen -
MongoDB.Driver: Offizieller MongoDB .NET-Treiber für Datenbankkonnektivität und -vorgänge -
Newtonsoft.Json: Beliebte JSON-Serialisierungs- und Deserialisierungsbibliothek
Konfigurieren und Ausführen der App
Führen Sie die folgenden Schritte aus, um die App mit Ihren eigenen Werten zu konfigurieren und Suchvorgänge für Ihren Azure DocumentDB-Cluster auszuführen.
Konfigurieren der App
Aktualisieren Sie die appsettings.json Platzhalterwerte mit Ihren eigenen:
{
"AzureOpenAI": {
"EmbeddingModel": "text-embedding-ada-002",
"ApiVersion": "2023-05-15",
"Endpoint": "https://<your-openai-service-name>.openai.azure.com/"
},
"DataFiles": {
"WithoutVectors": "HotelsData_toCosmosDB.JSON",
"WithVectors": "HotelsData_toCosmosDB_Vector.json"
},
"Embedding": {
"FieldToEmbed": "Description",
"EmbeddedField": "text_embedding_ada_002",
"Dimensions": 1536,
"BatchSize": 16
},
"MongoDB": {
"TenantId": "<your-tenant-id>",
"ClusterName": "<your-cluster-name>",
"LoadBatchSize": 100
},
"VectorSearch": {
"Query": "quintessential lodging near running trails, eateries, retail",
"DatabaseName": "Hotels",
"TopK": 5
}
}
Für Azure authentifizieren
Die Beispiel-App verwendet die kennwortlose Authentifizierung über DefaultAzureCredential und die Microsoft Entra-ID.
Melden Sie sich mit einem unterstützten Tool wie azure CLI oder Azure PowerShell bei Azure an, bevor Sie die Anwendung ausführen, damit sie sicher auf Azure-Ressourcen zugreifen kann.
Hinweis
Stellen Sie sicher, dass Ihre angemeldete Identität über die erforderlichen Datenebenenrollen sowohl im Azure DocumentDB-Konto als auch in der Azure OpenAI-Ressource verfügt.
az login
Erstellen und Ausführen des Projekts
Die Beispiel-App füllt vektorisierte Beispieldaten in einer MongoDB-Auflistung auf und ermöglicht es Ihnen, verschiedene Arten von Suchabfragen auszuführen.
Verwenden Sie den
dotnet runBefehl, um die App zu starten:dotnet runDie App druckt ein Menü, in dem Sie Datenbank- und Suchoptionen auswählen können:
=== Cosmos DB Vector Samples Menu === Please enter your choice (0-5): 1. Create embeddings for data 2. Show all database indexes 3. Run IVF vector search 4. Run HNSW vector search 5. Run DiskANN vector search 0. ExitGeben Sie
5ein, und drücken Sie die EINGABETASTE.Nachdem die App die Datenbank aufgefüllt und die Suche ausgeführt hat, werden die fünf besten Hotels angezeigt, die mit der ausgewählten Vektorsuchabfrage und ihren Ähnlichkeitsbewertungen übereinstimmen.
Die App-Protokollierung und -Ausgabe zeigen Folgendes an:
- Sammlungserstellungs- und Dateneinfügungsstatus
- Bestätigung der Vektorindexerstellung
- Suchergebnisse mit Hotelnamen, Standorten und Ähnlichkeitsbewertungen
Beispielausgabe (verkürzt aus Platzgründen):
MongoDB client initialized with passwordless authentication Starting DiskANN vector search workflow Collection is empty, loading data from file Successfully loaded 50 documents into collection Creating vector index 'vectorIndex_diskann' Vector index 'vectorIndex_diskann' is ready for DiskANN search Executing DiskANN vector search for top 5 results Search Results (5 found using DiskANN): 1. Roach Motel (Similarity: 0.8399) 2. Royal Cottage Resort (Similarity: 0.8385) 3. Economy Universe Motel (Similarity: 0.8360) 4. Foot Happy Suites (Similarity: 0.8354) 5. Country Comfort Inn (Similarity: 0.8346)
Erkunden Sie den App-Code
Die folgenden Abschnitte enthalten Details zu den wichtigsten Diensten und Code in der Beispiel-App. Besuchen Sie das GitHub-Repository , um den vollständigen App-Code zu erkunden.
Erkunden des Suchdiensts
Der VectorSearchService orchestriert eine End-to-End-Vektorähnlichkeitssuche mittels der Suchtechniken IVF, HNSW und DiskANN mit Azure OpenAI-Einbettungen.
using Azure.AI.OpenAI;
using Azure.Identity;
using CosmosDbVectorSamples.Models;
using Microsoft.Extensions.Logging;
using MongoDB.Bson;
using MongoDB.Driver;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Reflection;
namespace CosmosDbVectorSamples.Services.VectorSearch;
/// <summary>
/// Service for performing vector similarity searches using different algorithms (IVF, HNSW, DiskANN).
/// Handles data loading, vector index creation, query embedding generation, and search execution.
/// </summary>
public class VectorSearchService
{
private readonly ILogger<VectorSearchService> _logger;
private readonly AzureOpenAIClient _openAIClient;
private readonly MongoDbService _mongoService;
private readonly AppConfiguration _config;
public VectorSearchService(ILogger<VectorSearchService> logger, MongoDbService mongoService, AppConfiguration config)
{
_logger = logger;
_mongoService = mongoService;
_config = config;
// Initialize Azure OpenAI client with passwordless authentication
_openAIClient = new AzureOpenAIClient(new Uri(_config.AzureOpenAI.Endpoint), new DefaultAzureCredential());
}
/// <summary>
/// Executes a complete vector search workflow: data setup, index creation, query embedding, and search
/// </summary>
/// <param name="indexType">The vector search algorithm to use (IVF, HNSW, or DiskANN)</param>
public async Task RunSearchAsync(VectorIndexType indexType)
{
try
{
_logger.LogInformation($"Starting {indexType} vector search workflow");
// Setup collection
var collectionSuffix = indexType switch
{
VectorIndexType.IVF => "ivf",
VectorIndexType.HNSW => "hnsw",
VectorIndexType.DiskANN => "diskann",
_ => throw new ArgumentException($"Unknown index type: {indexType}")
};
var collectionName = $"hotels_{collectionSuffix}_fixed";
var indexName = $"vectorIndex_{collectionSuffix}";
var collection = _mongoService.GetCollection<HotelData>(_config.VectorSearch.DatabaseName, collectionName);
// Load data from file if collection is empty
var assemblyLocation = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? string.Empty;
var dataFilePath = Path.Combine(assemblyLocation, _config.DataFiles.WithVectors);
await _mongoService.LoadDataIfNeededAsync(collection, dataFilePath);
// Create the vector index with algorithm-specific search options
var searchOptions = indexType switch
{
VectorIndexType.IVF => CreateIVFSearchOptions(_config.Embedding.Dimensions),
VectorIndexType.HNSW => CreateHNSWSearchOptions(_config.Embedding.Dimensions),
VectorIndexType.DiskANN => CreateDiskANNSearchOptions(_config.Embedding.Dimensions),
_ => throw new ArgumentException($"Unknown index type: {indexType}")
};
await _mongoService.CreateVectorIndexAsync(
_config.VectorSearch.DatabaseName, collectionName, indexName,
_config.Embedding.EmbeddedField, searchOptions);
_logger.LogInformation($"Vector index '{indexName}' is ready for {indexType} search");
await Task.Delay(5000); // Allow index to be fully initialized
// Create embedding for the query
var embeddingClient = _openAIClient.GetEmbeddingClient(_config.AzureOpenAI.EmbeddingModel);
var queryEmbedding = (await embeddingClient.GenerateEmbeddingAsync(_config.VectorSearch.Query)).Value.ToFloats().ToArray();
_logger.LogInformation($"Generated query embedding with {queryEmbedding.Length} dimensions");
// Build MongoDB aggregation pipeline for vector search
var searchPipeline = new BsonDocument[]
{
// Vector similarity search using cosmosSearch
new BsonDocument("$search", new BsonDocument
{
["cosmosSearch"] = new BsonDocument
{
["vector"] = new BsonArray(queryEmbedding.Select(f => new BsonDouble(f))),
["path"] = _config.Embedding.EmbeddedField, // Field containing embeddings
["k"] = _config.VectorSearch.TopK // Number of results to return
}
}),
// Project results with similarity scores
new BsonDocument("$project", new BsonDocument
{
["score"] = new BsonDocument("$meta", "searchScore"),
["document"] = "$$ROOT"
})
};
// Execute and process the search
_logger.LogInformation($"Executing {indexType} vector search for top {_config.VectorSearch.TopK} results");
var searchResults = (await collection.AggregateAsync<BsonDocument>(searchPipeline)).ToList()
.Select(result => new SearchResult
{
Document = MongoDB.Bson.Serialization.BsonSerializer.Deserialize<HotelData>(result["document"].AsBsonDocument),
Score = result["score"].AsDouble
}).ToList();
// Print the results
if (searchResults?.Count == 0)
{
_logger.LogInformation("❌ No search results found. Check query terms and data availability.");
}
else
{
_logger.LogInformation($"\n✅ Search Results ({searchResults!.Count} found using {indexType}):");
for (int i = 0; i < searchResults.Count; i++)
{
var result = searchResults[i];
var hotelName = result.Document?.HotelName ?? "Unknown Hotel";
_logger.LogInformation($" {i + 1}. {hotelName} (Similarity: {result.Score:F4})");
}
}
}
catch (Exception ex)
{
_logger.LogError(ex, $"{indexType} vector search failed");
throw;
}
}
/// <summary>
/// Creates IVF (Inverted File) search options - good for large datasets with fast approximate search
/// </summary>
private BsonDocument CreateIVFSearchOptions(int dimensions) => new BsonDocument
{
["kind"] = "vector-ivf",
["similarity"] = "COS",
["dimensions"] = dimensions,
["numLists"] = 1
};
/// <summary>
/// Creates HNSW (Hierarchical Navigable Small World) search options - best accuracy/speed balance
/// </summary>
private BsonDocument CreateHNSWSearchOptions(int dimensions) => new BsonDocument
{
["kind"] = "vector-hnsw",
["similarity"] = "COS",
["dimensions"] = dimensions,
["m"] = 16,
["efConstruction"] = 64
};
/// <summary>
/// Creates DiskANN search options - optimized for very large datasets stored on disk
/// </summary>
private BsonDocument CreateDiskANNSearchOptions(int dimensions) => new BsonDocument
{
["kind"] = "vector-diskann",
["similarity"] = "COS",
["dimensions"] = dimensions
};
}
Im vorherigen Code führt VectorSearchService die folgenden Aufgaben aus:
- Bestimmt die Auflistungs- und Indexnamen basierend auf dem angeforderten Algorithmus.
- Erstellt oder ruft die MongoDB -Auflistung ab und lädt JSON-Daten, wenn sie leer ist
- Erstellt die algorithmusspezifischen Indexoptionen (IVF / HNSW / DiskANN) und stellt sicher, dass der Vektorindex vorhanden ist.
- Generiert eine Einbettung für die konfigurierte Abfrage über Azure OpenAI
- Erstellt und führt die Aggregationssuchpipeline aus.
- Deserialisiert und druckt die Ergebnisse.
Erkunden des Azure DocumentDB-Diensts
Die MongoDbService verwaltet die Interaktionen mit Azure DocumentDB, um Aufgaben wie das Laden von Daten, die Erstellung von Vektorindizes, die Indexauflistung und Masseneinfügungen für die Hotelvektorsuche zu handhaben.
using Azure.Identity;
using CosmosDbVectorSamples.Models;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using MongoDB.Bson;
using MongoDB.Driver;
using Newtonsoft.Json;
namespace CosmosDbVectorSamples.Services;
/// <summary>
/// Service for MongoDB operations including data insertion, index management, and vector index creation.
/// Supports Azure Cosmos DB for MongoDB with passwordless authentication.
/// </summary>
public class MongoDbService
{
private readonly ILogger<MongoDbService> _logger;
private readonly AppConfiguration _config;
private readonly MongoClient _client;
public MongoDbService(ILogger<MongoDbService> logger, IConfiguration configuration)
{
_logger = logger;
_config = new AppConfiguration();
configuration.Bind(_config);
// Validate configuration
if (string.IsNullOrEmpty(_config.MongoDB.ClusterName))
throw new InvalidOperationException("MongoDB connection not configured. Please provide ConnectionString or ClusterName.");
// Configure MongoDB connection for Azure Cosmos DB with OIDC authentication
var connectionString = $"mongodb+srv://{_config.MongoDB.ClusterName}.global.mongocluster.cosmos.azure.com/?tls=true&authMechanism=MONGODB-OIDC&retrywrites=false&maxIdleTimeMS=120000";
var settings = MongoClientSettings.FromUrl(MongoUrl.Create(connectionString));
settings.UseTls = true;
settings.RetryWrites = false;
settings.MaxConnectionIdleTime = TimeSpan.FromMinutes(2);
settings.Credential = MongoCredential.CreateOidcCredential(new AzureIdentityTokenHandler(new DefaultAzureCredential(), _config.MongoDB.TenantId));
settings.Freeze();
_client = new MongoClient(settings);
_logger.LogInformation("MongoDB client initialized with passwordless authentication");
}
/// <summary>Gets a database instance by name</summary>
public IMongoDatabase GetDatabase(string databaseName) => _client.GetDatabase(databaseName);
/// <summary>Gets a collection instance from the specified database</summary>
public IMongoCollection<T> GetCollection<T>(string databaseName, string collectionName) =>
_client.GetDatabase(databaseName).GetCollection<T>(collectionName);
/// <summary>
/// Creates a vector search index for Cosmos DB MongoDB, with support for IVF, HNSW, and DiskANN algorithms
/// </summary>
public async Task<BsonDocument> CreateVectorIndexAsync(string databaseName, string collectionName, string indexName, string embeddedField, BsonDocument cosmosSearchOptions)
{
var database = _client.GetDatabase(databaseName);
var collection = database.GetCollection<BsonDocument>(collectionName);
// Check if index already exists to avoid duplication
var indexList = await (await collection.Indexes.ListAsync()).ToListAsync();
if (indexList.Any(index => index.TryGetValue("name", out var nameValue) && nameValue.AsString == indexName))
{
_logger.LogInformation($"Vector index '{indexName}' already exists, skipping creation");
return new BsonDocument { ["ok"] = 1 };
}
// Create the specified vector index type
_logger.LogInformation($"Creating vector index '{indexName}' on field '{embeddedField}'");
return await database.RunCommandAsync<BsonDocument>(new BsonDocument
{
["createIndexes"] = collectionName,
["indexes"] = new BsonArray
{
new BsonDocument
{
["name"] = indexName,
["key"] = new BsonDocument { [embeddedField] = "cosmosSearch" },
["cosmosSearchOptions"] = cosmosSearchOptions
}
}
});
}
/// <summary>
/// Displays all indexes across all user databases, excluding system databases
/// </summary>
public async Task ShowAllIndexesAsync()
{
try
{
// Get user databases (exclude system databases)
var databases = (await (await _client.ListDatabaseNamesAsync()).ToListAsync())
.Where(name => !new[] { "admin", "config", "local" }.Contains(name)).ToList();
if (!databases.Any())
{
_logger.LogInformation("No user databases found or access denied");
return;
}
foreach (var dbName in databases)
{
var database = _client.GetDatabase(dbName);
var collections = await (await database.ListCollectionNamesAsync()).ToListAsync();
if (!collections.Any())
{
_logger.LogInformation($"Database '{dbName}': No collections found");
continue;
}
_logger.LogInformation($"\n📂 DATABASE: {dbName} ({collections.Count} collections)");
// Display indexes for each collection
foreach (var collName in collections)
{
try
{
var indexList = await (await database.GetCollection<BsonDocument>(collName).Indexes.ListAsync()).ToListAsync();
_logger.LogInformation($"\n 🗃️ COLLECTION: {collName} ({indexList.Count} indexes)");
indexList.ForEach(index => _logger.LogInformation($" Index: {index.ToJson()}"));
}
catch (Exception ex)
{
_logger.LogError(ex, $"Failed to list indexes for collection '{collName}'");
}
}
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to retrieve database indexes");
throw;
}
}
/// <summary>
/// Loads data from file into collection if the collection is empty
/// </summary>
/// <param name="collection">Target collection to load data into</param>
/// <param name="dataFilePath">Path to the JSON data file containing vector embeddings</param>
/// <returns>Number of documents loaded, or 0 if collection already had data</returns>
public async Task<int> LoadDataIfNeededAsync<T>(IMongoCollection<T> collection, string dataFilePath) where T : class
{
var existingDocCount = await collection.CountDocumentsAsync(Builders<T>.Filter.Empty);
// Skip loading if collection already has data
if (existingDocCount > 0)
{
_logger.LogInformation("Collection already contains data, skipping load operation");
return 0;
}
// Load and validate data file
_logger.LogInformation("Collection is empty, loading data from file");
if (!File.Exists(dataFilePath))
throw new FileNotFoundException($"Vector data file not found: {dataFilePath}");
var jsonContent = await File.ReadAllTextAsync(dataFilePath);
var data = JsonConvert.DeserializeObject<List<T>>(jsonContent) ?? new List<T>();
if (data.Count == 0)
throw new InvalidOperationException("No data found in the vector data file");
// Insert data using existing method
var summary = await InsertDataAsync(collection, data);
_logger.LogInformation($"Successfully loaded {summary.Inserted} documents into collection");
return summary.Inserted;
}
/// <summary>
/// Inserts data into MongoDB collection, converts JSON embeddings to float arrays, and creates standard indexes
/// </summary>
public async Task<InsertSummary> InsertDataAsync<T>(IMongoCollection<T> collection, IEnumerable<T> data)
{
var dataList = data.ToList();
_logger.LogInformation($"Processing {dataList.Count} items for insertion");
// Convert JSON array embeddings to float arrays for vector search compatibility
foreach (var hotel in dataList.OfType<HotelData>().Where(h => h.ExtraElements != null))
foreach (var kvp in hotel.ExtraElements.ToList().Where(k => k.Value is Newtonsoft.Json.Linq.JArray))
hotel.ExtraElements[kvp.Key] = ((Newtonsoft.Json.Linq.JArray)kvp.Value).Select(token => (float)token).ToArray();
int inserted = 0, failed = 0;
try
{
// Use unordered insert for better performance
await collection.InsertManyAsync(dataList, new InsertManyOptions { IsOrdered = false });
inserted = dataList.Count;
_logger.LogInformation($"Successfully inserted {inserted} items");
}
catch (Exception ex)
{
failed = dataList.Count;
_logger.LogError(ex, $"Batch insert failed for {dataList.Count} items");
}
// Create standard indexes for common query fields
var indexFields = new[] { "HotelId", "Category", "Description", "Description_fr" };
foreach (var field in indexFields)
await collection.Indexes.CreateOneAsync(new CreateIndexModel<T>(Builders<T>.IndexKeys.Ascending(field)));
return new InsertSummary { Total = dataList.Count, Inserted = inserted, Failed = failed };
}
/// <summary>Disposes the MongoDB client and its resources</summary>
public void Dispose() => _client?.Cluster?.Dispose();
}
Im vorherigen Code führt MongoDbService die folgenden Aufgaben aus:
- Liest die Konfiguration und erstellt einen kennwortlosen Client mit Azure-Anmeldeinformationen.
- Stellt Datenbank- oder Sammlungsverweise bei Bedarf bereit.
- Erstellt einen Vektorsuchindex nur, wenn er noch nicht vorhanden ist
- Listet alle Nicht-Systemdatenbanken, deren Auflistungen und die Indizes der einzelnen Auflistungen auf.
- Fügt Beispieldaten ein, wenn die Auflistung leer ist und unterstützende Indizes hinzufügt.
Anzeigen und Verwalten von Daten in Visual Studio Code
Installieren Sie die DocumentDB-Erweiterung und die C#-Erweiterung in Visual Studio Code.
Stellen Sie mithilfe der DocumentDB-Erweiterung eine Verbindung mit Ihrem Azure DocumentDB-Konto her.
Zeigen Sie die Daten und Indizes der Datenbank "Hotels" an.
Bereinigen von Ressourcen
Löschen Sie die Ressourcengruppe, den Azure DocumentDB-Cluster und die Azure OpenAI-Ressource, wenn Sie sie nicht mehr benötigen, um unnötige Kosten zu vermeiden.