Redis Vector Search lets you find similar items based on their meaning, not just keywords.
Here’s how it looks in action. Imagine you have a bunch of product descriptions and you want to find products similar to "a comfy red chair."
First, you need to embed your data. This means converting text (or images, or audio) into numerical vectors that capture their semantic meaning. Tools like OpenAI’s text-embedding-ada-002 or open-source models like all-MiniLM-L6-v2 do this.
# Example using a hypothetical embedding function
from redis import Redis
from redis.commands.search.query import Query
redis_client = Redis(host='localhost', port=6379, db=0)
def get_embedding(text):
# This would be your actual embedding model call
# For demonstration, returning a dummy vector
return [0.1] * 1536 # Assuming a 1536-dimensional vector
# Store the data with embeddings in Redis
# We'll use a RediSearch index for this
from redis.commands.search.field import VectorField, TagField, TextField
from redis.commands.search.indexDefinition import IndexDefinition, IndexType
# Define schema
schema = (
TextField("product_name"),
TextField("description"),
VectorField("embedding", "FLAT", {
"TYPE": "FLOAT32",
"DIM": 1536,
"DISTANCE_METRIC": "COSINE",
}),
)
# Create index if it doesn't exist
try:
redis_client.ft("idx:products").info()
except:
redis_client.ft("idx:products").create_index(
fields=schema,
definition=IndexDefinition(prefix=["product:"], index_type=IndexType.JSON)
)
# Add some sample data
products = [
{"id": "1", "product_name": "Plush Red Armchair", "description": "A very comfortable red armchair, perfect for reading."},
{"id": "2", "product_name": "Leather Office Chair", "description": "Ergonomic black leather chair for your workspace."},
{"id": "3", "product_name": "Cozy Blue Sofa", "description": "A soft and inviting blue sofa for your living room."},
{"id": "4", "product_name": "Modern Red Accent Chair", "description": "Stylish red chair with metal legs, a great addition to any room."},
]
for product in products:
product_id = product["id"]
embedding = get_embedding(product["description"])
redis_client.json().set(f"product:{product_id}", "$", {
"product_name": product["product_name"],
"description": product["description"],
"embedding": embedding
})
# Now, let's search for products similar to "a comfy red chair"
query_text = "a comfy red chair"
query_embedding = get_embedding(query_text)
# Construct the vector search query
# '$' is a placeholder for the query vector
# KNN is for k-Nearest Neighbors, 3 means find the 3 most similar
# "embedding" is the name of the vector field
# "COSINE" is the distance metric used in the index
vector_query = (
Query("*.json") # Search all JSON documents
.return_fields("product_name", "description")
.sort_by("__vector_score") # Sort by similarity score
.paging(0, 3) # Get top 3 results
.return_content()
.dialect(2) # Use dialect 2 for vector search
)
# Add the vector search part to the query
vector_query.add_query_params({
"vector_field": "embedding",
"query_params": {
"k": 3,
"vector": query_embedding,
"algorithm": "FLAT", # Matches the index algorithm
"distance_metric": "COSINE" # Matches the index metric
}
})
# Execute the search
results = redis_client.ft("idx:products").search(vector_query)
for doc in results.docs:
print(f"Name: {doc.product_name}, Score: {doc.vector_score}")
The core idea is that RediSearch, specifically its module for vector similarity search, allows you to index these high-dimensional vectors efficiently. It uses algorithms like FLAT (which does brute-force comparison, good for smaller datasets or when exactness is paramount) or HNSW (Hierarchical Navigable Small Worlds, an approximate nearest neighbor algorithm that’s much faster for large datasets). When you query, RediSearch compares your query vector against all indexed vectors (or a subset if using HNSW) using the specified distance metric (like COSINE similarity or L2 distance) and returns the closest matches.
The FLAT index type means it will perform a brute-force scan of all vectors in the index. The COSINE distance metric calculates the cosine of the angle between two vectors, effectively measuring their directional similarity. A score closer to 1 means higher similarity. The k=3 parameter tells RediSearch to return the 3 nearest neighbors.
The most surprising true thing about vector search is that the "meaning" captured by embeddings isn’t explicit; it emerges from the training data of the embedding model. A model trained on a massive text corpus learns to place semantically similar words and phrases closer together in its high-dimensional vector space, even if they don’t share keywords.
The __vector_score field is a special field automatically added by RediSearch when performing vector searches. It represents the similarity score between the query vector and the document’s vector, based on the DISTANCE_METRIC defined in the index. Lower scores generally indicate higher similarity for metrics like L2, while higher scores (closer to 1.0) indicate higher similarity for COSINE. Sorting by this field is how you surface the most semantically relevant results.
The next concept to explore is optimizing vector search performance for massive datasets, which usually involves moving from FLAT indexing to HNSW and understanding the trade-offs between accuracy and speed.