Skip to main content

LangChain VectorStore for MySQL 9's native VECTOR type — works with ShannonBase, self-hosted MySQL, and MySQL HeatWave.

Project description

langchain-shannonbase

PyPI CI Python License: MIT

A LangChain VectorStore backed by MySQL 9's native VECTOR type. If your data already lives in MySQL, you can do retrieval without bolting a separate vector database onto your stack.

It works against three things that share the same VECTOR / STRING_TO_VECTOR / DISTANCE surface:

Backend What it is Good for
ShannonBase open-source "MySQL for AI" local dev and self-hosting, no subscription
MySQL 9 vanilla self-hosted MySQL you already run MySQL
MySQL HeatWave Oracle's managed MySQL production on OCI

Why it exists

Before this, if your data was in MySQL your LangChain options were thin. The one MySQL vector store in the ecosystem is locked to Google Cloud SQL, and ShannonBase's LangChain integration was on their wishlist but nobody had built it. This is the plain, self-hostable version: no cloud lock-in, no extra service to run.

It passes LangChain's standard vector-store integration suite, so it behaves like any other store you'd drop into a chain.

When to use this (and when not to)

ShannonBase can also do the whole retrieve-and-generate loop in one SQL call with sys.ML_RAG. If you're all-in on ShannonBase and happy in SQL, that's the simpler path and you probably don't need this.

Reach for this package when you're already building in LangChain: you want orchestration in Python, your own embeddings (OpenAI, a local model, whatever), or to plug MySQL into a chain or agent you've already got. Same engine underneath, different front door.

Install

pip install "langchain-shannonbase[mysql]"

The [mysql] extra pulls in the database driver. Leave it off if you only want the offline in-memory backend for tests.

Quickstart

from langchain_openai import OpenAIEmbeddings
from langchain_shannonbase import ShannonBaseVectorStore

store = ShannonBaseVectorStore(
    embedding=OpenAIEmbeddings(model="text-embedding-3-small"),
    table="documents",
    host="127.0.0.1", port=3306, user="root", password="", database="rag",
)

store.add_texts(
    ["Refunds are accepted within 30 days.", "Free shipping over $50."],
    metadatas=[{"topic": "refunds"}, {"topic": "shipping"}],
    ids=["1", "2"],
)

store.similarity_search("what's the return policy?", k=2)

The table is created on the first write, with an embedding VECTOR(n) column sized to your embedding model.

For a full doc-in to grounded-answer example, see examples/rag.py.

Filtering, MMR, scores, retriever

# filter by metadata: equality, membership ($in/$nin), or comparison ($gt/$gte/$lt/$lte/$ne)
store.similarity_search("policy?", k=2, filter={"topic": "refunds"})
store.similarity_search("policy?", k=2, filter={"topic": {"$in": ["refunds", "returns"]}})
store.similarity_search("policy?", k=2, filter={"views": {"$gte": 100}})

# hybrid: blend vector similarity with keyword (FULLTEXT) matching
store.hybrid_search("O'Brien refund policy", k=3, vector_weight=0.5)

# maximal marginal relevance, for hits that aren't near-duplicates of each other
store.max_marginal_relevance_search("policy?", k=3, fetch_k=20, lambda_mult=0.5)

# cosine similarity, or a normalized [0,1] relevance score, with each hit
store.similarity_search_with_score("return policy?", k=2)
store.similarity_search_with_relevance_scores("return policy?", k=2)

# search with an embedding you already have
store.similarity_search_by_vector(my_vector, k=2)

# fetch or delete specific rows by id
store.get_by_ids(["1"])
store.delete(ids=["2"])

# use it as a retriever in any chain
retriever = store.as_retriever(search_kwargs={"k": 3})

Hybrid search

Vector search is great at meaning and bad at exact tokens: a product SKU, an error code, a surname like O'Brien. Keyword search is the opposite. hybrid_search runs both and fuses the two rankings, so you get semantic recall without losing the literal matches.

store.hybrid_search("error E4021 on checkout", k=5, vector_weight=0.5)

It uses reciprocal rank fusion: each retriever returns its top fetch_k, and a document's score is the weighted sum of 1 / (rank + k) across the two lists. Fusing on rank means the two very different score scales (cosine distance vs. a FULLTEXT relevance score) never have to be reconciled. vector_weight runs 0 (pure keyword) to 1 (pure vector); 0.5 is a sensible start.

