A lightweight Go vector-store library inspired by Microsoft.Extensions.VectorData.
MVP scope:
- Go 1.24.x
- Postgres +
pgvector - Record-based core API with optional typed codec wrapper
This library can be used to build retrieval systems such as:
- semantic search
- RAG pipelines (retrieve from vector DB, then generate with an LLM)
- context-aware assistants grounded on your own data
- Go 1.24.x
- PostgreSQL 16+ with
pgvectorextension - Docker (optional, for integration tests and sample compose flows)
vectordata: backend-agnostic core interfaces, record model, filters, typed wrapperstores/postgres: Postgres implementation withpgxpoolsamples: runnable demos (seesamples/README.md)docs: architecture and implementation notes
go get github.com/gabisonia/go-vectorstore- Start local Postgres +
pgvectorfrom the repository root:
docker compose up -d postgres- Export your OpenAI key and run the semantic sample:
export OPENAI_API_KEY=your_key_here
go get github.com/gabisonia/go-vectorstore/vectordata@latest
go run ./samples/semantic-search -q "how can I reduce cloud costs?"- Stop local services when done:
docker compose downpackage main
import (
"context"
"fmt"
"github.com/gabisonia/go-vectorstore/stores/postgres"
"github.com/gabisonia/go-vectorstore/vectordata"
"github.com/jackc/pgx/v5/pgxpool"
)
func main() {
ctx := context.Background()
pool, err := pgxpool.New(ctx, "postgres://postgres:postgres@localhost:54329/vectorstore_test?sslmode=disable")
if err != nil {
panic(err)
}
defer pool.Close()
store, err := postgres.NewVectorStore(pool, postgres.DefaultStoreOptions())
if err != nil {
panic(err)
}
collection, err := store.EnsureCollection(ctx, vectordata.CollectionSpec{
Name: "docs",
Dimension: 3,
Metric: vectordata.DistanceCosine,
Mode: vectordata.EnsureStrict,
})
if err != nil {
panic(err)
}
records := []vectordata.Record{
{
ID: "doc-1",
Vector: []float32{0.1, 0.2, 0.3},
Metadata: map[string]any{"category": "news", "rank": 10},
},
{
ID: "doc-2",
Vector: []float32{0.2, 0.1, 0.2},
Metadata: map[string]any{"category": "blog", "rank": 5},
},
}
if err := collection.Upsert(ctx, records); err != nil {
panic(err)
}
if err := collection.EnsureIndexes(ctx, vectordata.IndexOptions{
Vector: &vectordata.VectorIndexOptions{
Method: vectordata.IndexMethodHNSW,
Metric: vectordata.DistanceCosine,
HNSW: vectordata.HNSWOptions{
M: 16,
EfConstruction: 64,
},
},
Metadata: &vectordata.MetadataIndexOptions{UsePathOps: true},
}); err != nil {
panic(err)
}
filter := vectordata.And(
vectordata.Eq(vectordata.Metadata("category"), "news"),
vectordata.Gt(vectordata.Metadata("rank"), 6),
)
results, err := collection.SearchByVector(
ctx,
[]float32{0.1, 0.2, 0.25},
5,
vectordata.SearchOptions{Filter: filter},
)
if err != nil {
panic(err)
}
for _, r := range results {
fmt.Printf("id=%s score=%.4f distance=%.4f\n", r.Record.ID, r.Score, r.Distance)
}
}SearchByVector supports filtering, thresholding, and projection control.
threshold := 0.35
projection := &vectordata.Projection{
IncludeMetadata: true,
IncludeContent: true,
IncludeVector: false,
}
results, err := collection.SearchByVector(ctx, queryVector, 10, vectordata.SearchOptions{
Filter: vectordata.Eq(vectordata.Metadata("category"), "backend"),
Threshold: &threshold,
Projection: projection,
})If Projection is nil, the default projection includes Metadata and Content, but not Vector.
store, err := postgres.NewVectorStore(pool, postgres.StoreOptions{
Schema: "public",
EnsureExtension: true,
StrictByDefault: true,
})Schema: SQL schema for collection tablesEnsureExtension: auto-runsCREATE EXTENSION IF NOT EXISTS vectorStrictByDefault: default ensure mode whenCollectionSpec.Modeis not set
go test -tags=integration ./...Unit tests run with:
go test ./...Notes:
- Integration tests start Postgres/pgvector automatically via Testcontainers
- Docker daemon must be available when running integration tests
- Optional override: set
PGVECTOR_TEST_DSNto use an existing Postgres instance instead of starting a container
docker-compose.yml at the repository root is kept for manual local runs.
- Use root
docker-compose.ymlwhen you want a persistent local Postgres+pgvector instance outside tests - Use Testcontainers (
go test -tags=integration ./...) for integration tests - Sample apps have their own compose files at
samples/semantic-search/docker-compose.ymlandsamples/ragrimosa/docker-compose.yml - Sample Dockerfiles use
golang:1.24-alpineto match the repo Go version (1.24.x)
GitHub Actions workflows are configured for:
- CI on pushes/PRs (
.github/workflows/ci.yml) - release publishing (
.github/workflows/release.yml)
Release options:
- Manual (recommended): run
Releaseworkflow via GitHub UI withversioninput (0.3.0orv0.3.0). - Tag-driven: push a semver tag and the workflow publishes release notes automatically:
VERSION="$(cat VERSION)"
git tag "v${VERSION}"
git push origin "v${VERSION}"- Overview and entry points:
samples/README.md - Semantic search sample app:
samples/semantic-search - RAG sample app (
RAGrimosa):samples/ragrimosa
- Docs index:
docs/README.md - Internals and architecture:
docs/architecture.md - Connector development guide:
docs/connector-development.md
This project is licensed under the MIT License.