This page explains the internal design of go-vectorstore and how requests move through the library.
The project is split into two layers:
vectordata: backend-agnostic contracts and primitivesstores/postgres: PostgreSQL + pgvector implementation
This keeps the public API stable while allowing additional storage engines later.
The base record type is vectordata.Record:
ID(string): primary keyVector([]float32): embedding valuesMetadata(map[string]any): structured JSON metadataContent(*string): optional text payload
Search returns vectordata.SearchResult:
Record: matched itemDistance: backend-computed distance valueScore: normalized score derived from distance
Score normalization:
- cosine:
1 - distance - l2:
1 / (1 + distance) - inner product:
-distance
Main interfaces:
vectordata.VectorStorevectordata.Collection
Main operations:
EnsureCollectionInsert,UpsertGet,Delete,CountSearchByVectorEnsureIndexes
All methods require context.Context.
postgres.StoreOptions defaults (postgres.DefaultStoreOptions()):
Schema:publicEnsureExtension:trueStrictByDefault:true
Collection defaults:
- Metric defaults to cosine when omitted
- Ensure mode defaults to:
EnsureStrictifStrictByDefault=trueEnsureAutoMigrateifStrictByDefault=false
Other operational defaults:
- Writes are chunked at
maxRowsPerStatement=500 - Search default projection includes
MetadataandContent, but notVector
PostgresVectorStore.EnsureCollection:
- Validates collection spec (
name,dimension,metric,mode) - Ensures
vectorextension (if enabled) and schema - Checks whether the table exists
- Creates the table or validates existing schema
- Returns a
PostgresCollectionhandle
Default table shape:
id text primary key,
vector vector(n) not null,
metadata jsonb not null default '{}'::jsonb,
content textBulk writes are chunked and executed as parameterized SQL:
Insert: plainINSERTUpsert:INSERT ... ON CONFLICT (id) DO UPDATE
Each record is validated before sending:
- non-empty
ID - vector dimension match
- metadata JSON serialization
SearchByVector builds a query plan, then executes it:
- Validates
topK > 0 - Validates query vector dimension
- Chooses operator by metric:
- cosine:
<=> - l2:
<-> - inner product:
<#>
- cosine:
- Builds
distanceexpression ("vector" <op> $1::vector) - Applies optional filter SQL
- Applies optional distance threshold (
distance <= threshold) - Orders by
distance ASC, limits bytopK - Scans rows into
SearchResult
The pgvector operators are documented in the pgvector README.
Filters are represented as an AST in vectordata:
Eq,In,Gt,Lt,ExistsAnd,Or,Not
Fields can target:
- fixed columns (
Column("id"),Column("content")) - metadata JSON paths (
Metadata("category"),Metadata("a", "b"))
CompileFilterSQL converts AST to:
- SQL predicate fragment
- parameter list
Behavior details:
- SQL injection safety is preserved by binding values as query args
- metadata
Eq/Incompares JSONB values (::jsonb), so value types matter - metadata
Gt/Ltuses numeric comparison when the input is numeric, otherwise text comparison - column filters are whitelist-based (
id,contentin the postgres backend)
JSON path extraction behavior comes from PostgreSQL JSON/JSONB functions and operators.
CollectionSpec.Mode controls ensure behavior:
EnsureStrict:- fails if expected columns/type/dimension mismatch
EnsureAutoMigrate:- can add missing optional columns (
metadata,content)
- can add missing optional columns (
Dimension is always validated against vector(n) and must match.
EnsureIndexes supports:
- Vector index:
- HNSW (default method)
- IVFFlat (optional)
- Metadata index:
- GIN on
metadataJSONB - optional
jsonb_path_ops
- GIN on
Defaults when index options are omitted:
- vector index name:
idx_<collection>_vector_<method> - metadata index name:
idx_<collection>_metadata_gin - HNSW:
m=16,ef_construction=64 - IVFFlat:
lists=100
Metric-specific operator classes are selected automatically:
- cosine ->
vector_cosine_ops - l2 ->
vector_l2_ops - inner product ->
vector_ip_ops
For vector indexing details:
- HNSW: original paper and overview
- IVFFlat: FAISS index reference and pgvector IVFFlat docs
For metadata indexing details:
- GIN index basics: PostgreSQL GIN indexes
- JSONB operators and indexing context: PostgreSQL JSON functions and operators
MVP public API is record-based.
An optional typed wrapper exists:
vectordata.Codec[T]vectordata.TypedCollection[T]
This lets application code work with domain models while storage stays record-oriented.
Common exported errors:
ErrNotFoundErrDimensionMismatchErrSchemaMismatchErrInvalidFilter
Errors are wrapped with context so callers can use errors.Is(...) against base error types.
- Build embeddings for 3 fake articles via OpenAI embeddings API
- Ensure a Postgres vector collection
- Upsert article records
- Ensure vector + metadata indexes
- Embed user query and run semantic search
- Print ranked top matches with score and distance
- Build embeddings for manually chunked Lacrimosa story records
- Ensure a Postgres vector collection
- Upsert chunk records and ensure indexes
- Embed user prompt and retrieve nearest chunks from DB
- Send retrieved chunks as context to OpenAI chat completions
- Print the grounded answer
Current MVP scope:
- Postgres + pgvector backend
- single-vector column per collection
- metadata filtering through a focused AST
Future extension points:
- additional backends under
stores/ - richer filter operators
- optional reranking strategies
- pgvector project docs: https://github.com/pgvector/pgvector
- PostgreSQL docs: https://www.postgresql.org/docs/current/index.html