vectorstore

package module
v0.3.0-alpha.1 Latest Latest
Warning

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

Go to latest
Published: Aug 26, 2026 License: MIT Imports: 9 Imported by: 0

README

vectorstore

Vector similarity search store abstraction with explicit backend registration and an in-memory implementation for gokit.

Features

  • Store Interface: Abstraction for vector similarity search
  • InMemoryStore: Thread-safe in-memory implementation with linear scan search
  • Metrics: Canonical metric names are cosine, dot, and l2
  • Filtering: Support for field-based filtering on search queries
  • Metadata Support: Store arbitrary JSON-serializable metadata alongside vectors

Usage

package main

import (
	"context"
	"fmt"
	"github.com/kbukum/gokit/vectorstore"
)

func main() {
	reg := vectorstore.NewFactoryRegistry()
	if err := vectorstore.RegisterMemory(reg); err != nil {
		panic(err)
	}
	store, err := vectorstore.New(reg, vectorstore.Config{
		Provider: vectorstore.ProviderMemory,
		Metric:   vectorstore.MetricCosine,
	})
	if err != nil {
		panic(err)
	}
	ctx := context.Background()
	
	// Ensure collection exists
	err = store.EnsureCollection(ctx, "documents", 384)
	if err != nil {
		panic(err)
	}
	
	// Upsert vectors with metadata
	payload1 := vectorstore.NewPointPayload().
		WithField("title", "Document 1").
		WithField("source", "web")
	
	err = store.Upsert(ctx, "documents", vectorstore.Point{
		ID: "doc1",
		Vector: []float32{
			0.1, 0.2, 0.3, // ... 384 dimensions total
		},
		Payload: payload1,
	})
	if err != nil {
		panic(err)
	}
	
	// Search for similar vectors
	query := []float32{0.1, 0.2, 0.3, /* ... */}
	results, err := store.Search(ctx, "documents", vectorstore.SearchQuery{Vector: query, Limit: 10})
	if err != nil {
		panic(err)
	}
	
	for _, result := range results {
		fmt.Printf("ID: %s, Score: %.4f, Title: %v\n",
			result.ID,
			result.Score,
			result.Payload.Fields["title"])
	}
}
Searching with Filters
// Search with field filtering
filter := vectorstore.NewSearchFilter().
	MustMatch("source", "web").
	MustMatch("status", "active")

results, err := store.Search(ctx, "documents", vectorstore.SearchQuery{Vector: query, Limit: 10, Filter: filter})
if err != nil {
	panic(err)
}
Managing Points
// Delete a point
err := store.Delete(ctx, "documents", "doc1")
if err != nil {
	panic(err)
}

// Update a point (upsert with same ID)
newPayload := vectorstore.NewPointPayload().
	WithField("title", "Updated Document 1")

err = store.Upsert(ctx, "documents", vectorstore.Point{ID: "doc1", Vector: newVector, Payload: newPayload})
if err != nil {
	panic(err)
}

Store Interface

type Store interface {
	EnsureCollection(ctx context.Context, collection string, dimensions int) error
	Upsert(ctx context.Context, collection string, point Point) error
	Search(ctx context.Context, collection string, query SearchQuery) ([]SearchResult, error)
	Delete(ctx context.Context, collection, id string) error
}

Data Types

PointPayload

Stores metadata for each vector point.

payload := vectorstore.NewPointPayload().
	WithField("key1", "value1").
	WithField("count", 42).
	WithField("active", true)

Supports any JSON-serializable types: strings, numbers, booleans, objects, arrays.

SearchResult

Result from a search query.

type SearchResult struct {
	ID      string
	Score   float32      // Cosine similarity score [-1, 1]
	Payload *PointPayload
}
SearchFilter

Optional filtering for search queries.

filter := vectorstore.NewSearchFilter().
	MustMatch("field1", "value1").
	MustMatch("field2", 42)

All conditions are AND-ed together. Only exact matches are supported.

Thread Safety

InMemoryStore is thread-safe via sync.RWMutex. Multiple goroutines can safely call methods concurrently.

Performance Notes

InMemoryStore is designed for testing and prototyping:

  • Linear scan search: O(n) per query
  • No indexing or optimization
  • All data stored in memory
  • Not suitable for production with large datasets

For production use cases, consider:

  • Qdrant for vector databases
  • Weaviate for vector search
  • Pinecone for managed vector stores
  • Elasticsearch with vector search

Testing

make test M=vectorstore

Run linting with:

make lint M=vectorstore

Examples

Multi-document RAG
// Store multiple document embeddings
for i, embedding := range embeddings {
	payload := vectorstore.NewPointPayload().
		WithField("doc_id", docIDs[i]).
		WithField("chunk_index", i)
	
	err := store.Upsert(ctx, "rag_docs", vectorstore.Point{
		ID:      fmt.Sprintf("doc_%d", i),
		Vector:  embedding,
		Payload: payload,
	})
	if err != nil {
		panic(err)
	}
}

