Metadata-Version: 2.4
Name: milvusql
Version: 0.1.4
Summary: PEP 249 DBAPI (sync + async) for Milvus, backed by sqlglot-milvus
Keywords: milvus,dbapi,pep249,vector-search,asyncio
Author: Neko1313
Author-email: Neko1313 <nikita.ribalchencko@yandex.ru>
License-Expression: MIT
License-File: LICENSE
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.12
Classifier: Framework :: AsyncIO
Classifier: Topic :: Database
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Dist: pymilvus>=2.6,<3
Requires-Dist: sqlglot-milvus>=0.1.0,<0.2
Requires-Python: >=3.12
Project-URL: Homepage, https://github.com/Callix-Tools/milvusql
Project-URL: Repository, https://github.com/Callix-Tools/milvusql
Project-URL: Issues, https://github.com/Callix-Tools/milvusql/issues
Description-Content-Type: text/markdown

<div align="center">
  <img src="https://Callix-Tools.github.io/milvusql-docs/img/logo.svg" alt="milvusql logo" width="220"/>

  <h1>milvusql</h1>

  <p>A <a href="https://peps.python.org/pep-0249/">PEP 249</a> DBAPI (sync + async) for <a href="https://milvus.io">Milvus</a> — parses/generates MilvusQL via <a href="https://github.com/Callix-Tools/sqlglot-milvus"><code>sqlglot-milvus</code></a> and executes the resulting AST against <code>pymilvus</code>.</p>

  [![PyPI](https://img.shields.io/pypi/v/milvusql?color=blue)](https://pypi.org/project/milvusql/)
  [![Python](https://img.shields.io/pypi/pyversions/milvusql)](https://pypi.org/project/milvusql/)
  [![PyPI Downloads](https://static.pepy.tech/personalized-badge/milvusql?period=total&units=INTERNATIONAL_SYSTEM&left_color=lightgrey&right_color=blue&left_text=downloads)](https://pepy.tech/projects/milvusql)
  [![License](https://img.shields.io/github/license/Callix-Tools/milvusql)](LICENSE)
  [![CI](https://img.shields.io/github/actions/workflow/status/Callix-Tools/milvusql/ci-core.yml?label=CI)](https://github.com/Callix-Tools/milvusql/actions)

  [📚 Documentation](https://Callix-Tools.github.io/milvusql-docs/) · [PyPI](https://pypi.org/project/milvusql/) · [sqlglot-milvus](https://github.com/Callix-Tools/sqlglot-milvus)
</div>

---

## Why a DBAPI, not a client wrapper?

| Feature | **milvusql** | raw `pymilvus` |
|---|:---:|:---:|
| Query surface | SQL (MilvusQL) | Python method calls |
| Parameterized queries | ✅ `:name` binds | ⚠️ manual dict-building |
| Standard `Connection`/`Cursor` (PEP 249) | ✅ | ❌ |
| Sync + async, same dispatch table | ✅ | ⚠️ separate `MilvusClient`/`AsyncMilvusClient` |
| Drop-in for SQLAlchemy / Django | ✅ [`milvusql-sqlalchemy`](packages/milvusql-sqlalchemy), [`milvusql-django`](packages/milvusql-django) | ❌ |
| Auto-`LOAD` on first use, cached per connection | ✅ | manual `load_collection()` |
| Consistency-level fallback (per-connection default, per-query override) | ✅ | manual per-call |

Writing MilvusQL instead of chaining `pymilvus` calls means the same `SELECT ... ORDER BY embedding <=> :q LIMIT n` string works whether it's typed by hand, generated by an ORM, or built by an LLM tool call — and it works the same way from `cursor.execute()` or `await acursor.execute()`, off one shared parser and dispatch table (`translate.ast_to_pymilvus`).

## Installation

```bash
pip install milvusql
```

## Quick start

```python
import milvusql

conn = milvusql.connect(uri="./items.db")  # Milvus Lite, or a real server's URI
cur = conn.cursor()

cur.execute(
    """
    CREATE TABLE items (
        id BIGINT PRIMARY KEY AUTO_INCREMENT,
        embedding VECTOR(8),
        category VARCHAR(64)
    ) WITH (shards=1, consistency_level='Strong')
    """
)
cur.execute(
    "CREATE INDEX idx_embedding ON items (embedding) USING HNSW WITH (metric_type='COSINE')"
)
cur.executemany(
    "INSERT INTO items (embedding, category) VALUES (:embedding, :category)",
    [{"embedding": [0.1] * 8, "category": "book"}],
)

cur.execute("SELECT id FROM items WHERE category = :cat LIMIT 10", {"cat": "book"})
print(cur.fetchall())

cur.execute(
    "SELECT id FROM items ORDER BY embedding <=> :q LIMIT 5",
    {"q": [0.1] * 8},
)
print(cur.fetchall())
```

The same program, asyncio-native, over `milvusql.aio` (built on `pymilvus.AsyncMilvusClient`):

```python
from milvusql import aio

conn = aio.connect(uri="./items.db")
cur = conn.cursor()

await cur.execute("SELECT id FROM items WHERE category = :cat", {"cat": "book"})
async for row in cur:
    print(row)

await conn.close()
```

## API

### `milvusql.connect()`

```python
milvusql.connect(
    uri="http://localhost:19530",  # or a Milvus Lite file path
    token="",                      # "user:password", or a full token string
    db_name="",
    consistency_level=None,        # per-connection default; a query's own CONSISTENCY LEVEL wins
    **kwargs,                      # passed straight through to pymilvus.MilvusClient
) -> Connection
```

| `Connection` | Description |
|---|---|
| `.cursor()` | Returns a new `Cursor` bound to this connection |
| `.commit()` | No-op — every statement is already applied when it returns |
| `.rollback()` | Raises `NotSupportedError` — Milvus has no multi-statement rollback; catch and compensate instead |
| `.close()` | Closes the underlying `MilvusClient` |
| Context manager | `with milvusql.connect(...) as conn: ...` |

| `Cursor` | Description |
|---|---|
| `.execute(operation, parameters=None)` | Runs one statement; `parameters` binds `:name` placeholders |
| `.executemany(operation, seq_of_parameters)` | Batched `INSERT` in one round trip where the statement allows it; falls back to one call per parameter set otherwise |
| `.fetchone()` / `.fetchmany(size)` / `.fetchall()` | Read back result rows |
| `.description`, `.rowcount`, `.lastrowid`, `.arraysize` | Standard PEP 249 attributes |
| Iteration | `for row in cursor: ...` |

`milvusql.aio.connect()`/`AsyncConnection`/`AsyncCursor` mirror the same shape, `async`/`await` throughout — deliberately **not** PEP 249 itself (`execute()` as a coroutine can't be), but built on the same parser, dispatch table, and error hierarchy as the sync path.

### Errors

Standard PEP 249 hierarchy, importable from `milvusql`:

```
Warning
Error
├── InterfaceError
└── DatabaseError
    ├── DataError
    ├── OperationalError
    ├── IntegrityError
    ├── InternalError
    ├── ProgrammingError
    └── NotSupportedError
```

Every `pymilvus` exception and gRPC error raised while executing a statement is translated into one of these before it reaches your code.

## Packages

This is the core of a `uv` workspace. Two packages build on `milvusql`'s DBAPI:

| Package | Description |
|---|---|
| [`milvusql-sqlalchemy`](packages/milvusql-sqlalchemy) | SQLAlchemy 2.0 dialect — `VECTOR`/`SPARSEVEC` column types, `hybrid_search()`, Alembic support |
| [`milvusql-django`](packages/milvusql-django) | Django database backend — `VectorField`, ORM CRUD/filtering through the normal compiler |

Each is installed and versioned separately; both depend on this package as their DBAPI layer.

## Examples

| Example | Shows |
|---|---|
| [`examples/basic_walkthrough`](examples/basic_walkthrough) | A guided, top-to-bottom tour of the DBAPI: connect, `CREATE TABLE`/`CREATE INDEX`, insert, filter `SELECT`, vector search, `UPDATE`/`DELETE` — sync and async |
| [`examples/temporal_worker`](examples/temporal_worker) | A [Temporal](https://temporal.io) workflow/activity that inserts rows into Milvus as a durable, retry-safe ingestion pipeline |

See also [`milvusql-sqlalchemy`'s own examples](packages/milvusql-sqlalchemy/examples) (a FastAPI image-search service, a pydantic-ai agent).

## Development

Requires Python 3.12+, [uv](https://docs.astral.sh/uv/), [task](https://taskfile.dev/).

```bash
task install           # uv sync --all-groups --all-packages
task lint              # ruff + ty + bandit for core + all packages
task tests             # all tests (core + sqlalchemy + django) -- integration tests need Docker (testcontainers)
```

Individual package tasks:

```bash
task core:lint         task core:test
task sqlalchemy:lint   task sqlalchemy:test
task django:lint       task django:test
```

## License

MIT
