GitHub Copilot ChatGPT Claude Codex CLI Cursor opencode Skill Text

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

Ciza · 0 points · 19 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download microsoft-skills-.github_plugins_azure-sdk-dotnet_skills_azure-search-documents-dotnet-e58528d.zip · 7 KB
Part of microsoft/skills — 195 skills

Install

skills CLI npx skills add https://github.com/microsoft/skills/tree/main/.github/plugins/azure-sdk-dotnet/skills/azure-search-documents-dotnet
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install microsoft-skills@llmmart
Git 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

  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 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.

No comments yet.

Reviews (0)

No reviews yet.

Related