azureaisearch

package module
v0.18.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 10, 2026 License: Apache-2.0 Imports: 20 Imported by: 0

Documentation

Overview

Package azureaisearch exposes Azure AI Search's vector capabilities through the Core vector-store capability interfaces over the REST API (Azure doesn't ship a typed Go SDK for the Search service yet). Documents containing media are rejected before indexing I/O because this adapter persists document text and metadata only.

Requirements: an Azure AI Search service (Basic tier or higher), with an index pre-provisioned through ARM / Terraform / Portal / REST. The store does NOT create indexes — Azure AI Search index schemas are typed and declared at creation; scope assumes the configured ID / content / vector / metadata fields exist.

Authentication: API key via the `api-key` header. For Managed Identity / OAuth, inject a bearer token through a custom http.Client.

Semantic search supplies one vector query. Hybrid search supplies the same vector together with `search` and restricts lexical evidence to the configured content field, leaving fusion to Azure AI Search. Search consumes server-provided continuation parameters before treating a query as complete. Returned metadata retains its JSON representation, including integers outside the exact float64 range.

Vector request shape:

POST /indexes/<index>/docs/search?api-version=2024-07-01
{
  "top": K,
  "vectorQueries": [{"kind": "vector", "vector": [...],
                     "k": K, "fields": "contentVector"}],
  "filter": "<odata>"
}