// Search for similar documents
results, err := store.Search(ctx, "rag_docs", vectorstore.SearchQuery{Vector: queryEmbedding, Limit: 5})
// Store with source metadata
payload := vectorstore.NewPointPayload().
	WithField("source", "web").
	WithField("timestamp", "2024-01-15")

// Search only web documents
filter := vectorstore.NewSearchFilter().
	MustMatch("source", "web")

results, err := store.Search(ctx, "documents", vectorstore.SearchQuery{Vector: query, Limit: 10, Filter: filter})

Documentation

Overview

Package vectorstore provides abstractions for vector similarity search stores. It includes an in-memory store implementation for testing and prototyping.

Index

Constants

View Source
const (
	// DefaultMaxSearchLimit bounds the number of results a single search may request.
	DefaultMaxSearchLimit = 1000
	// DefaultMaxVectorDimensions bounds a collection's vector dimensionality.
	DefaultMaxVectorDimensions = 32768
	// DefaultMaxPayloadFields bounds the number of fields on a point payload.
	DefaultMaxPayloadFields = 128
	// DefaultMaxPayloadBytes bounds the approximate serialized size of a payload.
	DefaultMaxPayloadBytes = 64 * 1024
	// DefaultMaxFilterConditions bounds the number of must-match filter conditions.
	DefaultMaxFilterConditions = 32
)

Default safety bounds for vector operations. They cap resource use at every trust boundary so a single request cannot exhaust memory or overwhelm a backend. Values match the cross-kit defaults.

View Source
const (
	// ProviderMemory is the lean in-process vectorstore backend.
	ProviderMemory = "memory"

	// MetricCosine ranks by cosine similarity.
	MetricCosine = "cosine"
	// MetricDot ranks by dot product.
	MetricDot = "dot"
	// MetricL2 ranks by negative Euclidean distance, so higher scores are better.
	MetricL2 = "l2"

	DefaultProvider = ProviderMemory
	DefaultMetric   = MetricCosine
)

Variables

This section is empty.

Functions

func RegisterMemory

func RegisterMemory(reg *FactoryRegistry) error

RegisterMemory registers the core in-memory backend.

Types

type Config

type Config struct {
	Name     string            `mapstructure:"name" json:"name" yaml:"name"`
	Provider string            `mapstructure:"provider" json:"provider" yaml:"provider"`
	Metric   string            `mapstructure:"metric" json:"metric" yaml:"metric"`
	Limits   VectorStoreLimits `mapstructure:"limits" json:"limits" yaml:"limits"`
}

Config holds provider-agnostic vectorstore configuration.

func (*Config) ApplyDefaults

func (c *Config) ApplyDefaults()

ApplyDefaults fills zero-valued fields.

func (*Config) Validate

func (c *Config) Validate() error

Validate checks provider-agnostic settings.

type Factory

type Factory func(cfg Config) (Store, error)

Factory creates a Store from provider-agnostic config. Provider-specific configuration is captured by typed backend Register calls.

type FactoryRegistry

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

FactoryRegistry stores vectorstore factories by provider name.

func NewFactoryRegistry

func NewFactoryRegistry() *FactoryRegistry

NewFactoryRegistry creates an isolated vectorstore factory registry.

func (*FactoryRegistry) Get

func (r *FactoryRegistry) Get(name string) (Factory, bool)

Get returns a vectorstore factory by provider name.

func (*FactoryRegistry) Register

func (r *FactoryRegistry) Register(name string, f Factory) error

Register stores a vectorstore backend factory for a provider name.

type FilterCondition

type FilterCondition struct {
	Field string
	Value any
}

FilterCondition is a single must-match equality condition on a payload field. Value is a JSON-representable payload value (the documented opaque exception); backends restrict it to supported scalar types where required.

type InMemoryStore

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

InMemoryStore is an in-memory vector store implementation backed by a simple slice. It performs linear scan search using the configured similarity metric. Intended for unit tests and prototyping — not suitable for production workloads. Thread-safe via sync.RWMutex.

func NewInMemoryStore

func NewInMemoryStore() *InMemoryStore

NewInMemoryStore creates a new empty in-memory vector store.

func NewInMemoryStoreWithConfig

func NewInMemoryStoreWithConfig(cfg Config) (*InMemoryStore, error)

NewInMemoryStoreWithConfig creates an in-memory vector store with config.

func (*InMemoryStore) Delete

func (s *InMemoryStore) Delete(ctx context.Context, collectionName, id string) error

Delete deletes a point by ID.

func (*InMemoryStore) EnsureCollection

func (s *InMemoryStore) EnsureCollection(ctx context.Context, collectionName string, dimensions int) error

EnsureCollection ensures a collection exists, creating it if necessary.

func (*InMemoryStore) Search

func (s *InMemoryStore) Search(ctx context.Context, collectionName string, query SearchQuery) ([]SearchResult, error)

