Mode d’emploi
- Copiez la commande et examinez le paquet avant de l’installer dans votre environnement agent.
- Consultez le document original pour les outils, identifiants et environnements requis.
- Décrivez les entrées et le livrable souhaité ; comparez la sortie aux sources.
npx skills add https://github.com/victorzhuk/skills --skill z-go-pgvectorAvant de commencer
- L’installation n’inclut ni abonnement au modèle, ni quota API, ni accès aux fichiers.
- Étoiles et installations reflètent l’activité, pas un test indépendant de qualité ou de sécurité.
Rendu depuis le dépôt source en conservant titres, exemples, code, tableaux, liens et images.
pgvector similarity search in Go
pgvector is a PostgreSQL extension that adds a vector type and distance operators for ANN search. github.com/pgvector/pgvector-go wraps a []float32 in a pgvector.Vector that pgx sends as a regular query parameter — no ORM, no manual text encoding, once pgxvec.RegisterTypes is wired into the pool.
Extension and column
CREATE EXTENSION IF NOT EXISTS vector;
CREATE TABLE items (
id bigserial PRIMARY KEY,
content text NOT NULL,
embedding vector(1536) -- dimension must match your model's output
);The dimension is fixed at column creation. Changing it later requires ALTER TABLE items ALTER COLUMN embedding TYPE vector(N) plus re-embedding every row.
Encoding []float32 for a query
Primary: github.com/pgvector/pgvector-go wraps a []float32 in a pgvector.Vector that pgx sends as a query parameter directly — no manual casting.
import (
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgxpool"
"github.com/pgvector/pgvector-go"
pgxvec "github.com/pgvector/pgvector-go/pgx"
)
cfg, err := pgxpool.ParseConfig(dsn)
if err != nil {
return nil, fmt.Errorf("parse pool config: %w", err)
}
cfg.AfterConnect = func(ctx context.Context, conn *pgx.Conn) error {
return pgxvec.RegisterTypes(ctx, conn)
}
pool, err := pgxpool.NewWithConfig(ctx, cfg)pgxvec.RegisterTypes in AfterConnect runs once per pooled connection. After that, every SQL site passes pgvector.NewVector(vec) as a plain $n parameter:
_, err = pool.Exec(ctx,
"INSERT INTO items (content, embedding) VALUES ($1, $2)",
content, pgvector.NewVector(embedding))Fallback: skip the dependency and encode the vector as a Postgres text literal yourself, cast with ::vector at the call site:
func toVectorLiteral(v []float32) string {
parts := make([]string, len(v))
for i, f := range v {
parts[i] = fmt.Sprintf("%g", f)
}
return "[" + strings.Join(parts, ",") + "]"
}_, err = pool.Exec(ctx,
"INSERT INTO items (content, embedding) VALUES ($1, $2::vector)",
content, toVectorLiteral(embedding))Distance operators
| Operator | Distance | Notes |
|---|---|---|
<-> | L2 (Euclidean) | default; good for normalized unit vectors |
<=> | cosine | use when vector magnitude is not meaningful |
<#> | inner product | multiply by -1 to get similarity |
Choose one and be consistent — the index operator class must match the query operator (see Index trade-off below).
k-nearest-neighbour query
SELECT id, content,
embedding <=> $1 AS distance
FROM items
ORDER BY distance
LIMIT $2type Result struct {
ID int64
Content string
Distance float64
}
func Search(ctx context.Context, db *pgxpool.Pool, embedding []float32, k int) ([]Result, error) {
rows, err := db.Query(ctx,
`SELECT id, content, embedding <=> $1 AS distance
FROM items
ORDER BY distance
LIMIT $2`,
pgvector.NewVector(embedding), k)
if err != nil {
return nil, fmt.Errorf("vector search: %w", err)
}
defer rows.Close()
var out []Result
for rows.Next() {
var r Result
if err := rows.Scan(&r.ID, &r.Content, &r.Distance); err != nil {
return nil, fmt.Errorf("scan: %w", err)
}
out = append(out, r)
}
return out, rows.Err()
}Apply a distance threshold in SQL or after retrieval according to the contract. Either can yield zero matches. Post-filtering a limited candidate set can yield fewer than k results:
const maxDistance = 0.35
var filtered []Result
for _, r := range results {
if r.Distance <= maxDistance {
filtered = append(filtered, r)
}
}Embedder interface
Define the embedder on the consumer side (use-case or repository package), not in the infra package:
type Embedder interface {
Embed(ctx context.Context, text string) ([]float32, error)
}Validate dimensionality at the infra boundary before returning:
if len(vec) != cfg.Dim {
return nil, fmt.Errorf("embed: got %d dims, want %d", len(vec), cfg.Dim)
}Wire any OpenAI-compatible provider (OpenAI, Azure, LM Studio, vLLM) behind this interface. Do not mark the API key as required if local keyless providers must be supported.
Index trade-off
| Exact scan | ivfflat | hnsw | |
|---|---|---|---|
| Index | none | CREATE INDEX … USING ivfflat | CREATE INDEX … USING hnsw |
| Build time | — | fast | slow (memory-intensive) |
| Query speed | O(n) full scan | fast | faster |
| Recall | exact | measure against exact search; tune lists/probes | measure against exact search; tune ef_search and index construction |
| When to add | meets measured latency | representative recall/latency justifies the index | representative recall/latency/memory justifies the index |
Start without an index. Exact scan is accurate and simple; add an index only after profiling shows it matters.
-- ivfflat: lists ≈ sqrt(row_count), probe at query time with SET ivfflat.probes
CREATE INDEX ON items USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);
-- hnsw: higher m = better recall, more memory
CREATE INDEX ON items USING hnsw (embedding vector_cosine_ops) WITH (m = 16, ef_construction = 64);The operator class (vector_cosine_ops, vector_l2_ops, vector_ip_ops) must match the distance operator used in queries.
Do not
- Skip
pgxvec.RegisterTypesinAfterConnectwhen using pgvector-go — without
it pgx has no codec for pgvector.Vector and the query fails to encode.
- Forget the
::vectorcast on the hand-rolled text-literal fallback — Postgres
can't infer the column type from a bare string parameter.
- Change the vector column dimension without re-embedding all rows and running
a migration.
- Assume a WHERE distance predicate categorically disables ANN indexing. Preserve an indexable distance ORDER BY and LIMIT; inspect EXPLAIN. For filtered ANN search, evaluate iterative scans and the documented materialized-CTE pattern for distance filters. See pgvector filtering.
- Mix operator classes between the index and query — results will be wrong
or the index unused.
Verify
# extension present
psql -c "SELECT extversion FROM pg_extension WHERE extname = 'vector';"
# column type and dimension
psql -c "\d+ items"
# inspect the actual query with a nonzero vector matching the column dimension
# compare ANN recall against exact search on representative data
timeout 5m go test -parallel 4 -p 2 -short -race -timeout 5m ./internal/repository/...
