azure-search-documents-dotnet
Azure AI Search SDK for .NET (Azure.Search.Documents). Use for building search applications with full-text, vector, semantic, and hybrid search. Covers SearchClient (queries, document CRUD), SearchIndexClient (index management), and SearchIndexerClient (indexers, skillsets). Trig
Install
npx skills add https://github.com/microsoft/skills/tree/main/.github/plugins/azure-sdk-dotnet/skills/azure-search-documents-dotnet
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install microsoft-skills@llmmart
git clone https://github.com/microsoft/skills.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole microsoft/skills collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
Azure.Search.Documents (.NET)
Build search applications with full-text, vector, semantic, and hybrid search capabilities.
Installation
dotnet add package Azure.Search.Documents
dotnet add package Azure.Identity
Current Versions: Stable v11.7.0, Preview v11.8.0-beta.1
Environment Variables
SEARCH_ENDPOINT=https://<search-service>.search.windows.net # Required: search service endpoint
SEARCH_INDEX_NAME=<index-name> # Required: search index name
AZURE_TOKEN_CREDENTIALS=prod # Required only if DefaultAzureCredential is used in production
SEARCH_API_KEY=<api-key> # Only required for AzureKeyCredential auth
Authentication
Microsoft Entra Token Credential:
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:
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)
using Azure.Search.Documents.Indexes;
using Azure.Search.Documents.Indexes.Models;
// Define model with attributes
public 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 index
var 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
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
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);
// Delete
await searchClient.DeleteDocumentsAsync("hotelId", new[] { "1", "2" });
// Batch operations
var batch = IndexDocumentsBatch.Create(
IndexDocumentsAction.Upload(hotel1),
IndexDocumentsAction.Merge(hotel2),
IndexDocumentsAction.Delete(hotel3));
await searchClient.IndexDocumentsAsync(batch);
Search Patterns
Basic Search
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
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
// Autocomplete
var autocompleteOptions = new AutocompleteOptions { Mode = AutocompleteMode.OneTermWithContext };
var autocomplete = await searchClient.AutocompleteAsync("lux", "suggester-name", autocompleteOptions);
// Suggestions
var suggestOptions = new SuggestOptions { UseFuzzyMatching = true };
var suggestions = await searchClient.SuggestAsync<Hotel>("lux", "suggester-name", suggestOptions);
Vector Search
See references/vector-search.md for detailed patterns.
using Azure.Search.Documents.Models;
// Pure vector search
var 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 for detailed patterns.
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 answers
foreach (var answer in results.Value.SemanticSearch.Answers)
{
Console.WriteLine($"Answer: {answer.Text} (Score: {answer.Score})");
}
// Access captions
await foreach (var result in results.Value.GetResultsAsync())
{
var caption = result.SemanticSearch?.Captions?.FirstOrDefault();
Console.WriteLine($"Caption: {caption?.Text}");
}
Hybrid Search (Vector + Keyword + Semantic)
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 ranking
var 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
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 | Vector search, hybrid search, vectorizers |
| references/semantic-search.md | Semantic ranking, captions, answers |
Files (skills)
-
references
-
semantic-search.md 6.8 KB
# Semantic Search Patterns Detailed patterns for semantic ranking, captions, and answers with Azure.Search.Documents. ## Index Configuration for Semantic Search ```csharp using Azure.Search.Documents.Indexes.Models; var index = new SearchIndex("articles") { Fields = { new SimpleField("id", SearchFieldDataType.String) { IsKey = true }, new SearchableField("title"), new SearchableField("content"), new SearchableField("summary"), new SimpleField("category", SearchFieldDataType.String) { IsFilterable = true } }, SemanticSearch = new SemanticSearch { DefaultConfigurationName = "my-semantic-config", Configurations = { new SemanticConfiguration("my-semantic-config", new SemanticPrioritizedFields { TitleField = new SemanticField("title"), ContentFields = { new SemanticField("content"), new SemanticField("summary") }, KeywordsFields = { new SemanticField("category") } }) } } }; await indexClient.CreateOrUpdateIndexAsync(index); ``` ## Basic Semantic Search ```csharp using Azure.Search.Documents.Models; var options = new SearchOptions { QueryType = SearchQueryType.Semantic, SemanticSearch = new SemanticSearchOptions { SemanticConfigurationName = "my-semantic-config" }, Select = { "id", "title", "content" }, Size = 10 }; var results = await searchClient.SearchAsync<Article>( "What are the best practices for cloud security?", options); await foreach (var result in results.Value.GetResultsAsync()) { Console.WriteLine($"{result.Document.Title} (Score: {result.Score})"); } ``` ## Semantic Search with Captions Captions provide highlighted excerpts showing why a document matched: ```csharp var options = new SearchOptions { QueryType = SearchQueryType.Semantic, SemanticSearch = new SemanticSearchOptions { SemanticConfigurationName = "my-semantic-config", QueryCaption = new QueryCaption(QueryCaptionType.Extractive) { HighlightEnabled = true } } }; var results = await searchClient.SearchAsync<Article>("cloud security best practices", options); await foreach (var result in results.Value.GetResultsAsync()) { Console.WriteLine($"Title: {result.Document.Title}"); if (result.SemanticSearch?.Captions != null) { foreach (var caption in result.SemanticSearch.Captions) { // Highlights contains <em> tags around key phrases Console.WriteLine($"Caption: {caption.Highlights ?? caption.Text}"); } } } ``` ## Semantic Search with Answers Answers extract direct responses from the content: ```csharp var options = new SearchOptions { QueryType = SearchQueryType.Semantic, SemanticSearch = new SemanticSearchOptions { SemanticConfigurationName = "my-semantic-config", QueryAnswer = new QueryAnswer(QueryAnswerType.Extractive) { Count = 3, // Number of answers to return Threshold = 0.7 // Minimum confidence threshold }, QueryCaption = new QueryCaption(QueryCaptionType.Extractive) } }; var results = await searchClient.SearchAsync<Article>( "What is zero trust security?", options); // Check for semantic answers (appear before documents) if (results.Value.SemanticSearch?.Answers != null) { foreach (var answer in results.Value.SemanticSearch.Answers) { Console.WriteLine($"Answer: {answer.Highlights ?? answer.Text}"); Console.WriteLine($"Score: {answer.Score}"); Console.WriteLine($"Document Key: {answer.Key}"); } } // Process documents with captions await foreach (var result in results.Value.GetResultsAsync()) { Console.WriteLine($"\nDocument: {result.Document.Title}"); Console.WriteLine($"Reranker Score: {result.SemanticSearch?.RerankerScore}"); } ``` ## Semantic Hybrid Search (Vector + Keyword + Semantic) Combines all three search modalities for best relevance: ```csharp var vectorQuery = new VectorizedQuery(embedding) { KNearestNeighborsCount = 50, Fields = { "contentVector" } }; var options = new SearchOptions { QueryType = SearchQueryType.Semantic, SemanticSearch = new SemanticSearchOptions { SemanticConfigurationName = "my-semantic-config", QueryCaption = new QueryCaption(QueryCaptionType.Extractive), QueryAnswer = new QueryAnswer(QueryAnswerType.Extractive) }, VectorSearch = new VectorSearchOptions { Queries = { vectorQuery } }, Select = { "id", "title", "content" }, Size = 10 }; // Keyword search + vector search + semantic reranking var results = await searchClient.SearchAsync<Article>( "best practices for securing cloud infrastructure", options); ``` ## Semantic Configuration Options ### SemanticPrioritizedFields | Field | Purpose | Recommendation | |-------|---------|----------------| | `TitleField` | Document title | Short, descriptive field | | `ContentFields` | Main content (ordered by priority) | Up to 10 fields, most important first | | `KeywordsFields` | Keywords/tags | Categorical or tag fields | ### QueryCaption Options ```csharp new QueryCaption(QueryCaptionType.Extractive) { HighlightEnabled = true // Wrap key phrases in <em> tags } ``` ### QueryAnswer Options ```csharp new QueryAnswer(QueryAnswerType.Extractive) { Count = 3, // Max answers to return (1-10) Threshold = 0.7 // Minimum confidence (0.0-1.0) } ``` ## Semantic Ranking Scores | Score | Description | |-------|-------------| | `result.Score` | BM25 keyword relevance score | | `result.SemanticSearch.RerankerScore` | Semantic relevance (0-4 scale) | | `answer.Score` | Answer confidence (0-1 scale) | ## Error Handling ```csharp var results = await searchClient.SearchAsync<Article>(query, options); // Check if semantic search was applied if (results.Value.SemanticSearch?.ErrorReason != null) { Console.WriteLine($"Semantic search warning: {results.Value.SemanticSearch.ErrorReason}"); // Results still returned, but without semantic ranking } ``` ## Best Practices 1. **Configure semantic fields carefully** - Title and content fields significantly impact quality 2. **Use answers for Q&A scenarios** - Set appropriate threshold to filter low-confidence answers 3. **Combine with vector search** - Semantic hybrid provides best relevance 4. **Monitor reranker scores** - Scores below 1.0 indicate weak semantic match 5. **Enable captions** - Helps users understand why documents matched 6. **Set answer count appropriately** - More answers = more latency 7. **Use filters before semantic ranking** - Reduces documents to rerank -
vector-search.md 5.8 KB
# Vector Search Patterns Detailed patterns for vector and hybrid search with Azure.Search.Documents. ## Index Configuration for Vector Search ```csharp using Azure.Search.Documents.Indexes.Models; var index = new SearchIndex("products") { Fields = { new SimpleField("id", SearchFieldDataType.String) { IsKey = true }, new SearchableField("name"), new SearchableField("description"), new SearchField("descriptionVector", SearchFieldDataType.Collection(SearchFieldDataType.Single)) { VectorSearchDimensions = 1536, // Must match embedding model VectorSearchProfileName = "vector-profile" } }, VectorSearch = new VectorSearch { Profiles = { new VectorSearchProfile("vector-profile", "hnsw-algo") { VectorizerName = "openai-vectorizer" // Optional: for integrated vectorization } }, Algorithms = { new HnswAlgorithmConfiguration("hnsw-algo") { Parameters = new HnswParameters { M = 4, EfConstruction = 400, EfSearch = 500, Metric = VectorSearchAlgorithmMetric.Cosine } } }, Vectorizers = { new AzureOpenAIVectorizer("openai-vectorizer") { Parameters = new AzureOpenAIVectorizerParameters { ResourceUri = new Uri("https://<resource>.openai.azure.com"), DeploymentName = "text-embedding-ada-002", ModelName = "text-embedding-ada-002" } } } } }; ``` ## Pure Vector Search ```csharp using Azure.Search.Documents.Models; // Get embedding from your embedding model float[] embedding = await GetEmbeddingAsync("luxury hotel with pool"); var vectorQuery = new VectorizedQuery(embedding) { KNearestNeighborsCount = 10, Fields = { "descriptionVector" }, Exhaustive = false // Use HNSW index (faster) }; var options = new SearchOptions { VectorSearch = new VectorSearchOptions { Queries = { vectorQuery } }, Select = { "id", "name", "description" } }; // Pass null for search text in pure vector search var results = await searchClient.SearchAsync<Product>(null, options); await foreach (var result in results.Value.GetResultsAsync()) { Console.WriteLine($"{result.Document.Name} (Score: {result.Score})"); } ``` ## Hybrid Search (Vector + Keyword) ```csharp var vectorQuery = new VectorizedQuery(embedding) { KNearestNeighborsCount = 10, Fields = { "descriptionVector" } }; var options = new SearchOptions { VectorSearch = new VectorSearchOptions { Queries = { vectorQuery } }, Select = { "id", "name", "description" }, Size = 10 }; // Pass search text for hybrid search var results = await searchClient.SearchAsync<Product>("luxury pool", options); ``` ## Multi-Vector Search Search across multiple vector fields: ```csharp var titleVector = new VectorizedQuery(titleEmbedding) { KNearestNeighborsCount = 10, Fields = { "titleVector" } }; var descriptionVector = new VectorizedQuery(descriptionEmbedding) { KNearestNeighborsCount = 10, Fields = { "descriptionVector" } }; var options = new SearchOptions { VectorSearch = new VectorSearchOptions { Queries = { titleVector, descriptionVector } } }; ``` ## Vector Search with Filters ```csharp var vectorQuery = new VectorizedQuery(embedding) { KNearestNeighborsCount = 10, Fields = { "descriptionVector" } }; var options = new SearchOptions { VectorSearch = new VectorSearchOptions { Queries = { vectorQuery } }, Filter = "category eq 'Electronics' and price lt 500", Select = { "id", "name", "price", "category" } }; var results = await searchClient.SearchAsync<Product>(null, options); ``` ## Integrated Vectorization (Text-to-Vector) When a vectorizer is configured, you can search with text directly: ```csharp var vectorQuery = new VectorizableTextQuery("luxury hotel with ocean view") { KNearestNeighborsCount = 10, Fields = { "descriptionVector" } }; var options = new SearchOptions { VectorSearch = new VectorSearchOptions { Queries = { vectorQuery } } }; // No need to generate embeddings client-side var results = await searchClient.SearchAsync<Hotel>(null, options); ``` ## Algorithm Configuration ### HNSW (Hierarchical Navigable Small World) Best for most scenarios - fast approximate nearest neighbor search: ```csharp new HnswAlgorithmConfiguration("hnsw-algo") { Parameters = new HnswParameters { M = 4, // Connections per layer (4-10 typical) EfConstruction = 400, // Index build quality (higher = better, slower) EfSearch = 500, // Search quality (higher = better, slower) Metric = VectorSearchAlgorithmMetric.Cosine } } ``` ### Exhaustive KNN Exact nearest neighbor search (slower but precise): ```csharp new ExhaustiveKnnAlgorithmConfiguration("exhaustive-algo") { Parameters = new ExhaustiveKnnParameters { Metric = VectorSearchAlgorithmMetric.Cosine } } ``` ## Vector Search Metrics | Metric | Use Case | |--------|----------| | `Cosine` | Text embeddings (most common) | | `Euclidean` | When magnitude matters | | `DotProduct` | Normalized vectors, performance | ## Best Practices 1. **Match dimensions** to your embedding model (e.g., 1536 for text-embedding-ada-002) 2. **Use HNSW** for production workloads (faster than exhaustive) 3. **Tune EfSearch** based on latency vs. recall requirements 4. **Apply filters** to reduce search space before vector comparison 5. **Use integrated vectorization** to simplify client code 6. **Combine with semantic ranking** for best relevance
-
-
SKILL.md 10.1 KB
--- name: azure-search-documents-dotnet description: | Azure AI Search SDK for .NET (Azure.Search.Documents). Use for building search applications with full-text, vector, semantic, and hybrid search. Covers SearchClient (queries, document CRUD), SearchIndexClient (index management), and SearchIndexerClient (indexers, skillsets). Triggers: "Azure Search .NET", "SearchClient", "SearchIndexClient", "vector search C#", "semantic search .NET", "hybrid search", "Azure.Search.Documents". license: MIT metadata: author: Microsoft version: "1.0.0" package: Azure.Search.Documents --- # Azure.Search.Documents (.NET) Build search applications with full-text, vector, semantic, and hybrid search capabilities. ## Installation ```bash dotnet add package Azure.Search.Documents dotnet add package Azure.Identity ``` **Current 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 endpoint SEARCH_INDEX_NAME=<index-name> # Required: search index name AZURE_TOKEN_CREDENTIALS=prod # Required only if DefaultAzureCredential is used in production SEARCH_API_KEY=<api-key> # Only required for AzureKeyCredential auth ``` ## Authentication **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 attributes public 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 index var 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); // Delete await searchClient.DeleteDocumentsAsync("hotelId", new[] { "1", "2" }); // Batch operations var 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 // Autocomplete var autocompleteOptions = new AutocompleteOptions { Mode = AutocompleteMode.OneTermWithContext }; var autocomplete = await searchClient.AutocompleteAsync("lux", "suggester-name", autocompleteOptions); // Suggestions var suggestOptions = new SuggestOptions { UseFuzzyMatching = true }; var suggestions = await searchClient.SuggestAsync<Hotel>("lux", "suggester-name", suggestOptions); ``` ## Vector Search See [references/vector-search.md](references/vector-search.md) for detailed patterns. ```csharp using Azure.Search.Documents.Models; // Pure vector search var 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](references/semantic-search.md) 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 answers foreach (var answer in results.Value.SemanticSearch.Answers) { Console.WriteLine($"Answer: {answer.Text} (Score: {answer.Score})"); } // Access captions await 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 ranking var 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 1. **Use `DefaultAzureCredential`** over API keys for production 2. **Use `FieldBuilder`** with model attributes for type-safe index definitions 3. **Use `CreateOrUpdateIndexAsync`** for idempotent index creation 4. **Batch document operations** for better throughput 5. **Use `Select`** to return only needed fields 6. **Configure semantic search** for natural language queries 7. **Combine vector + keyword + semantic** for best relevance ## Reference Files | File | Contents | |------|----------| | [references/vector-search.md](references/vector-search.md) | Vector search, hybrid search, vectorizers | | [references/semantic-search.md](references/semantic-search.md) | Semantic ranking, captions, answers |
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.