Skip to main content

SereneDB Team

Sep 3, 2026 · 17 minutes read

SereneDB meets LangChain: A Vector Store for RAG Pipelines

SereneDB is now available as a VectorStore for LangChain applications

SereneDB has built a VectorStore integration package for LangChain, pairing flexible AI orchestration with the analytical power of a full-featured database. Three wins for your RAG pipeline:

  • Unlimited analytical power — native hybrid search, metadata filtering, and relational queries let your agents retrieve on any shape and complexity of data, far beyond simple vector matching.
  • Single engine - SereneDB is a full database, not just a vector store. All the surrounding data can stay in the same instance. No additional engines to maintain.
  • Rapid development — the package handles all the store specifics, so you can focus on the actual application.

RAG in the modern world

Large language models are remarkable generalists with two well-known blind spots: their knowledge stops at a training cutoff, and they know nothing about your data — internal docs, tickets, codebases, product catalogs. Retrieval-Augmented Generation (RAG) is the pattern that closes both gaps. Instead of hoping the model memorized the answer, a RAG application first retrieves the most relevant pieces of your own data and hands them to the model as context, so the answer is grounded in real, current, private sources — with far fewer hallucinations and even citations to back it up.

In a few short years RAG has gone from a research idea to the default architecture for applied LLM systems: chat-with-your-docs assistants, support bots, enterprise search, code assistants — nearly every "LLM + company data" product is a RAG pipeline at its core. The mechanics have matured along the way. Documents are split into chunks and embedded into vectors, similarity search finds candidates at query time, and modern pipelines layer on hybrid keyword + semantic retrieval, metadata filtering, and re-ranking to keep quality high at scale. Which means every RAG system stands on two practical pillars: a framework to orchestrate the pipeline, and a database that can store and search vectors efficiently.

Today we will talk about one of the most powerful RAG frameworks — LangChain — and how SereneDB slots into it as the vector store.

A little about LangChain

LangChain is an open-source framework for building applications on top of large language models. Instead of wiring raw API calls together by hand, developers compose applications from standard building blocks: a model call, a prompt template, a retriever, a memory layer. The framework's real value is in its abstractions — every provider-specific service (an LLM, an embedding API, a database) is hidden behind a common interface, so an application written against LangChain can swap OpenAI for Anthropic, or one database for another, without rewriting the surrounding Logic.

For making a RAG pipeline with LangChain one usually uses the following blocks: loader → splitter → embeddings → vector store at ingestion time, and query → embeddings → vector store (as retriever) → chat model at answer time. And here is where SereneDB appears.

SereneDB in the pipeline: the vector store

langchain-serenedb plugs SereneDB into that pipeline as the VectorStore implementation. So it fits natively into the LangChain pipeline as shown.

SereneDB in the pipeline: the vector storeSereneDB in the pipeline: the vector store

The integration package maps LangChain's vector store contract onto SereneDB's native capabilities: vector columns, distance operators, the IVF ANN index, BM25 full-text search, and JSON metadata.

The key point: You don't need to know SQL. Although SereneDB is a full SQL database underneath, the integration handles the entire database side internally — creating tables, declaring indexes with the right options, composing search queries, and translating filters. You work with Python objects and LangChain's standard vector store API; the package generates correct, index-aware SQL behind the scenes. Someone building a RAG application never needs to learn SereneDB's DDL or SQL query syntax to get full use of its engine — they can stay focused on the real deal: their documents, their retrieval quality, their application.

Let’s see what SereneDB brings to your RAG pipeline.

Vectors first

Vector similarity search is the first thing you need when adding VectorStore to the pipeline. The integration stores your embeddings and queries them through SereneDB's IVF (inverted-file) ANN index for fast approximate nearest-neighbor search at scale. You choose the distance that fits your embeddings — cosine, euclidean, inner product, or manhattan — and the package keeps the index and every query aligned on it. Every standard LangChain entry point — similarity_search, similarity_search_with_score, MMR, as_retriever() — is served by that IVF index, so the whole vector-store surface is index-accelerated, not just a special "fast path."

What about metadata?

