RAG with Redis and Beanis: Build Vector Search in ~50 Lines (No Separate Vector DB)

TL;DR

Beanis is a Redis ODM (object-document mapper) with built-in vector search, so if you already run Redis you can build RAG (retrieval-augmented generation) without a dedicated vector database. You define a Pydantic-style model with a VectorField, Beanis creates the HNSW index automatically, and similarity search is one call.

  • What it replaces: a separate vector service (Pinecone, Weaviate, pgvector) for small-to-mid corpora (roughly 10K to 1M documents).
  • Setup: redis-stack (includes the RediSearch module) plus pip install beanis transformers redis.
  • Embeddings: open-source jina-embeddings-v4, runs locally, no API key.
  • Code size: about 50 lines total for ingest and search.
  • Docs: Beanis Documentation
  • Example: Simple RAG
# Define a model, ingest, and search
doc = KnowledgeBase(text=text, embedding=embedding)
await doc.insert()

results = await IndexManager.find_by_vector_similarity(
    redis_client, KnowledgeBase, "embedding", query_embedding, k=5
)

The problem

You are building an AI app that needs semantic search for RAG. The common advice is to reach for Pinecone ($70/month minimum, plus 100+ lines of boilerplate), Weaviate (another service to deploy and monitor), or pgvector (slower queries and index tuning).

Meanwhile you already run Redis for caching, session storage, and job queues. What most people miss is that Redis is also a vector database. If you are already running it, the vector search capability is there whether you use it or not.

The solution: Beanis on Redis

Beanis is a Redis ODM with built-in vector search. The whole RAG system is short:

# models.py (14 lines)
from beanis import Document, VectorField
from typing import List
from typing_extensions import Annotated

class KnowledgeBase(Document):
    text: str
    embedding: Annotated[List[float], VectorField(dimensions=1024)]

    class Settings:
        name = "knowledge"

# ingest.py (20 lines)
from transformers import AutoModel

model = AutoModel.from_pretrained('jinaai/jina-embeddings-v4')

async def ingest_text(text: str):
    embedding = model.encode([text])[0].tolist()
    doc = KnowledgeBase(text=text, embedding=embedding)
    await doc.insert()

# search.py (15 lines)
from beanis.odm.indexes import IndexManager

async def search(query: str):
    query_emb = model.encode([query])[0].tolist()

    results = await IndexManager.find_by_vector_similarity(
        redis_client, KnowledgeBase, "embedding", query_emb, k=5
    )

    return [await KnowledgeBase.get(doc_id) for doc_id, score in results]

That is the entire RAG system.

Why this approach works

Vector indexes are created automatically when you call init_beanis(). No manual Redis commands, no setup scripts. Define your model with VectorField() and Beanis handles the index.

Beanis + Redis vs Pinecone

Pinecone is a good product, but it solves a problem you may not have. It makes sense for massive-scale search across billions of documents, global replication, and managed infrastructure with SLAs. If that is you, and you have the budget, use Pinecone.

Most apps do not need that. You have maybe 10K to 1M documents, you already run Redis, and you would rather not add a monthly bill. Here is what changes with Beanis + Redis:

Dimension Beanis + Redis Pinecone
Setup docker run, no API keys Account, API keys
Code about 50 lines 100+ lines
Cost $0 if Redis is already running from $70/month
Query latency 15ms (local) 40ms (network)
Operations one service one more service

The trade-off is self-hosting. If managed infrastructure matters to you, stay on Pinecone. If you already run Redis and do not mind operating it, this is simpler and cheaper.

The code comparison

Pinecone:

# Setup
import pinecone
pinecone.init(api_key=os.getenv("PINECONE_API_KEY"), environment="us-west1-gcp")
index = pinecone.Index("my-index")

# Upsert (complex)
vectors = [(str(i), embedding, {"text": text}) for i, (text, embedding) in enumerate(docs)]
index.upsert(vectors=vectors, namespace="docs")

# Search (multiple steps)
query_response = index.query(
    vector=query_embedding,
    top_k=5,
    namespace="docs",
    include_metadata=True
)
results = [match['metadata']['text'] for match in query_response['matches']]

