Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Azure AI Search SDK for .NET (Azure.Search.Documents). Use for building search applications with full-text, vector, semantic, and hybrid search.
.claude/skills/azure-search-documents-dotnet/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | — | — |
| case-02 | ✗→✓ | ▲ Improved | — | — |
| case-14 | ✗→✓ | ▲ Improved | — | — |
| case-19 | ✗→✗ | = Same ✗ | — | — |
| case-21 | ✗→✗ | = Same ✗ | — | — |
Build search applications with full-text, vector, semantic, and hybrid search capabilities.
bashdotnet add package Azure.Search.Documents dotnet add package Azure.Identity
Current Versions: Stable v11.7.0, Preview v11.8.0-beta.1
bashSEARCH_ENDPOINT=https://<search-service>.search.windows.net SEARCH_INDEX_NAME=<index-name> # For API key auth (not recommended for production) SEARCH_API_KEY=<api-key>
DefaultAzureCredential (preferred):
csharpusing Azure.Identity; using Azure.Search.Documents; var credential = new DefaultAzureCredential(); var client = new SearchClient( new Uri(Environment.GetEnvironmentVariable("SEARCH_ENDPOINT")), Environment.GetEnvironmentVariable("SEARCH_INDEX_NAME"), credential);
API Key:
csharpusing 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 | Purpose | |--------|---------| | SearchClient | Query indexes, upload/update/delete documents | | SearchIndexClient | Create/manage indexes, synonym maps | | SearchIndexerClient | Manage indexers, skillsets, data sources |
csharpusing 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);
csharpvar 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" } } };
csharpvar 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);
csharpvar 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})"); }
csharpvar 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}"); }
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);
See references/vector-search.md for detailed patterns.
csharpusing 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);
See references/semantic-search.md for detailed patterns.
csharpvar 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}"); }
csharpvar 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);
| 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 |
csharpusing 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}"); }
DefaultAzureCredential over API keys for productionFieldBuilder with model attributes for type-safe index definitionsCreateOrUpdateIndexAsync for idempotent index creationSelect to return only needed fields| File | Contents | |------|----------| | references/vector-search.md | Vector search, hybrid search, vectorizers | | references/semantic-search.md | Semantic ranking, captions, answers |
This skill is applicable to execute the workflow or actions described in the overview.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-19 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-21 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-18 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-01 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-22 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-12 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-09 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-02 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-11 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-07 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-10 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-14 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-17 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-04 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-16 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-20 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-03 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-05 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-06 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-08 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-13 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-15 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +14 percentage points is the difference between those two pass rates over the 22 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
The per-case answers from this run were removed by the retention sweep, so the case table below shows the verdicts without the text either arm produced. The counts above were recorded at the time and are unaffected. Answers are now kept for 180 days.
Other measured skills in the registry, with their headline benchmark lift.