Vector search alone gives you the semantically closest chunks, but "closest" isn't always "relevant" — you often want the nearest neighbors among last quarter's tickets, from this author, in the pricing category. In the real world documents carry that structure by metadata — categories, timestamps, authors, tags. And constraining the similarity search by it is what turns a decent result set into the right one. The store gives you a rich filter language rather than bare equality: comparisons and ranges, set membership, existence and text patterns. Every filter compiles to SQL predicates evaluated inside the engine, right beside the vector scan, never as a slow post-filter in Python. Metadata can sit in a zero-ceremony JSON column or be promoted to typed columns when you know the schema — so filters ride an index scan instead of a full table scan.

Content is not just payload

So far the content has been along for the ride — embedded into a vector, filtered by its metadata, then handed back on a hit. But the text itself is a search signal too, and SereneDB can search in it directly. Turn on hybrid search and the store builds a combined index that fuses semantic vector similarity with SereneDB's native BM25 full-text ranking in a single query, so exact terms that embeddings miss — product codes, function names, rare keywords — still surface, combined by the fusion strategy you choose. And because the content column is inverted-indexed, the same machinery lets you bring full-text operators too.

You own your data

SereneDB is self-hostable — and for RAG that is more than an ops preference. The documents you index are usually exactly the data you can't send to a third party: internal knowledge bases, contracts, source code, customer records. Run SereneDB on your own infrastructure and the entire retrieval side of the pipeline — documents, embeddings, queries — stays inside your perimeter. Your data stays yours.

Not just VectorStore

VectorStore is only one corner of the system. SereneDB is a full database rather than a dedicated vector engine, so the same instance that serves your embeddings also serves everything around them: the relational tables your application already needs, analytical queries over your document corpus, full-text search on its own, JSON Storage — one connection string, one operational surface, standard PostgreSQL tooling. Your chunks can live next to the users, sessions, and business data they belong to. While this part is not covered by this LangChain integration package - you can use SQL to access the data and run additional tasks on it. The SereneDB documentation covers the full feature set well beyond what this integration touches.

And what matters the most is that all features are in one engine: IVF-accelerated vector similarity, BM25 full-text ranking fused with it in a single hybrid query, and inverted-index-backed metadata filtering that runs next to the vector search instead of after it. You get the retrieval quality tricks of a modern pipeline — semantic recall, exact-term matching, precise filtering — without stitching together a vector engine, a search engine, and a filter layer from three different products.

Getting started

Now it is time to see SereneDB in action. As a tradition - a small HelloWorld example. You will need at least python 3.10 to run the example.

First of all we get a fresh SereneDB instance running. In this example we will use convenient script that will handle the details of creating a docker container:

curl https://install.serenedb.com | sh

Alternatively you can use other means for creating the instance - look at our QuickStart guide

Now the langchain-serenedb integration package

pip install langchain_serenedb

For the purposes of this example we will use langchain core provided fake embedder. But it is trivial to replace it with any of the embedder integrations supported by LangChain.

from langchain_serenedb import SereneDBEngine, SereneDBVectorStore, IVFIndex
from langchain_core.embeddings import DeterministicFakeEmbedding
from langchain_core.documents import Document

# This is the embedder. Replace it with the real one.
my_embeddings = DeterministicFakeEmbedding(size=768)

# Connect in a postgres style
engine = SereneDBEngine.from_connection_string(
"host=127.0.0.1 port=7890 user=postgres dbname=postgres"
)

# Create the table and its IVF ANN index in one call.
# All internal SQL is generated automatically.
engine.init_vectorstore_table("my_docs", vector_size=768, vector_index=IVFIndex())

# Spin up the VectorStore
store = SereneDBVectorStore.create_sync(
engine, embedding_service=my_embeddings, table_name="my_docs"
)

# A tiny corpus to index (page content + optional metadata)
docs = [
Document(page_content="SereneDB speaks the PostgreSQL wire protocol.", metadata={"topic": "intro"}),
Document(page_content="The IVF index accelerates nearest-neighbor search.", metadata={"topic": "index"}),
Document(page_content="Hybrid search fuses BM25 keyword ranking with vectors.", metadata={"topic": "search"}),
Document(page_content="Metadata filters run inside the engine, next to the vectors.", metadata={"topic": "filter"}),
Document(page_content="Set sdb_nprobe to trade recall for latency per query.", metadata={"topic": "tuning"}),
]

# Add some document to the index
store.add_documents(docs)

# And run your search!
results = store.similarity_search("how do I configure the index?")

From here it behaves like any LangChain vector store — similarity_search, similarity_search_with_score, max_marginal_relevance_search, get_by_ids, delete, and as_retriever() for dropping it into a chain. Of course this is only the basic setup, we will cover more advanced features in the following chapter.

