إشعار
يتطلب الوصول إلى هذه الصفحة تخويلاً. يمكنك محاولة تسجيل الدخول أو تغيير الدلائل.
يتطلب الوصول إلى هذه الصفحة تخويلاً. يمكنك محاولة تغيير الدلائل.
تعلم استخدام البحث المتجه في Azure DocumentDB باستخدام برنامج تشغيل .NET MongoDB لتخزين واستعلام بيانات المتجهات بكفاءة.
يوفر هذا التشغيل السريع جولة إرشادية لتقنيات البحث عن المتجهات الرئيسية باستخدام نموذج تطبيق .NET على GitHub.
يستخدم التطبيق عينة من مجموعة بيانات الفندق في ملف JSON مع متجهات محسوبة مسبقا من النموذج text-embedding-ada-002 ، على الرغم من أنه يمكنك أيضا إنشاء المتجهات بنفسك. تتضمن بيانات الفندق أسماء الفنادق والمواقع والأوصاف وتضمينات المتجهات.
Prerequisites
اشتراك Azure
- إذا لم يكن لديك اشتراك Azure، فقم بإنشاء حساب مجاني
مجموعة Azure DocumentDB موجودة
- إذا لم يكن لديك عنقود، أنشئ عنقود جديد
جدار الحماية تم تعيينه للسماح بالوصول إلى عنوان IP الخاص بالعميل الخاص بك
-
text-embedding-ada-002تم نشر النموذج
استخدم بيئة Bash في Azure Cloud Shell. لمزيد من المعلومات، راجع بدء استخدام Azure Cloud Shell.
إذا كنت تفضل تشغيل أوامر مرجع CLI محلياً قم بتثبيت CLI Azure. إذا كنت تعمل على نظام تشغيل Windows أو macOS، ففكر في تشغيل Azure CLI في حاوية Docker. لمزيد من المعلومات، راجع كيفية تشغيل Azure CLI في حاوية Docker.
إذا كنت تستخدم تثبيت محلي، يُرجى تسجيل الدخول إلى Azure CLI مستخدمًا أمر az login. لإنهاء عملية المصادقة، اتبع الخطوات المعروضة في جهازك. للحصول على خيارات تسجيل الدخول الأخرى، راجع المصادقة على Azure باستخدام Azure CLI.
عندما يُطلب منك، قم بتثبيت ملحق Azure CLI عند الاستخدام لأول مرة. لمزيد من المعلومات حول الملحقات، راجع استخدام الملحقات وإدارتها باستخدام Azure CLI.
يُرجى تشغيل إصدار az للوصول إلى الإصدار والمكتبات التابعة التي تم تثبيتها. للتحديث لآخر إصدار، يُرجى تشغيل تحديث az.
.NET 8.0 SDK أو أحدث
- ملحق #C لـ Visual Studio Code
تبعيات التطبيق
يستخدم التطبيق حزم NuGet التالية:
-
Azure.Identityمكتبة هوية Azure للمصادقة بدون كلمة مرور باستخدام معرف Microsoft Entra: -
Azure.AI.OpenAIمكتبة عميل Azure OpenAI للتواصل مع نماذج الذكاء الاصطناعي وإنشاء عمليات تضمين البيانات الاتجاهية: -
Microsoft.Extensions.Configurationإدارة الضبط لإعدادات التطبيق: -
MongoDB.Driverبرنامج تشغيل MongoDB .NET الرسمي لاتصال قاعدة البيانات وعملياتها: -
Newtonsoft.Jsonمكتبة التسلسل وإلغاء التسلسل JSON الشائعة:
تكوين التطبيق وتشغيله
أكمل الخطوات التالية لتكوين التطبيق بقيمك الخاصة وإجراء عمليات البحث على مجموعة Azure DocumentDB الخاصة بك.
تكوين التطبيق
عدل قيم العناصر النائبة appsettings.json بقيم العناصر النائبة الخاصة بك:
{
"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
يستخدم نموذج التطبيق المصادقة بدون كلمة مرور عبر DefaultAzureCredential معرف Microsoft Entra.
سجل الدخول إلى Azure باستخدام أداة مدعومة مثل Azure CLI أو Azure PowerShell قبل تشغيل التطبيق حتى يتمكن من الوصول إلى موارد Azure بأمان.
إشعار
تأكد من أن هويتك المسجلة تحتوي على أدوار المستوى البيانات المطلوبة على كل من حساب Azure DocumentDB ومورد Azure OpenAI.
- Azure CLI
- واجهة سطر أوامر (CLI) لمطور Azure
- Azure PowerShell
az login
بناء المشروع وتشغيله
يملأ نموذج التطبيق عينة البيانات المتجهة في مجموعة MongoDB ويتيح لك تشغيل أنواع مختلفة من استعلامات البحث.
استخدم الأمر
dotnet runلبدء التطبيق:dotnet runيطبع التطبيق قائمة لك لتحديد خيارات قاعدة البيانات والبحث:
=== 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. Exitاكتب
5واضغط على Enter.بعد أن يقوم التطبيق بملء قاعدة البيانات وتشغيل البحث، سترى أفضل خمسة فنادق تطابق استعلام البحث المتجه المحدد ودرجات التشابه الخاصة به.
يظهر تسجيل التطبيق وإخراجه:
- إنشاء المجموعة وحالة إدراج البيانات
- تأكيد إنشاء فهرس المتجه
- نتائج البحث مع أسماء الفنادق والمواقع الجغرافية ونقاط التشابه
مثال على الإخراج (مختصرة للإيجاز):
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)
استكشاف رمز التطبيق
توفر الأقسام التالية تفاصيل حول أهم الخدمات والتعليمات البرمجية في نموذج التطبيق. قم بزيارة مستودع GitHub لاستكشاف رمز التطبيق الكامل.
استكشاف خدمة البحث
يقوم هذا VectorSearchService البحث عن تشابه المتجه من طرف إلى طرف باستخدام تقنيات البحث IVF وHNSW وDiskANN مع تضمينات Azure OpenAI.
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
};
}
في التعليمات البرمجية السابقة ، يقوم بتنفيذ VectorSearchService المهام التالية:
- تحديد أسماء المجموعة والفهرس استنادا إلى الخوارزمية المطلوبة
- إنشاء مجموعة MongoDB أو الحصول عليها وتحميل بيانات JSON إذا كانت فارغة
- يبني خيارات الفهرس الخاصة بالخوارزمية (IVF / HNSW / DiskANN) ويضمن وجود فهرس المتجه
- ينشئ تضمينا للاستعلام الذي تم تكوينه عبر Azure OpenAI
- إنشاء وتشغيل مسار البحث التجميعي
- إلغاء تسلسل النتائج وطباعتها
Explore the Azure DocumentDB service
يدير التفاعلات MongoDbService مع Azure DocumentDB للتعامل مع مهام مثل تحميل البيانات، إنشاء فهرس المتجهات، إدراج الفهرس، والإدخالات الجماعية للبحث في الفنادق المتجهية.
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();
}
في التعليمات البرمجية السابقة ، يقوم بتنفيذ MongoDbService المهام التالية:
- يقرأ التكوين وينشئ عميلا بدون كلمة مرور باستخدام بيانات اعتماد Azure
- يوفر مراجع قاعدة البيانات أو المجموعة عند الطلب
- ينشئ فهرس بحث متجه فقط إذا لم يكن موجودا بالفعل
- يسرد جميع قواعد البيانات غير التابعة للنظام ومجموعاتها وفهارس كل مجموعة
- إدراج بيانات العينة إذا كانت المجموعة فارغة وإضافة فهارس داعمة
عرض البيانات وإدارتها في Visual Studio Code
قم بتثبيت ملحق DocumentDBوملحق C# في Visual Studio Code.
اتصل بحساب Azure DocumentDB الخاص بك باستخدام إضافة DocumentDB.
عرض البيانات والفهارس في قاعدة بيانات الفنادق.
تنظيف الموارد
احذف مجموعة الموارد، وعنقود Azure DocumentDB، وموارد Azure OpenAI عندما لا تعود بحاجة إليها لتجنب التكاليف غير الضرورية.