The keyword half needs a MySQL FULLTEXT index on the content column. Tables created by this package (0.6.0+) get one automatically. For a table created by an earlier version, or your own table, add it once:

store.ensure_fulltext_index()

Two InnoDB FULLTEXT quirks worth knowing, because they're easy to mistake for bugs: the default minimum token length is 3 (innodb_ft_min_token_size), so one- and two-character terms are ignored, and common words on the stopword list don't match. Both are server settings, not something this package controls.

Async

If you're in an async app, use the a-prefixed methods and they'll do non-blocking I/O through aiomysql instead of tying up the event loop:

from langchain_shannonbase import ShannonBaseVectorStore

store = ShannonBaseVectorStore(embedding=embeddings, table="documents",
                               host="127.0.0.1", user="root", password="", database="rag")
await store.aadd_texts(["hello world"])
docs = await store.asimilarity_search("greeting", k=3)
hits = await store.ahybrid_search("error E4021", k=5)

Install the driver with the async extra:

pip install "langchain-shannonbase[async]"

MySQL 9 defaults to caching_sha2_password, which aiomysql needs cryptography for; the extra pulls it in. aadd_texts, asimilarity_search[_with_score], aget_by_ids, adelete, and ahybrid_search are wired to the async driver. Any async method you don't see here still works through LangChain's thread-pool fallback, and so does everything if you haven't installed the async extra.

How it works

No extensions, just MySQL 9's built-in vector support:

CREATE TABLE documents (
  id VARCHAR(36) PRIMARY KEY,
  content TEXT,
  metadata JSON,
  embedding VECTOR(1536)
);
-- inserts go through STRING_TO_VECTOR('[...]')
-- search:  ORDER BY DISTANCE(embedding, STRING_TO_VECTOR('[...]'), 'COSINE') LIMIT k

Search returns the nearest rows as LangChain Documents, each with a score of 1 - distance. Cosine is the default; pass metric="dot" or metric="euclidean" if you'd rather. similarity_search_with_relevance_scores normalizes to [0, 1] for all three metrics. dot uses the fact that ShannonBase's DISTANCE(...,'DOT') is the negated inner product, so on normalized embeddings (what most models produce) the dot score matches cosine; larger inner products from non-unit vectors clamp to 1.

Performance and scale

By default search is exact: a full DISTANCE scan that returns the true nearest neighbours, so recall is 100%. That's fine for thousands to low millions of vectors.

Past that, build an approximate IVF index so a search only scans a fraction of the table:

store.add_texts(my_docs)           # load your data first
store.build_index(n_lists=1000)    # k-means centroids + an indexed cluster column

store.similarity_search("query", k=5, nprobe=10)   # scans ~ nprobe / n_lists of the rows

It's k-means clustering plus an indexed cluster column, the same idea as pgvector's IVFFlat, done in application logic because MySQL 9 and ShannonBase don't have a native ANN index yet. Recall is approximate and rises with nprobe. On clustered data the trade is steep in your favour: in the offline tests, probing 1 of 8 lists keeps recall@10 at 1.0 while scanning ~12% of rows. Real recall depends on your data, so measure with bench/benchmark.py on your own set.

Rows added after build_index are assigned to their nearest centroid automatically, so the index stays correct as you keep writing. Rebuild periodically (call build_index again) to re-centre the clusters as the data grows.

A note on native indexes, since people ask: ShannonBase and self-hosted MySQL 9 do a brute-force DISTANCE scan with no built-in ANN index (I checked the ShannonBase source), which is exactly why the IVF index above exists. MySQL HeatWave is the exception, it documents an automatic vector index; if you're on HeatWave that's managed server-side, so I'd lean on it there and skip build_index.

Connections are pooled (pool_size defaults to 5, override it in the constructor), so repeated queries reuse connections instead of reconnecting each time.

There's a latency benchmark in bench/benchmark.py if you want numbers for your own instance.

API