Advanced Topics

Here we provide a short list of most usable features in detail. And dive into some internal machinery that powers our VectoreStore.

Features walkthrough

Accelerated vector search with IVF. SereneDB's ANN index is an IVF (inverted file) index, and the integration exposes it as a plain Python object: IVFIndex. Declare one and the package derives the distance metric from your chosen DistanceStrategy (Euclidean/L2, cosine, inner product) and guarantees the index and every query it issues agree on it — a consistency detail that trips people up when managing ANN indexes by hand, handled here automatically. Optional quantization trades a little recall for a lot of memory and speed: sq8, sq4, pq (with configurable sub-quantizers), or rabitq (1–9 bits) — each just a constructor argument. For bulk loads, creating the index after loading lets SereneDB train better IVF clusters; for incremental workloads, creating it with the table (init_vectorstore_table(..., vector_index=IVFIndex())) means search is accelerated from the first row. apply_vector_index(), reindex(), and drop_vector_index() manage the index on a live store — no DDL statements to write or migrations to maintain.

Tunable query-time recall. IVFQueryOptions controls the recall/latency trade-off per query: nprobe sets how many IVF cluster lists are scanned (applied as the sdb_nprobe session setting), and rerank_factor sizes the exact-distance rerank pool when a quantized index is in play.

Hybrid search: BM25 + vectors in one query. Semantic search is great at paraphrase but can miss exact terms — product codes, function names, rare keywords. Build the combined full-text + vector index with a HybridIndexConfig, give the store a HybridSearchConfig, and it runs a single query that fuses SereneDB's native BM25 ranking with vector distance. Three FusionStrategy options are available:

  • RRF (Reciprocal Rank Fusion, the default) — combines ranks rather than scores (sum(1 / (rrf_k + rank))), so BM25 scores and vector distances fuse without any normalization.
  • Weighted — min-max normalizes each branch (inverting distance so nearer = higher) and takes a weighted sum, preserving score magnitudes.
  • Weighted sum — a plain weighted sum of the raw branch scores. See more detailed description with examples at our documentation page

Metadata filtering. Searches accept a MongoDB-style filter dictionary — $eq, $ne, $lt/$lte/$gt/$gte, $in/$nin, $between, $exists, $like/$ilike, composed with $and/$or/$not. On inverted-indexed columns you also get full-text operators — $startswith, $regex, $fuzzy, $ngram, $match, $phrase — for prefix, regex, typo-tolerant and phrase matching. The package translates the dictionary into database predicates for you, so filtering runs inside the engine, right next to the vector search — no post-filtering in Python, and no query syntax to learn. Flexible metadata storage — and indexes for it. Metadata can live in a single JSON column (zero schema ceremony) or be promoted to explicit typed columns via Column/ColumnDict when you know your schema. Either way it can be indexed for fast filtering: MetadataColumnIndex for dedicated columns and JsonFieldIndex for individual fields inside the JSON blob, bundled through MetadataIndexConfig. Indexing a field inside a JSON document correctly has subtle expression-matching rules at the database level; the package encodes them so the index is always actually used — another piece of database expertise you don't have to bring yourself.

MMR retrieval. max_marginal_relevance_search is supported for result diversification — useful when the top-k nearest neighbors are near-duplicates and the chain benefits from broader coverage.

Sync and async, one behavior. Every operation exists in both flavors — add_documents/aadd_documents, similarity_search/asimilarity_search, and so on — with the sync store delegating to the same async core, so behavior never drifts between the two.

How it works

Under LangChain's tidy add_documents / similarity_search surface, the package maps a collection onto a deliberately simple SereneDB layout: one table and one inverted index. Its real job is to keep the DDL it writes and every query it later issues in lockstep — same distance metric, same extraction expressions, same dictionary — so searches actually hit the index instead of quietly degrading into a full scan.

The table. init_vectorstore_table creates a single table per collection: an id primary key, a content TEXT column, the embedding as a fixed-size FLOAT[N] array, and your metadata. Metadata has two homes, and you can mix them: a catch-all JSON column (the default — no schema to declare) or explicit typed columns promoted with Column when you know a field's shape and want to index or constrain it.

from langchain_serenedb import Column

engine.init_vectorstore_table(
"my_docs", vector_size=768,
metadata_columns=[
Column("category", "TEXT", nullable=False),
Column("year", "INTEGER"),
],
)