Filter visitor produces OData `$filter` syntax — metadata fields must exist as TOP-LEVEL index fields (Azure AI Search doesn't support nested-property paths in $filter). IN maps to `search.in(field, 'v1,v2,...', ',')`.

LIKE is refused. Azure's $filter offers no string function to build a pattern match on — its only Boolean functions are geo.intersects, search.in, search.ismatch, and search.ismatchscoring — and the last two run an analyzed full-text query rather than matching a whole value. search.ismatch('Alice') matches an author of "Alice Smith" or "alice", and Azure's own example notes that searching "waterfront" also matches "water" and "front". Refusing keeps one filter from meaning tokenized, case-insensitive, substring matching here and whole-value, case-sensitive matching on every other store.

Index and DeleteWhere share the document action endpoint and its 1000-action request limit. Each document's response must acknowledge its action; HTTP success alone does not establish that every action succeeded. Partial failures return an error while successful actions remain applied. Metadata cannot use the configured ID, content, or embedding fields, or protocol annotation names beginning with @. The entire Index request is checked for these conflicts before embedding or sending any batch.

Filtered deletion. Azure identifies a document to delete by its key and offers no delete-by-filter, so DeleteWhere collects keys first. It cannot collect them with skip: Azure's own continuation is the request back with a skip added, a filter-only query scores every match 1.00 in what Azure calls "an arbitrary order", and paged results over a changing index are documented as unstable, with the example returning one document twice — the same event as another being returned never. A key never enumerated is a document never deleted under a call that reported success. The walk instead follows Azure's documented "sort order and range filter as a workaround for skip", ordering by the key and carrying `<key> gt <last>` into each following page, and ends only on an empty page because a short page is not evidence of the last one. That is why NewStore also requires the configured ID field to be the index's key with `filterable` and `sortable` set: the walk cannot run without them, and Azure states they "can only be enabled when a field is first added to an index".

Vector profiles belong to the pre-provisioned index's vector field. Queries select that field and use its profile without a second store-level setting. Because the transformation below is metric-specific, NewStore reads the index definition and follows the vector field to its profile, the profile to its algorithm, and the algorithm to its metric, refusing a configured value that disagrees with ErrIncompatibleIndex. Azure treats an index definition as an object rather than content, so that read needs the admin key StoreConfig.APIKey already documents — a query key is scoped to /indexes/{name}/docs and answers 403 here, as does an Entra role without Microsoft.Search/searchServices/indexes/read. The same read catches a vector field that is absent or names no profile, which would answer every query with nothing.

Scoring. @search.score is never the raw metric value; Azure transforms it so it falls monotonically as the match worsens. The cosine transformation and its 0.333 to 1.00 range are documented, so the store inverts them to recover the cosine. Azure publishes neither for dotProduct or euclidean, so those scores pass through clamped rather than through a formula the store guessed.

Null tests emit `<field> eq null`, which OData documents as matching a field that "will be null if it was never set, or if it was explicitly set to null" — the same two states the filter AST reads as nil.

Filterable keys. A metadata key is written into the query language as text, and that language cannot quote a field name, so a filter can only name a key that is a plain identifier. An indexed key is a string literal in the filter DSL, so without that limit a caller's key was read as syntax. A document whose metadata key is anything at all still stores and reads back fine; this is only about which keys a filter can name.

See https://learn.microsoft.com/azure/search/vector-search-overview.

Index

Constants

View Source
const (
	Provider = "AzureAISearch"

	// DefaultAPIVersion targets the GA "2024-07-01" REST surface, the
	// first stable release that exposes the typed vector-query
	// payload used by the Scope store.
	DefaultAPIVersion = "2024-07-01"

	// DefaultContentField / DefaultEmbeddingField / DefaultIDField
	// name the well-known fields written to and read from each
	// document. They must exist on the underlying index schema.
	DefaultContentField     = "content"
	DefaultEmbeddingField   = "contentVector"
	DefaultIDField          = "id"
	DefaultMaxResponseBytes = int64(16 * 1024 * 1024)
)

Exported identifiers keep provider-owned names and defaults out of caller literals.

Variables

View Source
var ErrIncompatibleIndex = errors.New("azureaisearch: index is incompatible")

ErrIncompatibleIndex reports an index that cannot serve this store: the configured vector field is missing or unsearchable, the algorithm behind it was configured with a different similarity metric, or the configured ID field is not a key this store can enumerate and delete by.

Functions

This section is empty.

Types

type SimilarityMetric

type SimilarityMetric string

SimilarityMetric records the metric configured on the existing Azure AI Search vector field.

const (
	SimilarityCosine    SimilarityMetric = "cosine"
	SimilarityDot       SimilarityMetric = "dotProduct"
	SimilarityEuclidean SimilarityMetric = "euclidean"
)

The metric is a closed vocabulary because score direction and threshold semantics depend on it: the same raw number means "near" under one metric and "far" under another, so an unrecognized value must be rejected rather than guessed.

func (SimilarityMetric) String

func (s SimilarityMetric) String() string

func (SimilarityMetric) Valid

func (s SimilarityMetric) Valid() bool

type Store

type Store struct {
	// contains filtered or unexported fields
}

Store implements vector-store capabilities through the Azure AI Search REST API.

func NewStore

func NewStore(ctx context.Context, config StoreConfig) (*Store, error)

NewStore reads the existing index during construction, which is why it takes a context. Both facts it checks there fail quietly at run time: a store built on the wrong metric goes on returning scores that are wrong rather than absent, and an ID field that is not a filterable, sortable key makes DeleteWhere leave documents behind while reporting success. Both are misconfigurations at wiring, and the ID field's attributes cannot be changed once the index exists.

func (*Store) DeleteWhere

func (s *Store) DeleteWhere(ctx context.Context, expr filter.Predicate) (err error)

func (*Store) Index

func (s *Store) Index(ctx context.Context, request *vectorstore.IndexRequest) (err error)

Index validates metadata ownership across the full request, embeds documents, and uploads them through acknowledged batches of at most 1000 actions.

func (*Store) Search

func (s *Store) Search(ctx context.Context, req *vectorstore.SearchRequest) (response *vectorstore.SearchResponse, err error)

Search runs a semantic vector query or a native hybrid query that combines the same vector with lexical evidence from the configured content field.

type StoreConfig

type StoreConfig struct {
	// Endpoint is the search service URL, e.g.
	// "https://my-search.search.windows.net". Required.
	Endpoint string

	// APIKey is the admin API key. Required for both read and write.
	// Use Managed Identity / OAuth via [HTTPClient] for finer
	// authorization control.
	APIKey string

	// IndexName is the index to operate on. Required. The schema
	// must already contain the configured ID, content, vector, and
	// metadata fields — Azure AI Search index schemas are typed and
	// cannot be created lazily.
	IndexName string

	// APIVersion overrides the REST API version. Optional: defaults
	// to [DefaultAPIVersion].
	APIVersion string

	// IDField / ContentField / EmbeddingField name the well-known
	// fields on each document. Optional defaults apply. These fields must
	// differ and cannot use protocol annotation names beginning with @.
	IDField        string
	ContentField   string
	EmbeddingField string

	// EmbeddingModel produces vectors for the documents. Required.
	EmbeddingModel embedding.Model

	// DocumentBatcher batches documents before upsert. Required.
	DocumentBatcher vectorstore.Batcher

	// SimilarityMetric must match the metric in the index's vector-search
	// algorithm configuration. Required because @search.score is metric-specific.
	SimilarityMetric SimilarityMetric

	// HTTPClient lets callers override transport (timeouts,
	// proxies, MSAL bearer-token injection). Optional: defaults to
	// http.DefaultClient.
	HTTPClient *http.Client

	// MaxResponseBytes bounds every buffered HTTP response. Zero selects
	// [DefaultMaxResponseBytes].
	MaxResponseBytes int64
}

StoreConfig contains configuration options for the Azure AI Search vector store. The store talks to the REST surface directly — Azure doesn't ship a typed Go SDK for the Search service.

func (StoreConfig) Validate

func (s StoreConfig) Validate() error

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL