Azure.Search.Documents (.NET)
Build search applications with full-text, vector, semantic, and hybrid search capabilities.
Installation
bash
dotnet add package Azure.Search.Documentsdotnet add package Azure.IdentityCurrent Versions: Stable v11.7.0, Preview v11.8.0-beta.1
Environment Variables
bash
SEARCH_ENDPOINT=https://<search-service>.search.windows.net # Required: search service endpointSEARCH_INDEX_NAME=<index-name> # Required: search index nameAZURE_TOKEN_CREDENTIALS=prod # Required only if DefaultAzureCredential is used in productionSEARCH_API_KEY=<api-key> # Only required for AzureKeyCredential authAuthentication
Microsoft Entra Token Credential:
csharp
using Azure.Identity;using Azure.Search.Documents;
// Local dev: DefaultAzureCredential. Production: set AZURE_TOKEN_CREDENTIALS=prod or AZURE_TOKEN_CREDENTIALS=<specific_credential>var credential = new DefaultAzureCredential( DefaultAzureCredential.DefaultEnvironmentVariableName);// Or use a specific credential directly in production:// See https://learn.microsoft.com/dotnet/api/overview/azure/identity-readme?view=azure-dotnet#credential-classes// var credential = new ManagedIdentityCredential();var client = new SearchClient( new Uri(Environment.GetEnvironmentVariable("SEARCH_ENDPOINT")), Environment.GetEnvironmentVariable("SEARCH_INDEX_NAME"), credential);API Key:
csharp
using Azure;using Azure.Search.Documents;
var credential = new AzureKeyCredential( Environment.GetEnvironmentVariable("SEARCH_API_KEY"));var client = new SearchClient( new Uri(Environment.GetEnvironmentVariable("SEARCH_ENDPOINT")), Environment.GetEnvironmentVariable("SEARCH_INDEX_NAME"), credential);Client Selection
| Client | Purpose |
|---|---|
SearchClient | Query indexes, upload/update/delete documents |
SearchIndexClient | Create/manage indexes, synonym maps |
SearchIndexerClient | Manage indexers, skillsets, data sources |
Index Creation
Using FieldBuilder (Recommended)
csharp
using Azure.Search.Documents.Indexes;using Azure.Search.Documents.Indexes.Models;
// Define model with attributespublic class Hotel{ [SimpleField(IsKey = true, IsFilterable = true)] public string HotelId { get; set; }
[SearchableField(IsSortable = true)] public string HotelName { get; set; }
[SearchableField(AnalyzerName = LexicalAnalyzerName.EnLucene)] public string Description { get; set; }
[SimpleField(IsFilterable = true, IsSortable = true, IsFacetable = true)] public double? Rating { get; set; }
[VectorSearchField(VectorSearchDimensions = 1536, VectorSearchProfileName = "vector-profile")] public ReadOnlyMemory<float>? DescriptionVector { get; set; }}
// Create indexvar indexClient = new SearchIndexClient(endpoint, credential);var fieldBuilder = new FieldBuilder();var fields = fieldBuilder.Build(typeof(Hotel));
var index = new SearchIndex("hotels"){ Fields = fields, VectorSearch = new VectorSearch { Profiles = { new VectorSearchProfile("vector-profile", "hnsw-algo") }, Algorithms = { new HnswAlgorithmConfiguration("hnsw-algo") } }};
await indexClient.CreateOrUpdateIndexAsync(index);Manual Field Definition
csharp
var index = new SearchIndex("hotels"){ Fields = { new SimpleField("hotelId", SearchFieldDataType.String) { IsKey = true, IsFilterable = true }, new SearchableField("hotelName") { IsSortable = true }, new SearchableField("description") { AnalyzerName = LexicalAnalyzerName.EnLucene }, new SimpleField("rating", SearchFieldDataType.Double) { IsFilterable = true, IsSortable = true }, new SearchField("descriptionVector", SearchFieldDataType.Collection(SearchFieldDataType.Single)) { VectorSearchDimensions = 1536, VectorSearchProfileName = "vector-profile" } }};Document Operations
csharp
var searchClient = new SearchClient(endpoint, indexName, credential);
// Upload (add new)var hotels = new[] { new Hotel { HotelId = "1", HotelName = "Hotel A" } };await searchClient.UploadDocumentsAsync(hotels);
// Merge (update existing)await searchClient.MergeDocumentsAsync(hotels);
// Merge or Upload (upsert)await searchClient.MergeOrUploadDocumentsAsync(hotels);
// Deleteawait searchClient.DeleteDocumentsAsync("hotelId", new[] { "1", "2" });
// Batch operationsvar batch = IndexDocumentsBatch.Create( IndexDocumentsAction.Upload(hotel1), IndexDocumentsAction.Merge(hotel2), IndexDocumentsAction.Delete(hotel3));await searchClient.IndexDocumentsAsync(batch);Search Patterns
Basic Search
csharp
var options = new SearchOptions{ Filter = "rating ge 4", OrderBy = { "rating desc" }, Select = { "hotelId", "hotelName", "rating" }, Size = 10, Skip = 0, IncludeTotalCount = true};
SearchResults<Hotel> results = await searchClient.SearchAsync<Hotel>("luxury", options);
Console.WriteLine($"Total: {results.TotalCount}");await foreach (SearchResult<Hotel> result in results.GetResultsAsync()){ Console.WriteLine($"{result.Document.HotelName} (Score: {result.Score})");}Faceted Search
csharp
var options = new SearchOptions{ Facets = { "rating,count:5", "category" }};
var results = await searchClient.SearchAsync<Hotel>("*", options);
foreach (var facet in results.Value.Facets["rating"]){ Console.WriteLine($"Rating {facet.Value}: {facet.Count}");}Autocomplete and Suggestions
csharp
// Autocompletevar autocompleteOptions = new AutocompleteOptions { Mode = AutocompleteMode.OneTermWithContext };var autocomplete = await searchClient.AutocompleteAsync("lux", "suggester-name", autocompleteOptions);
// Suggestionsvar suggestOptions = new SuggestOptions { UseFuzzyMatching = true };var suggestions = await searchClient.SuggestAsync<Hotel>("lux", "suggester-name", suggestOptions);Vector Search
See references/vector-search.md [blocked] for detailed patterns.
csharp
using Azure.Search.Documents.Models;
// Pure vector searchvar vectorQuery = new VectorizedQuery(embedding){ KNearestNeighborsCount = 5, Fields = { "descriptionVector" }};
var options = new SearchOptions{ VectorSearch = new VectorSearchOptions { Queries = { vectorQuery } }};
var results = await searchClient.SearchAsync<Hotel>(null, options);Semantic Search
See references/semantic-search.md [blocked] for detailed patterns.
csharp
var options = new SearchOptions{ QueryType = SearchQueryType.Semantic, SemanticSearch = new SemanticSearchOptions { SemanticConfigurationName = "my-semantic-config", QueryCaption = new QueryCaption(QueryCaptionType.Extractive), QueryAnswer = new QueryAnswer(QueryAnswerType.Extractive) }};
var results = await searchClient.SearchAsync<Hotel>("best hotel for families", options);
// Access semantic answersforeach (var answer in results.Value.SemanticSearch.Answers){ Console.WriteLine($"Answer: {answer.Text} (Score: {answer.Score})");}
// Access captionsawait foreach (var result in results.Value.GetResultsAsync()){ var caption = result.SemanticSearch?.Captions?.FirstOrDefault(); Console.WriteLine($"Caption: {caption?.Text}");}Hybrid Search (Vector + Keyword + Semantic)
csharp
var vectorQuery = new VectorizedQuery(embedding){ KNearestNeighborsCount = 5, Fields = { "descriptionVector" }};
var options = new SearchOptions{ QueryType = SearchQueryType.Semantic, SemanticSearch = new SemanticSearchOptions { SemanticConfigurationName = "my-semantic-config" }, VectorSearch = new VectorSearchOptions { Queries = { vectorQuery } }};
// Combines keyword search, vector search, and semantic rankingvar results = await searchClient.SearchAsync<Hotel>("luxury beachfront", options);Field Attributes Reference
| Attribute | Purpose |
|---|---|
SimpleField | Non-searchable field (filters, sorting, facets) |
SearchableField | Full-text searchable field |
VectorSearchField | Vector embedding field |
IsKey = true | Document key (required, one per index) |
IsFilterable = true | Enable $filter expressions |
IsSortable = true | Enable $orderby |
IsFacetable = true | Enable faceted navigation |
IsHidden = true | Exclude from results |
AnalyzerName | Specify text analyzer |
Error Handling
csharp
using Azure;
try{ var results = await searchClient.SearchAsync<Hotel>("query");}catch (RequestFailedException ex) when (ex.Status == 404){ Console.WriteLine("Index not found");}catch (RequestFailedException ex){ Console.WriteLine($"Search error: {ex.Status} - {ex.ErrorCode}: {ex.Message}");}Best Practices
- Use
DefaultAzureCredentialover API keys for production - Use
FieldBuilderwith model attributes for type-safe index definitions - Use
CreateOrUpdateIndexAsyncfor idempotent index creation - Batch document operations for better throughput
- Use
Selectto return only needed fields - Configure semantic search for natural language queries
- Combine vector + keyword + semantic for best relevance
Reference Files
| File | Contents |
|---|---|
| references/vector-search.md [blocked] | Vector search, hybrid search, vectorizers |
| references/semantic-search.md [blocked] | Semantic ranking, captions, answers |


