Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
Vektör verilerini verimli bir şekilde depolamak ve sorgulamak için Azure DocumentDB'de .NET MongoDB sürücüsüyle vektör arama kullanmayı öğrenin.
Bu hızlı başlangıçta, GitHub'da bir .NET örnek uygulaması kullanarak temel vektör arama tekniklerine yönelik kılavuzlu bir tur sunulmaktadır.
Uygulama, modelden text-embedding-ada-002 önceden hesaplanmış vektörler içeren bir JSON dosyasında örnek bir otel veri kümesi kullanır, ancak vektörleri kendiniz de oluşturabilirsiniz. Otel verileri otel adlarını, konumlarını, açıklamalarını ve vektör eklemelerini içerir.
Önkoşullar
Azure aboneliği
- Azure aboneliğiniz yoksa ücretsiz bir hesap oluşturun
Mevcut bir Azure DocumentDB kümesi
- Kümeniz yoksa yeni bir küme oluşturun
İstemci IP adresinize erişime izin verecek şekilde yapılandırılmış güvenlik duvarı
-
text-embedding-ada-002dağıtılan model
Azure Cloud Shell'de Bash ortamını kullanın. Daha fazla bilgi için bkz. Azure Cloud Shell'i kullanmaya başlama.
CLI referans komutlarını yerel olarak çalıştırmayı tercih ediyorsanız, Azure CLI'yi yükleyin. Windows veya macOS üzerinde çalışıyorsanız, Azure CLI'yi bir Docker konteynerinde çalıştırmayı düşünün. Daha fazla bilgi için Azure CLI'nin bir Docker konteynerında nasıl çalıştırılacağını inceleyin.
Yerel bir kurulum kullanıyorsanız, az login komutunu kullanarak Azure CLI'ye giriş yapın. Kimlik doğrulama işlemini tamamlamak için, terminalinizde görüntülenen adımları takip edin. Diğer oturum açma seçenekleri için bkz. Azure CLI kullanarak Azure'da kimlik doğrulaması.
İstendiğinde, ilk kullanımda Azure CLI uzantısını yükleyin. Uzantılar hakkında daha fazla bilgi için bkz. Azure CLI ile uzantıları kullanma ve yönetme.
Yüklü olan sürümü ve bağımlı kütüphaneleri bulmak için az version komutunu çalıştırın. En son sürüme yükseltmek için az upgrade komutunu çalıştırın.
.NET 8.0 SDK veya üzeri
- Visual Studio Code için C# uzantısı
Uygulama bağımlılıkları
Uygulama aşağıdaki NuGet paketlerini kullanır:
-
Azure.Identity: Microsoft Entra Id ile parolasız kimlik doğrulaması için Azure Kimlik kitaplığı -
Azure.AI.OpenAI: Yapay zeka modelleri ile iletişim kurmak ve vektör eklemeleri oluşturmak için Azure OpenAI istemci kitaplığı -
Microsoft.Extensions.Configuration: Uygulama ayarları için yapılandırma yönetimi -
MongoDB.Driver: Veritabanı bağlantısı ve işlemleri için resmi MongoDB .NET sürücüsü -
Newtonsoft.Json: Popüler JSON serileştirme ve deserileştirme kütüphanesi
Uygulamayı yapılandırma ve çalıştırma
Uygulamayı kendi değerlerinizle yapılandırmak ve Azure DocumentDB kümenizde arama çalıştırmak için aşağıdaki adımları tamamlayın.
Uygulamayı yapılandırma
appsettings.json Yer tutucu değerlerini kendi değerlerinizle güncelleştirin:
{
"AzureOpenAI": {
"TenantId": "<your-tenant-id>",
"EmbeddingModel": "text-embedding-3-small",
"ApiVersion": "2023-05-15",
"Endpoint": "https://<your-openai-service-name>.openai.azure.com/"
},
"DataFiles": {
"WithoutVectors": "data/Hotels.json",
"WithVectors": "data/Hotels_Vector.json"
},
"Embedding": {
"FieldToEmbed": "Description",
"EmbeddedField": "DescriptionVector",
"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
}
}
Azure'da kimlik doğrulaması
Örnek uygulama DefaultAzureCredential ve Microsoft Entra ID aracılığıyla parolasız kimlik doğrulaması kullanır.
Azure kaynaklarına güvenli bir şekilde erişebilmesi için uygulamayı çalıştırmadan önce Azure CLI veya Azure PowerShell gibi desteklenen bir aracı kullanarak Azure'da oturum açın.
Uyarı
Oturum açmış kimliğinizin hem Azure DocumentDB hesabında hem de Azure OpenAI kaynağında gerekli veri düzlemi rollerine sahip olduğundan emin olun.
- Azure CLI
- Azure Geliştirici CLI
- Azure PowerShell
az login
Projeyi derle ve çalıştır
Örnek uygulama, mongoDB koleksiyonundaki vektörleştirilmiş örnek verileri doldurur ve farklı türlerde arama sorguları çalıştırmanıza olanak tanır.
dotnet runUygulamayı başlatmak için komutunu kullanın:dotnet runUygulama, veritabanı ve arama seçeneklerini belirlemeniz için bir menü yazdırır:
=== DocumentDB 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. ExitYazın
5ve Enter tuşuna basın.Uygulama veritabanını doldurup aramayı çalıştırdıktan sonra, seçilen vektör arama sorgusuyla eşleşen ilk beş oteli ve bunların benzerlik puanlarını görürsünüz.
Uygulamanın günlük ve çıktıları şunları gösterir:
- Koleksiyon oluşturma ve veri ekleme durumu
- Vektör dizini oluşturma onayı
- Otel adları, konumlar ve benzerlik puanlarıyla arama sonuçları
Örnek çıkış (kısa süre için kısaltıldı):
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. Royal Cottage Resort (Similarity: 0.4991) 2. Country Comfort Inn (Similarity: 0.4786) 3. Nordick's Valley Motel (Similarity: 0.4635) 4. Economy Universe Motel (Similarity: 0.4461) 5. Roach Motel (Similarity: 0.4388)
Uygulama kodunu keşfetme
Aşağıdaki bölümlerde örnek uygulamadaki en önemli hizmetler ve kod hakkında ayrıntılar sağlanır. Uygulama kodunun tamamını keşfetmek için GitHub deposunu ziyaret edin.
Arama hizmetini keşfetme
, VectorSearchService Azure OpenAI eklemeleri ile IVF, HNSW ve DiskANN arama tekniklerini kullanarak uçtan uca vektör benzerlik araması düzenler.
using Azure.AI.OpenAI;
using Azure.Identity;
using DocumentDBVectorSamples.Models;
using Microsoft.Extensions.Logging;
using MongoDB.Bson;
using MongoDB.Driver;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Reflection;
namespace DocumentDBVectorSamples.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
var credentialOptions = new DefaultAzureCredentialOptions();
if (!string.IsNullOrEmpty(_config.AzureOpenAI.TenantId))
{
credentialOptions.TenantId = _config.AzureOpenAI.TenantId;
}
var credential = new DefaultAzureCredential(credentialOptions);
_openAIClient = new AzureOpenAIClient(new Uri(_config.AzureOpenAI.Endpoint), credential);
}
/// <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}";
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
};
}
Yukarıdaki kodda VectorSearchService , aşağıdaki görevleri gerçekleştirir:
- koleksiyon ve dizin adlarını istenen algoritmaya göre belirler
- MongoDB koleksiyonunu oluşturur veya alır ve boşsa JSON verilerini yükler
- Algoritmaya özgü dizin seçeneklerini (IVF / HNSW / DiskANN) oluşturur ve vektör dizininin mevcut olduğundan emin olur
- Azure OpenAI aracılığıyla yapılandırılan sorgu için bir ekleme oluşturur
- Toplama arama işlem hattını oluşturur ve çalıştırır
- Sonuçları deserialize edip yazdırır.
Azure DocumentDB hizmetini keşfetme
, MongoDbService verileri yükleme, vektör dizini oluşturma, dizin listesi ve otel vektör araması için toplu eklemeler gibi görevleri işlemek için Azure DocumentDB ile etkileşimleri yönetir.
using Azure.Identity;
using DocumentDBVectorSamples.Models;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using MongoDB.Bson;
using MongoDB.Driver;
using Newtonsoft.Json;
namespace DocumentDBVectorSamples.Services;
/// <summary>
/// Service for MongoDB operations including data insertion, index management, and vector index creation.
/// Supports Azure DocumentDB 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 DocumentDB 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>Drops a collection from the specified database</summary>
public async Task DropCollectionAsync(string databaseName, string collectionName)
{
_logger.LogInformation($"Dropping collection '{collectionName}' from database '{databaseName}'");
await _client.GetDatabase(databaseName).DropCollectionAsync(collectionName);
_logger.LogInformation($"Collection '{collectionName}' dropped successfully");
}
/// <summary>
/// Creates a vector search index for DocumentDB, 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();
}
Yukarıdaki kodda MongoDbService , aşağıdaki görevleri gerçekleştirir:
- Yapılandırmayı okur ve Azure kimlik bilgileriyle parolasız bir istemci oluşturur
- Talep üzerine veritabanı veya koleksiyon başvuruları sağlar.
- Mevcut değilse yalnızca bir vektör arama dizini oluşturur
- Sistem dışı tüm veritabanlarını, koleksiyonlarını ve her koleksiyonun dizinlerini listeler
- Koleksiyon boşsa örnek verileri ekler ve destekleyici dizinler ekler
Visual Studio Code'da verileri görüntüleme ve yönetme
DocumentDB uzantısını ve C# uzantısını Visual Studio Code'a yükleyin.
DocumentDB uzantısını kullanarak Azure DocumentDB hesabınıza bağlanın.
Hotels veritabanındaki verileri ve dizinleri görüntüleyin.
Kaynakları temizle
Gereksiz maliyetlerden kaçınmak için artık gerekli olmadığında kaynak grubunu, Azure DocumentDB kümesini ve Azure OpenAI kaynağını silin.