Anything you don't promote still round-trips through the JSON column, so promoting a field is an optimization, never a requirement.

One index, several columns. SereneDB's inverted index is not vector-only: a single USING inverted (...) can carry the embedding column (with the ivf operator class for ANN), the content column (analyzed for BM25), and any number of metadata columns and JSON sub-fields — all at once. The package builds exactly one such index per collection and routes every search through it. That is the whole reason a hybrid, metadata-filtered query can be one SQL statement: the vector ranking, the keyword ranking, and the filter predicates are all served by the same index. And it is all powered by our own IResearch library that was started in 2016, and we have never stopped improving it. Check our benchmarks against other search engines in Search Benchmark Game to see the actual performance results.

The full-text dictionary. Full-text scoring needs a text-search dictionary — the analyzer that turns raw content into scored tokens. The configuration splits cleanly by concern: a build-time HybridIndexConfig describes the dictionary (used when the index is created — the package issues a CREATE TEXT SEARCH DICTIONARY and references it on the content column), while a query-time HybridSearchConfig carries the fusion knobs (used on every search). The default dictionary is deliberately universal: a segmentation template, lower-cased, with frequency, position, and norm enabled — each one unlocking a capability, frequencies for BM25 scoring, positions for phrase and n-gram matching, norms for the language-model scorers. The dictionary is created in the table's own schema so an index living outside public can still resolve it — a subtlety the package handles so you never meet it. Both sides are overridable:

from langchain_serenedb import HybridIndexConfig, HybridSearchConfig, FusionStrategy

# build-time: how the content column is analyzed
index_cfg = HybridIndexConfig(
dictionary_options="template = 'segmentation', case = 'lower', "
"frequency = true, position = true, norm = true",
)
# query-time: how the lexical and vector rankings fuse
search_cfg = HybridSearchConfig(fusion=FusionStrategy.RRF, scorer="BM25")

Deciding what to index. By default the package indexes every declared metadata column verbatim — one token per value — which is exactly what lets a plain =, IN, or range filter be answered from the index scan instead of a row-by-row recheck. There is a safety net: in this index-everything mode a column whose type the inverted index cannot take verbatim (a NUMERIC, a UUID, an INTERVAL) is quietly skipped, so the automatic index can never fail to build. When you want control, MetadataIndexConfig states precisely what joins the index — a subset of columns, a JSON sub-field, or a column analyzed for full-text.

from langchain_serenedb import (
MetadataIndexConfig, MetadataColumnIndex, JsonFieldIndex,
)

metadata_index = MetadataIndexConfig(
columns=[
MetadataColumnIndex("category"), # verbatim → =, IN, range
MetadataColumnIndex("title", dictionary="langchain_fts_dict"), # full-text
],
json_fields=[JsonFieldIndex("attrs.brand", "TEXT")], # a field inside the JSON
)

Here is a schematical data flow inside SereneDB database:

Data flow inside SereneDB databaseData flow inside SereneDB database

Two choices there carry real database expertise. Attaching a dictionary to a column flips it from verbatim to full-text-analyzed: you gain the $regex / $phrase / $fuzzy operators on it but give up plain-equality pushdown (analyzed columns match through @@, not =). And indexing a field inside the JSON blob only works if the index expression and the query expression are byte-identical — same ->> arrow, same ::type cast on both sides — or the filter pushes down but returns the wrong rows. The package generates both sides from one definition so they cannot drift.

Why can't the metric drift? The same principle governs the vector side. An IVF index only accelerates a query whose distance operator matches the metric the index was built with; let them fall out of step and the search silently reverts to a full scan. The package derives both the index metric and the query operator from a single DistanceStrategy, so the guarantee holds by construction — the recurring theme of the whole integration: you declare intent in Python, and the package keeps the generated SQL self-consistent.

Recap

By bringing vector similarity, BM25 full-text ranking, and metadata filtering together into a single, SQL-backed engine, SereneDB is a good fit for the architecture of modern LangChain RAG pipelines. Instead of managing multiple disparate systems, you get a cohesive retrieval layer that handles complex data shapes with index-accelerated performance. We invite you to try the langchain-serenedb package or dive into our documentation to see how SereneDB can streamline your own AI applications.

Additional links:

Interested in our product?

Join our community!

Questions, benchmarks and release chatter happen in the open.