Method What it does
add_texts(texts, metadatas, ids) embed and upsert, returns the ids
hybrid_search(query, k, vector_weight=...) vector + FULLTEXT keyword, fused by rank
similarity_search(query, k, filter=...) top-k Documents, optional metadata filter
similarity_search_with_score(query, k) same, with similarity scores
similarity_search_with_relevance_scores(query, k) with normalized [0,1] scores (cosine)
max_marginal_relevance_search(query, k, fetch_k, lambda_mult) diverse results
similarity_search_by_vector(embedding, k) search with a raw vector
get_by_ids(ids) fetch documents by id
delete(ids) remove by id
from_texts(texts, embedding, ...) build a populated store in one call
build_index(n_lists, nprobe) build an approximate IVF index for large tables

Metrics: cosine (default), dot, euclidean.

Custom schema

By default the store creates and owns its table. To point it at an existing table, or to use your own column names, pass them in and turn off table creation:

store = ShannonBaseVectorStore(
    embedding=embeddings,
    table="my_docs",
    id_column="doc_id",
    content_column="body",
    metadata_column="meta",
    embedding_column="vec",
    create_table=False,          # don't CREATE TABLE; use what's already there
    host="127.0.0.1", user="root", password="", database="app",
)

Column and table names are validated as SQL identifiers.

Testing

The logic is unit-tested offline against an in-memory backend, so you don't need a database to run the suite. That's also how the LangChain standard tests run in CI:

pip install -e ".[dev]"
pytest

There's a live round-trip test too, which runs against a real instance when you give it connection details:

export SB_HOST=127.0.0.1 SB_USER=root SB_PASSWORD=... SB_DATABASE=test
pytest tests/test_integration.py

For local development, ShannonBase gives you the MySQL 9 vector features without a HeatWave subscription.

Roadmap

Next on my list:

  • Wire the async path to a native HeatWave vector index where one's available
  • LlamaIndex sibling package, so the same MySQL backend works outside LangChain
  • More bench/ coverage: recall-vs-nprobe curves you can reproduce on your own data

A native ANN index would be next if MySQL or ShannonBase shipped one, but neither does today (HeatWave's is server-side and automatic), so the IVF index via build_index is the answer until then.

Done recently: native async (aiomysql), relevance scores for all three metrics, hybrid search (vector + FULLTEXT), an approximate IVF index, custom schemas, and metadata filter operators.

Issues and PRs welcome.

Requirements

  • Python 3.9+
  • A MySQL-9-compatible database with the VECTOR type (ShannonBase, MySQL 9, or HeatWave)
  • mysql-connector-python (via the [mysql] extra)

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

langchain_shannonbase-0.7.0.tar.gz (27.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

langchain_shannonbase-0.7.0-py3-none-any.whl (22.3 kB view details)

Uploaded Python 3

File details

Details for the file langchain_shannonbase-0.7.0.tar.gz.

File metadata

  • Download URL: langchain_shannonbase-0.7.0.tar.gz
  • Upload date:
  • Size: 27.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for langchain_shannonbase-0.7.0.tar.gz
Algorithm Hash digest
SHA256 846823b451980b7df9bd2b257ffa58f2cb3e4bc9c8d92e6ae56b0b2e60b01e2f
MD5 d4448387b27ec044821652eee20aae4e
BLAKE2b-256 9bdd1ad6ec08b6920b41ab300164ea0959c115096269f1f80bfffac941b43915

See more details on using hashes here.

Provenance

The following attestation bundles were made for langchain_shannonbase-0.7.0.tar.gz:

Publisher: publish.yml on apoorva-01/langchain-shannonbase

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file langchain_shannonbase-0.7.0-py3-none-any.whl.

File metadata

File hashes

Hashes for langchain_shannonbase-0.7.0-py3-none-any.whl
Algorithm Hash digest
SHA256 bcc6ac51abc92817528767b7b94737a98f8c7018f30f80e6f3743296db6bf7a1
MD5 7fb10e9b9003c79589f78cea99d0aabe
BLAKE2b-256 b54d7496172ef9c6f47002c227a4807ea14ae9374e4fc1f748ace173b1f56ca3

See more details on using hashes here.

Provenance

The following attestation bundles were made for langchain_shannonbase-0.7.0-py3-none-any.whl:

Publisher: publish.yml on apoorva-01/langchain-shannonbase

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page