# Data Standard: Azure AI Search (Vector Search)

## Overview
Azure AI Search provides vector and hybrid search for RAG (Retrieval-Augmented Generation) patterns.

## Index Design
```csharp
// Define index with vector field
var index = new SearchIndex("documents-index")
{
    Fields =
    [
        new SimpleField("id", SearchFieldDataType.String) { IsKey = true },
        new SearchableField("content") { AnalyzerName = "pt.microsoft" },
        new SearchableField("title") { AnalyzerName = "pt.microsoft" },
        new SimpleField("sourceUrl", SearchFieldDataType.String) { IsFilterable = true },
        new SimpleField("category", SearchFieldDataType.String) { IsFilterable = true, IsFacetable = true },
        new VectorSearchField("contentVector", dimensions: 1536, vectorSearchProfileName: "hnsw-profile")
    ],
    VectorSearch = new VectorSearch
    {
        Profiles = { new VectorSearchProfile("hnsw-profile", "hnsw-config") },
        Algorithms = { new HnswAlgorithmConfiguration("hnsw-config") }
    }
};
await indexClient.CreateOrUpdateIndexAsync(index);
```

## Embedding Generation
```csharp
public class EmbeddingService : IEmbeddingService
{
    private readonly OpenAIClient _client;
    private const string EmbeddingModel = "text-embedding-ada-002"; // 1536 dimensions

    public async Task<float[]> GenerateEmbeddingAsync(string text)
    {
        // Truncate to model limit
        var truncated = text.Length > 8192 ? text[..8192] : text;

        var response = await _client.GetEmbeddingsAsync(
            new EmbeddingsOptions(EmbeddingModel, [truncated]));

        return response.Value.Data[0].Embedding.ToArray();
    }

    public async Task<IReadOnlyList<float[]>> GenerateBatchEmbeddingsAsync(IReadOnlyList<string> texts)
    {
        var response = await _client.GetEmbeddingsAsync(
            new EmbeddingsOptions(EmbeddingModel, texts));
        return response.Value.Data.Select(d => d.Embedding.ToArray()).ToList();
    }
}
```

## Document Indexing
```csharp
public class DocumentIndexer
{
    public async Task IndexDocumentAsync(Document doc)
    {
        var embedding = await _embeddingService.GenerateEmbeddingAsync(doc.Content);

        var searchDoc = new SearchDocument
        {
            ["id"] = doc.Id.ToString(),
            ["content"] = doc.Content,
            ["title"] = doc.Title,
            ["sourceUrl"] = doc.SourceUrl,
            ["category"] = doc.Category,
            ["contentVector"] = embedding
        };

        await _searchClient.MergeOrUploadDocumentsAsync([searchDoc]);
    }
}
```

## Hybrid Search (Vector + Keyword)
```csharp
public async Task<IReadOnlyList<SearchResult>> SearchAsync(string query, string? category = null)
{
    var queryEmbedding = await _embeddingService.GenerateEmbeddingAsync(query);

    var options = new SearchOptions
    {
        Filter = category != null ? $"category eq '{category}'" : null,
        Size = 10,
        VectorSearch = new VectorSearchOptions
        {
            Queries =
            {
                new VectorizedQuery(queryEmbedding)
                {
                    Fields = { "contentVector" },
                    KNearestNeighborsCount = 50
                }
            }
        },
        QueryType = SearchQueryType.Semantic,
        SemanticSearch = new SemanticSearchOptions
        {
            SemanticConfigurationName = "my-semantic-config",
            QueryCaption = new QueryCaption(QueryCaptionType.Extractive)
        }
    };

    var response = await _searchClient.SearchAsync<SearchDocument>(query, options);
    return await response.Value.GetResultsAsync().Select(r => new SearchResult(
        r.Document["id"].ToString()!,
        r.Document["content"].ToString()!,
        r.Score ?? 0
    )).ToListAsync();
}
```

## Cost Optimization
- Index only necessary fields (cost per GB/month)
- Use semantic ranking sparingly (higher cost per query)
- Cache frequent query embeddings (embeddings are stable for same text)
- Batch indexing for large document sets (avoid per-document API calls)
