Getting Started
This guide walks you through the core RedisVL workflow: defining a schema, creating a search index, loading documents with vector embeddings, and running your first vector similarity query.
Prerequisites
-
Go 1.23 or later
-
A running Redis instance with the Query Engine (Redis 8 has it built in)
Installation
Install RedisVL into your Go module:
go get github.com/redis/redis-vl-golang@v0.2.0
The quickest way to run Redis locally is Docker:
docker run -d -p 6379:6379 redis:8.8.0
| Redis Cloud offers a free tier if you prefer a managed database, and the free Redis Insight GUI is a great companion for inspecting your indexed data. |
Define a schema
A schema describes the index: its name, key prefix, storage type (hash or JSON), and typed fields. You can build it programmatically or load it from YAML — the YAML format is identical to the Python library’s.
Programmatically
import "github.com/redis/redis-vl-golang/schema"
embedding, err := schema.NewVectorField("embedding", schema.VectorAttrs{
Algorithm: schema.Flat, // or schema.HNSW, schema.SVSVamana
Dims: 4,
DistanceMetric: schema.Cosine,
Datatype: "float32",
})
if err != nil {
log.Fatal(err)
}
s, err := schema.NewIndexSchema(
schema.IndexInfo{
Name: "user-idx",
Prefixes: []string{"user"},
StorageType: schema.Hash,
},
schema.NewTagField("user"),
schema.NewTagField("credit_score"),
schema.NewTextField("job_title", schema.TextAttrs{
BaseAttrs: schema.BaseAttrs{Sortable: true},
}),
schema.NewNumericField("age", schema.NumericAttrs{
BaseAttrs: schema.BaseAttrs{Sortable: true},
}),
embedding,
)
NewTextField, NewTagField, NewNumericField, and NewGeoField take optional attribute structs; NewVectorField requires VectorAttrs with at least Dims and Algorithm, and validates them.
From YAML
version: '0.1.0'
index:
name: user-idx
prefix: user
key_separator: ':'
storage_type: hash
fields:
- name: user
type: tag
- name: credit_score
type: tag
- name: job_title
type: text
attrs:
sortable: true
- name: embedding
type: vector
attrs:
algorithm: flat
dims: 4
distance_metric: cosine
datatype: float32
s, err := schema.FromYAMLFile("schemas/schema.yaml")
schema.FromYAML parses YAML bytes directly, and s.ToYAML() / s.ToYAMLFile(path, overwrite) serialize a schema back out.
Create a SearchIndex
SearchIndex performs all admin, load, and search operations. Every method takes a context.Context, so one type covers both the sync and async use cases of the Python library.
import redisvl "github.com/redis/redis-vl-golang"
index, err := redisvl.NewSearchIndexFromURL(s, "redis://localhost:6379")
if err != nil {
log.Fatal(err)
}
defer index.Close()
// Create the index in Redis
err = index.Create(ctx)
You can also bind to an existing go-redis client — useful when your application already manages a connection pool:
client := redis.NewClient(&redis.Options{Addr: "localhost:6379"})
index := redisvl.NewSearchIndex(s, client)
Create accepts options for recreating an index:
// Overwrite the index definition; Drop also deletes the underlying documents
err = index.Create(ctx, redisvl.CreateOptions{Overwrite: true, Drop: false})
Without Overwrite, Create is a no-op if an index with the same name already exists.
|
Load data
Documents are plain []map[string]any. For hash storage, vector values must be encoded as little-endian byte buffers — use vectors.ToBuffer (or vectors.ToBuffer32 for []float32):
import "github.com/redis/redis-vl-golang/vectors"
emb1, _ := vectors.ToBuffer([]float64{0.1, 0.1, 0.5, 0.9}, vectors.Float32)
emb2, _ := vectors.ToBuffer([]float64{0.7, 0.1, 0.5, 0.3}, vectors.Float32)
data := []map[string]any{
{"user": "john", "credit_score": "high", "job_title": "engineer", "age": 34, "embedding": emb1},
{"user": "mary", "credit_score": "low", "job_title": "doctor", "age": 52, "embedding": emb2},
}
keys, err := index.Load(ctx, data, redisvl.LoadOptions{IDField: "user"})
// keys: ["user:john", "user:mary"]
LoadOptions controls how documents are written:
| Option | Description |
|---|---|
|
Document field whose value becomes the key id; a ULID is generated when empty. |
|
Explicit full Redis keys, one per document (overrides |
|
Expiration applied to each written key. |
|
Pipeline batch size (default 200). |
|
Function applied to each document before writing. |
|
Check records against the schema before writing (off by default). |
Fetch documents back by id with index.Fetch(ctx, "john") or in bulk with index.FetchMany(ctx, "john", "mary").
| For JSON storage, vectors are stored as plain float slices (JSON arrays) — no byte encoding needed. |
Run a vector query
import "github.com/redis/redis-vl-golang/query"
q := query.NewVectorQuery("embedding", []float64{0.1, 0.1, 0.5, 0.1}).
NumResults(3).
ReturnFields("user", "credit_score", "job_title")
results, err := index.Query(ctx, q)
for _, doc := range results {
fmt.Println(doc["user"], doc["vector_distance"])
}
Each result is a map[string]any containing the requested fields plus vector_distance. See Querying with RedisVL for the full range of query types and Filter Expressions for metadata filtering.
Inspect and clean up
Info returns the FT.INFO attributes of the index:
info, err := index.Info(ctx)
fmt.Println(info["num_docs"], info["percent_indexed"])
When you are done, delete the index. Pass true to also drop the indexed documents:
err = index.Delete(ctx, true)
If the index does not exist, operations return an error you can test with errors.Is(err, redisvl.ErrIndexNotFound) — nothing panics.
Next steps
-
Querying with RedisVL — all query types, from KNN to hybrid search
-
Filter Expressions — the fluent metadata filter DSL
-
Vectorizers — generate embeddings with hosted APIs or local models