# ~100+ lines for production setup

Beanis:

# Setup
doc = KnowledgeBase(text=text, embedding=embedding)
await doc.insert()

# Search
results = await IndexManager.find_by_vector_similarity(
    redis_client, KnowledgeBase, "embedding", query_embedding, k=5
)

# ~50 lines total

Step-by-step tutorial

1. Install dependencies

pip install beanis transformers redis

Three packages, no account creation.

2. Start Redis

docker run -d -p 6379:6379 redis/redis-stack:latest

Use redis-stack. It includes the RediSearch module needed for vector search.

3. Define your model

from beanis import Document, VectorField
from typing import List
from typing_extensions import Annotated

class KnowledgeBase(Document):
    text: str
    embedding: Annotated[List[float], VectorField(dimensions=1024)]

    class Settings:
        name = "knowledge"

14 lines for the whole data model. VectorField() tells Beanis to create a vector index using the HNSW algorithm for fast similarity search.

4. Ingest documents

Vector indexes are created automatically, so there is no manual setup step.

from transformers import AutoModel
import redis.asyncio as redis
from beanis import init_beanis

# Load open-source embedding model (no API key)
model = AutoModel.from_pretrained('jinaai/jina-embeddings-v4', trust_remote_code=True)  # https://huggingface.co/jinaai/jina-embeddings-v4

async def ingest_text(text: str):
    # Generate embedding
    embedding = model.encode([text])[0].tolist()

    # Store in Redis
    doc = KnowledgeBase(text=text, embedding=embedding)
    await doc.insert()

    print(f"✓ Indexed: {text[:50]}...")

# Initialize
redis_client = redis.Redis(decode_responses=True)
await init_beanis(database=redis_client, document_models=[KnowledgeBase])

# Ingest your documents
texts = ["Redis is fast", "Python is great", "Beanis is simple"]
for text in texts:
    await ingest_text(text)

20 lines. Documents are now searchable and the vector index was built for you.

5. Search semantically

from beanis.odm.indexes import IndexManager

async def search(query: str, k: int = 5):
    # Embed query
    query_embedding = model.encode([query])[0].tolist()

    # Search
    results = await IndexManager.find_by_vector_similarity(
        redis_client=redis_client,
        document_class=KnowledgeBase,
        field_name="embedding",
        query_vector=query_embedding,
        k=k
    )

    # Get documents
    docs = []
    for doc_id, similarity_score in results:
        doc = await KnowledgeBase.get(doc_id)
        docs.append((doc.text, similarity_score))

    return docs

# Search
results = await search("what is semantic search?")
for text, score in results:
    print(f"{score:.3f}: {text}")

15 lines, semantic search working.

Why semantic search beats keywords

Say you are building documentation search and a user asks “how to cancel my subscription?” while the docs use the phrase “termination policy”. Keyword search returns nothing. Vector search matches on meaning, so it surfaces:

  • “Account termination policy”
  • “How to close your account”
  • “Subscription cancellation process”

Vector embeddings encode meaning, not exact tokens, so related phrasing still matches.

Performance comparison

I benchmarked this against the usual options with 10,000 documents. These are measured numbers, not marketing figures.

System Query latency Setup code Setup Incremental cost
Beanis + Redis 15ms ~50 lines docker run $0
Pinecone 40ms 100+ lines API key $70+/month
Weaviate 35ms 80+ lines deploy a service self-hosting
pgvector 200ms 60+ lines index tuning included with Postgres

Beanis wins on speed (local Redis beats API round trips) and on code size (the ODM pattern removes boilerplate). What you give up is managed infrastructure. If that matters, Pinecone is worth the cost.

The “already using Redis” advantage

Most modern web apps already run Redis for caching, sessions, job queues, and rate limiting. Vector search joins that list on the same service, with the same monitoring and the same deployment pipeline.

Before:

  • Redis (caching and sessions)
  • PostgreSQL (user data)
  • Pinecone (vectors, $70+/month)
  • Your app

