# Azure AI Search

## Description

Azure AI Search can be used as the backing implementation for Flex vector similarity search. Your application code should depend on `IFlexVectorStore`, while the provider implementation handles index access and SDK wiring.

## Important concepts

* **`IFlexVectorStore` is the contract**: app code performs upserts and similarity search through the shared interface, not Azure SDK types.
* **Upsert + search**: generated handlers typically call `UpsertAsync(id, vector, metadata, content)`, and generated queries call `SearchAsync(queryVector, topK, filter, minScore)`.
* **Collections**: Azure AI Search also supports `IFlexVectorStoreWithCollections` for collection/index operations when needed.

## Configuration in DI

Add the provider in your DI composition root (commonly in `EndPoints/...CommonConfigs/OtherApplicationServicesConfig.cs` or wherever you centralize registrations).

```csharp
// using Sumeru.Flex; // IFlexVectorStore

public static class OtherApplicationServicesConfig
{
	public static IServiceCollection AddOtherApplicationServices(
		this IServiceCollection services,
		IConfiguration configuration)
	{
		var section = configuration.GetSection("FlexBase:DataStores:Vector:AzureAISearch");

		services.AddFlexAzureAISearchVectorStore(options =>
		{
			section.Bind(options);
		});

		// Flex auto-wires generated Queries/Handlers/Plugins that *use* IFlexVectorStore.
		return services;
	}
}
```

## appsettings.json

Configuration is read from `FlexBase:DataStores:Vector:AzureAISearch`.

```json
{
  "FlexBase": {
	"DataStores": {
	  "Vector": {
		"AzureAISearch": {
		  "Endpoint": "https://your-search-service.search.windows.net",
		  "ApiKey": "<store-in-secrets>",
		  "IndexName": "your-index-name",
		  "Dimensions": 1536
		}
	  }
	}
  }
}
```

## Examples (template-based)

These examples mirror the generated Query and PostBus handler templates. You do **not** register these types manually—Flex discovers and wires generated Queries/Handlers/Plugins automatically.

### Similarity search (Query)

```csharp
using Microsoft.Extensions.Logging;
using Sumeru.Flex;
using System;
using System.Linq;
using System.Threading.Tasks;

namespace {YourApplication}.Queries.Vector;

/// <summary>
/// Query to search and return the single best matching vector from the VectorStore.
/// </summary>
public class SimilaritySearchGetSingle : FlexiQueryBridgeAsync<VectorSearchHitDto>
{
	protected readonly ILogger<SimilaritySearchGetSingle> _logger;
	protected readonly IFlexHost _flexHost;
	protected readonly IFlexVectorStore _vectorStore;
	protected SimilaritySearchGetSingleParams _params;
	protected FlexAppContextBridge _flexAppContext;

	public SimilaritySearchGetSingle(
		ILogger<SimilaritySearchGetSingle> logger,
		IFlexHost flexHost,
		IFlexVectorStore vectorStore)
	{
		_logger = logger;
		_flexHost = flexHost;
		_vectorStore = vectorStore;
	}

	public virtual SimilaritySearchGetSingle AssignParameters(SimilaritySearchGetSingleParams @params)
	{
		_params = @params;
		return this;
	}

	public virtual async Task<VectorSearchHitDto?> Fetch()
	{
		_flexAppContext = _params.GetAppContext();

		var results = await _vectorStore.SearchAsync(
			_params.QueryVector,
			topK: 1,
			filter: _params.Filter,
			minScore: _params.MinScore);

		var first = results.FirstOrDefault();
		if (first == null)
			return null;

		return new VectorSearchHitDto
		{
			Id = first.Id,
			Vector = first.Vector,
			Metadata = first.Metadata,
			Content = first.Content,
			Score = first.Score
		};
	}
}

public class SimilaritySearchGetSingleParams : DtoBridge
{
	public ReadOnlyMemory<float> QueryVector { get; set; }
	public FlexVectorFilter? Filter { get; set; }
	public float? MinScore { get; set; }
}

public class VectorSearchHitDto
{
	public string Id { get; set; }
	public ReadOnlyMemory<float> Vector { get; set; }
	public object? Metadata { get; set; }
	public string? Content { get; set; }
	public float Score { get; set; }
}
```

### Upsert a vector record (PostBus handler)

```csharp
using Microsoft.Extensions.Logging;
using Sumeru.Flex;
using System.Threading.Tasks;

namespace {YourApplication}.PostBusHandlers.Vector;

public partial class UpsertVectorRecordHandler : IUpsertVectorRecordHandler
{
	protected string EventCondition = "";

	protected readonly ILogger<UpsertVectorRecordHandler> _logger;
	protected readonly IFlexHost _flexHost;
	protected readonly IFlexVectorStore _vectorStore;

	protected FlexAppContextBridge? _flexAppContext;

	public UpsertVectorRecordHandler(
		ILogger<UpsertVectorRecordHandler> logger,
		IFlexHost flexHost,
		IFlexVectorStore vectorStore)
	{
		_logger = logger;
		_flexHost = flexHost;
		_vectorStore = vectorStore;
	}

	public virtual async Task Execute(UpsertVectorRecordCommand cmd, IFlexServiceBusContext serviceBusContext)
	{
		_flexAppContext = cmd.Dto.GetAppContext();  //do not remove this line

		await _vectorStore.UpsertAsync(
			cmd.Dto.Id,
			cmd.Dto.Vector,
			cmd.Dto.Metadata,
			cmd.Dto.Content);

		await this.Fire(EventCondition, serviceBusContext);
	}
}
```

## Azure AI Search considerations

* `Dimensions` must match the embedding model you use.
* Ensure the index exists (or is provisioned) with a vector field compatible with your dimensions.
* If you want Managed Identity auth, use `AddFlexAzureAISearchVectorStoreWithManagedIdentity(...)` instead of providing `ApiKey`.