Search searches for similar vectors using the collection's similarity metric.

func (*InMemoryStore) Upsert

func (s *InMemoryStore) Upsert(ctx context.Context, collectionName string, point Point) error

Upsert inserts or updates a vector point.

type LimitExceededError

type LimitExceededError struct {
	Limit  string
	Max    int
	Actual int
}

LimitExceededError reports that a value violated a configured safety bound.

func (*LimitExceededError) Error

func (e *LimitExceededError) Error() string

type MetricError

type MetricError struct {
	Metric string
}

MetricError reports an unsupported similarity metric.

func (*MetricError) Error

func (e *MetricError) Error() string

type Point

type Point struct {
	ID      string
	Vector  []float32
	Payload *PointPayload
}

Point is a vector point to insert or update in a collection.

type PointPayload

type PointPayload struct {
	Fields map[string]any `json:"fields"`
}

PointPayload represents the metadata stored alongside each vector point.

func NewPointPayload

func NewPointPayload() *PointPayload

NewPointPayload creates a new empty PointPayload.

func (*PointPayload) Validate

func (p *PointPayload) Validate(l VectorStoreLimits) error

Validate checks a payload's field count and approximate serialized size against the limits. A nil payload is valid.

func (*PointPayload) WithField

func (p *PointPayload) WithField(key string, value any) *PointPayload

WithField adds a field to the payload and returns the payload for chaining.

type SearchFilter

type SearchFilter struct {
	Must []FilterCondition
}

SearchFilter represents optional filters for search queries.

func NewSearchFilter

func NewSearchFilter() *SearchFilter

NewSearchFilter creates a new empty SearchFilter.

func (*SearchFilter) MustMatch

func (f *SearchFilter) MustMatch(field string, value any) *SearchFilter

MustMatch adds a must-match condition to the filter.

func (*SearchFilter) Validate

func (f *SearchFilter) Validate(l VectorStoreLimits) error

Validate checks a filter's condition count against the limits. A nil filter is valid.

type SearchQuery

type SearchQuery struct {
	Vector []float32
	// Limit caps the number of results; it must be non-negative (0 returns no results).
	Limit  int
	Filter *SearchFilter
}

SearchQuery describes a vector similarity search.

type SearchResult

type SearchResult struct {
	ID      string        `json:"id"`
	Score   float32       `json:"score"`
	Payload *PointPayload `json:"payload"`
}

SearchResult represents a single result from a vector search.

type Store

type Store interface {
	// EnsureCollection ensures a collection exists, creating it if necessary.
	EnsureCollection(ctx context.Context, collection string, dimensions int) error

	// Upsert inserts or updates a vector point.
	Upsert(ctx context.Context, collection string, point Point) error

	// Search searches for similar vectors. It returns a non-nil,
	// possibly empty slice when there are no matches (including when SearchQuery.Limit is zero).
	Search(ctx context.Context, collection string, query SearchQuery) ([]SearchResult, error)

	// Delete deletes a point by ID.
	Delete(ctx context.Context, collection, id string) error
}

Store is the interface for vector similarity search stores.

func New

func New(reg *FactoryRegistry, cfg Config) (Store, error)

New creates a Store using the selected registered provider.

type VectorStoreLimits

type VectorStoreLimits struct {
	MaxSearchLimit      int `mapstructure:"max_search_limit" json:"max_search_limit" yaml:"max_search_limit"`
	MaxVectorDimensions int `mapstructure:"max_vector_dimensions" json:"max_vector_dimensions" yaml:"max_vector_dimensions"`
	MaxPayloadFields    int `mapstructure:"max_payload_fields" json:"max_payload_fields" yaml:"max_payload_fields"`
	MaxPayloadBytes     int `mapstructure:"max_payload_bytes" json:"max_payload_bytes" yaml:"max_payload_bytes"`
	MaxFilterConditions int `mapstructure:"max_filter_conditions" json:"max_filter_conditions" yaml:"max_filter_conditions"`
}

VectorStoreLimits captures the safety bounds shared by every vectorstore backend. A zero-valued field adopts the corresponding default via ApplyDefaults.

func DefaultLimits

func DefaultLimits() VectorStoreLimits

DefaultLimits returns the default safety bounds.

func (*VectorStoreLimits) ApplyDefaults

func (l *VectorStoreLimits) ApplyDefaults()

ApplyDefaults fills any zero-valued bound with its default.

func (VectorStoreLimits) ValidateDimensions

func (l VectorStoreLimits) ValidateDimensions(dimensions int) error

ValidateDimensions checks that a collection's dimensionality is within bounds.

func (VectorStoreLimits) ValidateSearchLimit

func (l VectorStoreLimits) ValidateSearchLimit(limit int) error

ValidateSearchLimit checks a search limit. A zero limit is allowed and yields an empty result; a negative limit is rejected.

Jump to

Keyboard shortcuts

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