After:

  • Redis (caching, sessions, and vectors)
  • PostgreSQL (user data)
  • Your app

One fewer service to monitor, deploy, and pay for. Vectors sit next to your cache, so queries benefit from data locality, and there is one less system to check when something breaks.

Once the basics work, a few extensions are worth knowing.

Multimodal search. Jina v4 embeds text and images into the same vector space, so you can search images with text queries or find text from an image. It is the same model.encode() API, plus encode_image() for images:

from PIL import Image

# Search with text
text_emb = model.encode(["red sports car"])[0].tolist()
results = await IndexManager.find_by_vector_similarity(...)

# Search with image
img = Image.open("car.jpg")
img_emb = model.encode_image([img])[0].tolist()
results = await IndexManager.find_by_vector_similarity(...)

Both queries run against the same index.

Hybrid search. Combine vector similarity with traditional filters. Add indexed fields to your model and filter before or after the vector search:

class KnowledgeBase(Document):
    text: str
    embedding: Annotated[List[float], VectorField(dimensions=1024)]
    category: Indexed(str)  # Filter by category
    date: datetime
    language: Indexed(str)  # Filter by language

This supports queries like “similar documents, but only in English” or “semantic search within the documentation category”.

Production tuning. Redis Cluster handles sharding automatically, and you can tune HNSW parameters for your recall/speed trade-off:

VectorField(
    dimensions=1024,
    algorithm="HNSW",
    m=32,  # More connections = better recall, more memory
    ef_construction=400  # Higher = better index quality, slower indexing
)

Start with the defaults and only tune if you see problems.

Complete working example

Clone and run:

git clone https://github.com/andreim14/beanis-examples.git
cd beanis-examples/simple-rag

# Install
pip install -r requirements.txt

# Start Redis
docker run -d -p 6379:6379 redis/redis-stack:latest

# Ingest sample docs (vector indexes created automatically)
python ingest.py

# Search
python search.py "what is semantic search?"

FAQ

What is Beanis?

Beanis is a Redis ODM (object-document mapper) with built-in vector search. You define documents like Pydantic models, and it handles serialization, indexing, and vector similarity queries on Redis.

Do I need RediSearch?

Yes. Use redis-stack (which includes the RediSearch module) or install RediSearch manually. Plain Redis does not have vector search.

Can I use OpenAI embeddings instead of Jina?

Yes. Swap the embedding source and keep the rest of the pipeline:

import openai
embedding = openai.Embedding.create(input=text, model="text-embedding-3-small")

The jina-embeddings-v4 model is free, runs locally, and needs no API key, which is why the tutorial uses it.

How much data can Redis handle for vectors?

Redis can hold millions of vectors on a single node, and billions with sharding via Redis Cluster. Memory is roughly 4KB per document for 1024-dimension vectors, so about 4GB of RAM for 1M documents.

How do updates and deletes work?

Edit the document and save, or delete it. Indexes update automatically:

# Update
doc = await KnowledgeBase.get(doc_id)
doc.text = "Updated text"
doc.embedding = new_embedding
await doc.save()

# Delete
await doc.delete()

When should I still use a dedicated vector database?

When you need billions of documents, global replication, or fully managed infrastructure with SLAs. For roughly 10K to 1M documents on infrastructure you already run, Beanis + Redis is simpler and cheaper.

Is Beanis tied to a specific database?

No. It runs on Redis, so there is no proprietary lock-in. Your data and index live in a service you can host anywhere.

Resources




Enjoy Reading This Article?

Here are some more articles you might like to read next:

  • Stateful AI Agents with LangGraph and Beanis: RAG with Persistent Memory on Redis
  • Concept-pedia: A Multimodal Concept Dataset and Benchmark Beyond ImageNet (EMNLP 2025)
  • Redis Geo-Spatial Cache: Build a Restaurant Finder with Beanis and PostgreSQL
  • Word Sense Linking: Word Sense Disambiguation on Real Text (ACL 2024)
  • Beanis: A Typed Redis ODM for Python with Pydantic v2