Documentation
¶
Overview ¶
Package elasticsearch exposes the official go-elasticsearch v8 client through the Core vector-store capability interfaces. Documents are indexed JSON objects with a `dense_vector` field for the embedding and a nested `object` field for metadata. Documents containing media are rejected before indexing I/O because this adapter persists document text and metadata only.
Requirements: Elasticsearch 8.0+ for dense_vector + `knn` top-level query. The store uses the `knn` query (not `script_score`) for retrieval — that's GA since 8.4.
The v8 client is deliberate, not a stale pin. Elastic's clients are forward compatible only — they "support communicating with greater or equal minor versions of Elasticsearch" — and every 8.x Go client enables REST API compatibility by default, so v8 reaches a 9.x server while v9 sends compatible-with=9 and cannot serve an 8.x one. Moving to v9 would narrow which servers this store works against and gain nothing.
An index that already exists is checked rather than taken on trust. Elasticsearch derives _score from the vector field's own metric — "(1 + cosine(query, vector)) / 2" is not "1 / (1 + l2_norm(query, vector)^2)" — so a store configured for one metric against a field built for another returns plausible scores in the wrong scale, with MinScore filtering by a threshold that means something else. NewStore reads the mapping and refuses a disagreement with ErrIncompatibleIndex, which also covers a field that is absent, is not a dense_vector, holds a different width, or is mapped index:false and so "can only use exact brute-force search" rather than the knn query every Search sends. Nothing here can be repaired in place: neither similarity nor dims can be changed after the field exists.
Similarity functions: SimilarityCosine / SimilarityL2 / SimilarityDotProduct. The chosen value is recorded in the dense_vector mapping at index creation time and cannot be changed without rebuilding.
Search shape:
POST <index>/_search
{
"size": K,
"knn": {
"field": "embedding",
"query_vector": [...],
"k": K,
"num_candidates": ceil(K * NumCandidatesMultiplier),
"filter": {"query_string": {"query": "<lucene>"}}
}
}
Filter visitor produces Lucene query-string syntax — metadata fields are addressed under `metadata.<key>` paths; LIKE wildcards (% / _) map to Lucene wildcards (* / ?).
Search rejects a result that lost a targeted shard or timed out, because Elasticsearch answers with 200 and the surviving hits and a caller cannot otherwise tell a partial index from a small result.
Delete uses _delete_by_query with the same Lucene filter. Elasticsearch reports version conflicts, per-document failures, and query timeouts inside a successful response, so an incomplete deletion returns an error while the documents it already removed stay removed.
Metadata mapping. Metadata keys are unknown when the index is created, so their fields map dynamically. The default for a JSON string is "text with a .keyword sub-field" and the text field is analyzed, which would make `metadata.author:"Alice"` a tokenized, case-insensitive match — it would match an author of "Alice Smith" or of "alice". A dynamic template maps strings under the metadata path straight to keyword instead, so the field the filter compiler queries is the whole-value, case-sensitive one, and the sub-field's ignore_above cutoff never applies. An index created before this mapping needs a reindex for filters to compare exactly.
Filterable keys. A metadata key is written into the Lucene query as text, and query_string 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 metadata['a:1 OR b'] compiled to metadata.a:1 OR b and the caller's key became a term boundary and a boolean operator. 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://www.elastic.co/docs/reference for the full API.
Index ¶
- Constants
- Variables
- type SimilarityFunction
- type Store
- func (s *Store) DeleteIDs(ctx context.Context, ids []string) (err error)
- func (s *Store) DeleteWhere(ctx context.Context, expr filter.Predicate) (err error)
- func (s *Store) Index(ctx context.Context, request *vectorstore.IndexRequest) (err error)
- func (s *Store) Search(ctx context.Context, req *vectorstore.SearchRequest) (response *vectorstore.SearchResponse, err error)
- type StoreConfig
Constants ¶
const ( DefaultIndexName = "scope-vector-index" DefaultEmbeddingField = "embedding" DefaultContentField = "content" DefaultMetadataField = "metadata" DefaultSimilarity = SimilarityCosine )
Exported defaults keep constructor behavior visible and overridable.
const Provider = "Elasticsearch"
Provider is the stable backend name for host-side attribution.
Variables ¶
var ( // ErrIndexMissing reports an absent index that this store was not asked to // create. ErrIndexMissing = errors.New("elasticsearch: index not found") // ErrIncompatibleIndex reports an existing index whose vector field cannot // serve this store: it is absent, not a dense_vector, unindexed, or built // for a different similarity metric or width. ErrIncompatibleIndex = errors.New("elasticsearch: index is incompatible") )
Functions ¶
This section is empty.
Types ¶
type SimilarityFunction ¶
type SimilarityFunction string
SimilarityFunction selects the Elasticsearch dense-vector similarity metric. The chosen value is recorded in the index mapping; changing it after the index is created has no effect.
const ( // SimilarityCosine — cosine similarity. Default; suitable for // most use cases. SimilarityCosine SimilarityFunction = "cosine" // SimilarityL2 — Euclidean (L2) distance. SimilarityL2 SimilarityFunction = "l2_norm" // SimilarityDotProduct — dot product. Recommended for // already-normalized embeddings (e.g. OpenAI's). SimilarityDotProduct SimilarityFunction = "dot_product" )
func (SimilarityFunction) String ¶
func (s SimilarityFunction) String() string
func (SimilarityFunction) Valid ¶
func (s SimilarityFunction) Valid() bool
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store is an Elasticsearch-backed implementation of the vectorstore capability interfaces. It uses the dense_vector field type and the `knn` query for similarity search.
func NewStore ¶
func NewStore(ctx context.Context, config StoreConfig) (*Store, error)
NewStore performs schema setup during construction, which is why it takes a context: a store returned before its index mapping exists would fail on the first index rather than at wiring, where the misconfiguration actually is.
func (*Store) DeleteIDs ¶
DeleteIDs removes documents by their _id via a single bulk request carrying one delete action per id. An empty slice is a no-op; unknown ids are silently ignored (the bulk delete reports `not_found` rather than an error). Implements vectorstore.IDDeleter.
func (*Store) DeleteWhere ¶
DeleteWhere removes every matching document with a single delete_by_query. Elasticsearch reports per-document failures, version conflicts, and query timeouts inside a successful response, so the store treats an incomplete deletion as an error. Documents already deleted stay deleted; the caller repeats the operation to converge. Implements vectorstore.FilterDeleter.
func (*Store) Index ¶
func (s *Store) Index(ctx context.Context, request *vectorstore.IndexRequest) (err error)
func (*Store) Search ¶
func (s *Store) Search(ctx context.Context, req *vectorstore.SearchRequest) (response *vectorstore.SearchResponse, err error)
Search runs a KNN search over the embedding field. Optional metadata filtering is expressed via a query_string clause.
type StoreConfig ¶
type StoreConfig struct {
// Client is the go-elasticsearch typed client. Required.
Client *elasticsearch.Client
// IndexName names the Elasticsearch index. Optional: defaults
// to [DefaultIndexName].
IndexName string
// EmbeddingField is the dense_vector field name. Optional:
// defaults to [DefaultEmbeddingField].
EmbeddingField string
// ContentField is the field that stores the document text.
// Optional: defaults to [DefaultContentField].
ContentField string
// MetadataField is the object field that stores metadata.
// Optional: defaults to [DefaultMetadataField]. It must differ from
// ContentField and EmbeddingField.
MetadataField string
// EmbeddingModel produces vectors for the documents. Required.
EmbeddingModel embedding.Model
// DocumentBatcher batches documents before bulk upsert. Required.
DocumentBatcher vectorstore.Batcher
// Dimensions sets the dense_vector width for a newly created index. It
// must be positive when creating an index; an existing index does not need it.
Dimensions int
// Similarity selects the similarity metric used at index time.
// Optional: defaults to [SimilarityCosine].
Similarity SimilarityFunction
// InitializeSchema, when true, creates the index with the right
// mapping if it doesn't already exist. When false and the index
// is missing, [NewStore] returns [ErrIndexMissing]. Either way an index
// that already exists is checked against these settings and refused with
// [ErrIncompatibleIndex] when it disagrees.
InitializeSchema bool
// NumCandidatesMultiplier scales the KNN num_candidates parameter.
// num_candidates = ceil(topK * multiplier). Higher = better
// recall, slower. Optional: defaults to 1.5. Values below 1 are
// refused because Elasticsearch requires num_candidates to be at
// least k, so such a store could not serve any search.
NumCandidatesMultiplier float64
}
StoreConfig contains configuration options for the Elasticsearch vector store.
func (StoreConfig) Validate ¶
func (s StoreConfig) Validate() error