Skip to main content

Introduction

milvusql is a PEP 249 DBAPI for Milvus — sync and async — with a SQLAlchemy 2.0 dialect and a Django database backend built on top of it.

Why does this exist?

Milvus speaks gRPC, not SQL. There is no server-side SQL surface to connect a normal DBAPI to. So milvusql builds one: it parses and generates a SQL-like language called MilvusQL (via sqlglot-milvus, a standalone sqlglot dialect), and translates the resulting AST into pymilvus calls.

SQLAlchemy Core / Django ORM
│ select(Item).order_by(Item.embedding.l2_distance(q)).limit(5)

milvusql's SQLCompiler / SQL generation
│ "SELECT id, category FROM items ORDER BY embedding <-> :q LIMIT 5"

milvusql.dbapi.Cursor.execute(sql_text, params) ← PEP 249


sqlglot.parse_one(sql_text, read="milvus") ← sqlglot-milvus
│ AST

translate.ast_to_pymilvus.build_call(...) ← milvusql core


pymilvus.MilvusClient.search(...) / .insert(...) / ...

A vector never becomes query text. It travels as a real bind value the whole way down — the same reason pgvector.psycopg passes vectors as parameters instead of formatting them into SQL.

Three packages, one workspace

PackageWhat it isDepends on
milvusqlThe DBAPI itself — connect(), Cursor, AsyncCursor, the PEP 249 error hierarchysqlglot-milvus, pymilvus
milvusql-sqlalchemyA SQLAlchemy 2.0 dialectmilvusql, sqlalchemy
milvusql-djangoA Django database backendmilvusql, django

They're separately installable: a script that only needs cursor.execute(...) doesn't have to pull in SQLAlchemy or Django to get it.

Status

milvusql reached v1.0.0 — packaged as Development Status :: 5 - Production/Stable. The DBAPI core and the SQLAlchemy dialect are exercised end-to-end against Milvus Lite (create/load/insert/ search/delete/release, DDL, reflection, full-text, JOIN/GROUP BY/subqueries). The Django backend's schema/migration layer is still a first cut — see Schema & Migrations for exactly what that does and doesn't cover yet.

Next steps