AI-native embedded multimodal database for embodied intelligence. Columnar storage engine with ACID transactions, vector search, full-text search, and spatial indexing โ in a single embedded library.
Status: pre-1.0. The Rust embedding API and storage engine are stable and heavily tested; the SQL surface and multi-language FFI are still evolving. See Supported SQL for the current feature set.
cargo add motedbA minimal, runnable example (examples/hello_world.rs):
use motedb::{Database, QueryResult};
fn main() -> motedb::Result<()> {
// Create or open an embedded database (single file + WAL sidecars).
let db = Database::create("hello.mote")?;
// Standard SQL: CREATE / INSERT / SELECT
db.execute("CREATE TABLE users (id INT PRIMARY KEY, name TEXT, age INT)")?;
db.execute("INSERT INTO users VALUES (1, 'Ada', 36)")?;
db.execute("INSERT INTO users VALUES (2, 'Linus', 54)")?;
// Query โ materialize results
let result = db.execute("SELECT name, age FROM users WHERE age > 40")?;
if let QueryResult::Select { rows, .. } = result.materialize()? {
for row in &rows {
println!("{:?}", row);
}
}
Ok(())
}Run it with:
cargo run --example hello_worldFor the multimodal features (vector / full-text / spatial search), see
examples/crud.rs and the indexes overview.
Python bindings (pip install motedb-python, imported as motedb) โ bulk load and columnar fetch are
first-class:
import motedb, numpy as np
db = motedb.Database("app.mote")
db.execute("CREATE TABLE ev (id INT PRIMARY KEY, ts TIMESTAMP, emb VECTOR(384), tag TEXT)")
# ๐ ๅๅผๆน้ๅฏผๅ
ฅ (numpy ็ด้, GroupCommit ่ไน
ๆกฃ 197K rows/s):
db.insert_arrays("ev", {
"id": np.arange(N),
"ts": ts_micros_array, # i64 micros
"emb": emb_2d_float32, # (N, 384) f32
"tag": [f"t{i%16}" for i in range(N)],
})
# ๐ ๅๅผๅๅ (ๅ่ดจๆฐๅผๅ โ numpy ้ถๆท่ด; TEXT/NULL โ Python ๅ่กจ):
cols, arrays = db.fetch_arrays("SELECT id, ts, emb_norm... FROM ev WHERE ts > ?",
params=[t0])
# INSERT ... SELECT (็ฉๅๅ่ตฐๅไธๆน้็ฎก็บฟ, 1.6M rows/s):
db.execute("INSERT INTO archive SELECT * FROM ev WHERE ts < ?", params=[cutoff])Benchmark: 100K rows ร 384-dim vectors (+TEXT/FLOAT/TIMESTAMP) on Apple
Silicon M-series, official harness bindings/python/bench/compete_bench.py
(SQLite ๅ็
งๅๅฝข็ถ; ่ฏฆ็ปๆนๆณๅญฆไธๅๅฒๆฒ็บฟ่ง bindings/python/bench/README.md).
| Operation | MoteDB | SQLite | ๅๆฐ |
|---|---|---|---|
ๆน้ๅฏผๅ
ฅ insert_arrays (GroupCommit ่ไน
ๆกฃ) |
197K rows/s | 2.4M rows/s* | *SQLite ๆ ๅ้ๅ |
| ๆน้ๅฏผๅ ฅ (NoSync/Periodic preset) | 286K rows/s | โ | |
| PK ็นๆฅ | 17ยตs | 7ยตs | |
| ่ๅด COUNT+AVG (่ๅ+ๅนถ่ก) | 0.19ms | 0.39ms | 2.1x |
| GROUP BY (morsel ๅนถ่ก) | 0.71ms | 58ms | 82x |
| equi-JOIN + GROUP BY (ๆน hash join) | 0.44ms | 10.9ms | 25x |
| top-k ORDER LIMIT | 0.45ms | 45.7ms | 100x |
| ๅ้ top-10 (ๆ ็ดขๅผ็ฒพ็กฎๆซๆ, SIMD+ๅนถ่ก) | 18ms | 18.4ms** | **SQLite ๆ ๅ้็ฑปๅ, ๅ็ งไป ๅๆฐๆฎ้ |
| FTS BM25 | 0.13ms | โ | |
| ็ฌๅกๅฐ็งฏ COUNT(*) (6 ไบฟๅฏน, ๆๅ ) | 0.5ms | โ |
ๆฅ่ฏขๅ
ๅญ: ๅ
จ้จๆฅ่ฏขๅฝข็ถ RSS ๅข้ โ0 (steady-state ๅฎๆต <20MB, ้ป่ฎค
ๆกฃ็บฆๆ โค100MB); ่ตๆบ็ปๅๅ
จๅฅๅฟซ็
ง่ง resource_bench_*.jsonใ
ๅคๅผๆๅฏน็
ง (220Kร384, ๅ่ฏญๆๅ ground truth, make bench-industry
ไธ้ฎๅค็ฐ): MoteDB DiskANN recall@10 1.0000 / p50 0.57ms โ ๅฏน็
ง
faiss-hnsw(ef256) 0.96/0.38msใusearch 0.994/0.25msใduckdb-hnsw
0.999/0.96msใlancedb-ivfpq(้ป่ฎค) 0.22-0.25/1.2-3.1msใๅฎๆด่กจไธๆนๆณๅญฆ
(ๅซๅฏน MoteDB ไธๅฉ็่ก): docs/benchmark_report_2026-10-21.mdใ
ๆง่กๅ
ๆ ธ: ๅ้ๅ (VEC) + morsel ๅนถ่ก้ป่ฎคๅผๅฏ (MOTE_VEC=off ไธ้ฎ
ๅ้ๆง่กๅผ่ทฏๅพ); ๆ ็ดขๅผๅ้ๆซๆไธบๅญ่่ท็ฆปๆ ธ้ถๆท่ด (้กต็ผๅญๅธฆๅฎฝๅฐๆฟ)ใ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ MoteDB โ
โโโโโโโโโโโโฌโโโโโโโโโโโฌโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโค
โ SQL โ Vector โ Text โ Spatial โ
โ Parser โ DiskANN โ FTS โ i-Octree โ
โโโโโโโโโโโโดโโโโโโโโโโโดโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโค
โ Columnar Storage Engine โ
โ โโโโโโโโโโโ โโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโ โ
โ โ WAL โโ โ Columnar โโ โ Columnar SSTable โ โ
โ โ (fsync) โ โ Buffer โ โ (mmap + zstd) โ โ
โ โโโโโโโโโโโ โโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ MVCC Transaction โ Snapshot Isolation โ Conflict โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- Storage: Columnar SSTable, mmap zero-copy access, optional page-level zstd compression (
compact_storage, ~40-50% smaller disk; on by default infor_edge) - Write Path: WAL (durability) โ columnar buffer โ auto-finalize โ SSTable
- Read Path: SelectColumnar (zero-materialization), typed array access, predicate pushdown
- Transactions: VersionStore MVCC with snapshot isolation and conflict detection
MoteDB emits lifecycle and durability events (WAL flush, checkpoint, background
thread errors) through the standard log facade. The
library installs no logger itself โ your application chooses one (e.g.
env_logger, tracing, slog) and controls verbosity:
fn main() {
// Install a logger (env_logger reads RUST_LOG).
let _ = env_logger::Builder::from_env(
env_logger::Env::default().default_filter_or("off")
).try_init();
let db = motedb::Database::create("my_data").unwrap();
// ...
}RUST_LOG=motedb=info ./your_app # lifecycle events (open/close/checkpoint)
RUST_LOG=motedb=warn ./your_app # degraded conditions only
RUST_LOG=motedb=debug ./your_app # verbose hot-path detailWith no logger installed, all logging compiles to a no-op (zero runtime cost).
See examples/logging.rs for a runnable demo.
- ๐ ่ฟ็ปด่ชๆฃ โ
db.doctor()/motedb-cli doctor <path>๏ผ่กจๅธๅฑใๅ ๅญ้ข็ฎใ็ดขๅผ่ฆ็็ใ็ฃ็ๅ ็จ้้กน PASS/WARN ๆฅๅใ
SELECT id FROM RRF(
(SELECT id FROM docs WHERE MATCH(body, 'grasp failure') ORDER BY bm25(body) LIMIT 100),
(SELECT id FROM docs ORDER BY emb <-> ? LIMIT 100)
);One-SQL BM25 โ vector fusion (Reciprocal Rank Fusion, k/rrk/w1..w4
tunable). snippet(body, 'grasp failure', 80) renders FTS5-style excerpts
with <b> highlights.
-- For each frame, the latest joint-state at-or-before its timestamp (per session):
SELECT f.fname, j.q FROM frames f
ASOF JOIN joints j ON f.session = j.session AND j.ts <= f.ts;
-- LEFT ASOF JOIN keeps frames with no prior state (NULL right side).CREATE EPISODE TABLE teleop (
session TEXT, ts TIMESTAMP, joint VECTOR(7), gripper FLOAT
) EPISODE BY (session, ts) GAP '5s';
-- One row per episode: bounds, row count, and a 64-sample resampled
-- trajectory per VECTOR column (flattened 64รdim).
SELECT session, t0, t1, n_rows FROM EPISODES(teleop);
-- Trajectory similarity (DTW over the resampled series; two-stage
-- PAA-shortlist design validated in docs/design/2026-10-traj-spike.md):
SELECT session, t0 FROM EPISODES(teleop)
ORDER BY traj_sim(traj_joint, [0.0, 0.1, โฆ]) LIMIT 10;Episodes are derived data โ segmentation is a pure function of
(key, ts, gap), so there is no registry to persist or invalidate and
reopen consistency is free.
COMMIT DATASET 'fixpos-v3' NOTE 'after gripper fix';
-- โฆ table keeps evolving (compaction GC will not touch pinned segments) โฆ
EXPORT DATASET 'fixpos-v3' TO '/data/fixpos-v3.mote'; -- materialized copy
DROP DATASET 'fixpos-v3'; -- release the pinA snapshot is just the pinned segment list (segments are immutable files)
โ near-free to take, persisted in _dataset_commits, and compaction
physically defers deletion of pinned files until the pin is dropped.
EXPORT materializes the pinned state into a fresh standalone database.
motedb import mcap robot.mote recording.mcap # CLI
# ๆ Python:
db.import_mcap("recording.mcap", decode={"/joint_states": my_decoder})One table per topic (_uid PK, log_time/publish_time TIMESTAMP + auto
index, raw message data BLOB); a per-topic Python decode hook adds typed
columns (e.g. decoded emb VECTOR) at import time. Re-imports are no-ops
(_uid dedup) โ crash-safe resumes.
| Modality | Index | Query |
|---|---|---|
| Tabular | Column Value (B-tree) | WHERE, ORDER BY, GROUP BY |
| Vector | DiskANN (Vamana graph) | ORDER BY col <-> query LIMIT k |
| Text | FTS (Inverted Index) | WHERE MATCH(col) AGAINST('query') |
| Spatial | i-Octree (3D) | ST_DISTANCE, KNN, radius search |
- Low memory: 222 B/row (vs SQLite's 335 B โ 34% less); ๆฅ่ฏขๆ RSS ๅข้ โ0
(้ป่ฎคๆกฃๅ้็ผๅญ้ข็ฎ 64MB,
set_vector_cache_budgetๆ่กจ่ฐๆด) - Zero-copy reads: mmap with on-demand page loading; ๆ ็ดขๅผๅ้ๆซๆไธบ ๅญ่่ท็ฆปๆ ธ้ถๆท่ด (้ๅฏน้ฝ SIMD ็ด่ฏป้กต็ผๅญ)
- Fast writes: ๅๅผๆน้ๅฏผๅ ฅ insert_arrays โ GroupCommit ่ไน ๆกฃ 197K rows/s / NoSync ๆกฃ 286K (100Kร384 ๅฃๅพ); fast path ๆฎต็ดๅ่ไน (temp+fsync+rename ๅๅญๅๅธ + manifest fsync), WAL ๅชๆๅกๆ ข่ทฏๅพ
- Auto-checkpoint ๅ่งฆๅ: WAL ๅคงๅฐ + ๆฎต่ฎกๆฐ (
max_segment_count, ้ป่ฎค 32; edge preset 16) โ WAL-less ๆน้ๅฏผๅ ฅ็ๆฎต้ตๆ็ - Small disk: zstd compression in compact mode (~40-50% smaller, 67 B/row on disk)
- No daemon: Single library, embedded directly
- Atomic: WAL-based crash recovery
- Consistent: PK uniqueness, NOT NULL, type coercion
- Isolated: MVCC snapshot isolation
- Durable: WAL fsync + auto-finalize
Every push runs through three CI workflows:
| Workflow | What it guards |
|---|---|
| CI | fmt (strict), clippy, cargo-deny supply chain, unit + integration suites on ubuntu/macos, native arm64 test runner (the edge/robotics target), aarch64 cross-compile, kill -9 crash-injection durability loop |
| Perf Gate | Query-shape latency ratios vs a full-scan baseline โ machine-independent budgets that catch complexity-class regressions (e.g. an O(Nยฒ) path that slipped through once) |
| Fuzz (daily) | 5 min ร 2 targets (fuzz_sql_parser, fuzz_wal_recover) under AddressSanitizer โ found and fixed a parser stack-overflow on day one |
| Language | Package | Status |
|---|---|---|
| Rust | motedb (crates.io) |
stable |
| Python | motedb-python (PyPI, import motedb) |
stable |
| Node.js | motedb (npm, bindings/node, napi-rs v3) |
preview โ prebuilt for macOS arm64/x64, Linux x64/arm64 (glibc), Windows x64; musl/Alpine builds in progress |
| Go | bindings/go (cgo) |
preview |
Installation problems? See docs/18-install-troubleshooting.md.
cargo add motedbOr in Cargo.toml:
[dependencies]
motedb = "0.16"For minimal edge builds (no tokenizer, no parallelism), disable default features:
[dependencies]
motedb = { version = "0.16", default-features = false, features = ["jemalloc"] }Pick a preset that matches your device, or start from one and override fields:
use motedb::{Database, DBConfig};
// Edge device (low memory, periodic fsync, single write partition)
let config = DBConfig::for_edge();
// Robotics (fast sensor ingestion, vector support)
let config = DBConfig::for_robotics();
// Embodied AI (vision-language models, real-time control loops)
let config = DBConfig::for_embodied();
let db = Database::create_with_config("my_data", config)?;See docs/ for the full configuration reference and per-field docs.
Supported: CREATE TABLE / CREATE INDEX (column, vector, text, spatial,
timestamp) / CREATE TEXT|VECTOR|SPATIAL|TIMESTAMP INDEX, DROP TABLE [IF EXISTS] / DROP INDEX, ALTER TABLE (ADD COLUMN, AUTO_INCREMENT = N),
INSERT (ๅซ INSERT ... SELECT), UPDATE, DELETE, SELECT with:
WHERE,JOIN(INNER / LEFT / RIGHT / FULL), subqueries inWHERE,FROM (SELECT ...)ๆดพ็่กจGROUP BY(columns, expressions likeid % 5, and SELECT aliases),HAVING(incl. aggregate aliases),ORDER BY(multi-key, expressions, non-projected columns,NULLS FIRST/LAST),LIMIT/OFFSETDISTINCT(rows) andCOUNT(DISTINCT col)aggregates- Aggregates:
COUNT,SUM,AVG,MIN,MAX,STDDEV,VARIANCE UNION/UNION ALLCASE WHEN ... THEN ... ELSE ... ENDWITH/ Common Table Expressions incl.WITH RECURSIVE(semi-naive fixed-point, UNION ALL bodies, chained CTEs)- Window functions:
ROW_NUMBER,RANK,DENSE_RANK,NTILE,LAG,LEAD,FIRST_VALUE,LAST_VALUE+ aggregatesOVER (PARTITION BY โฆ ORDER BY โฆ [ROWS|RANGE BETWEEN <bound> AND <bound>])โ explicit frames (ROWS physical rows; RANGE peer groups or value intervals likeRANGE BETWEEN 10 PRECEDING AND 10 FOLLOWINGon the numeric/timestamp order key; default RANGE peer-group frame) - Scalar functions:
UPPER/LOWER/LENGTH/TRIM/SUBSTR/REPLACE/CONCAT(skips NULL args, SQLite/Postgres semantics;||propagates NULL)/INSTR/COALESCE/ROUND(binary-exact decimal rounding)/ABS/arithmetic - Interop:
EXPORT TABLE t TO 'x.parquet'/IMPORT PARQUET 'x' INTO t(pyarrow/pandas/DuckDB round-trip),EXPORT LEROBOT <episode-table> TO 'dir/' [FPS n] [TASKS col]โ a LeRobot v3 dataset the officialLeRobotDatasetloads directly - Multimodal predicates:
MATCH(col) AGAINST('q')(BM25 ranked FTS; multi-word default is AND, explicita OR bunions โ FTS5-compatible), vector<->/<~>ordering (DiskANN ANN), geometryloc <-> ST_POINT(x, y)distance ordering,ST_WITHIN,ST_DISTANCE,ST_KNN - Transactions:
BEGIN/COMMIT/ROLLBACK, savepoints, read-your-writes visibility inside a transaction
BLOB type: payload BLOB columns with SQLite-style X'0aff00' hex
literals and Python bytes parameters (byte-exact round-trip, 16 MiB
cap). Known v0.13 gaps: scalar functions over BLOB columns
(LENGTH(payload)) and BLOB output under ORDER BY are not yet wired
through all executor paths โ bare projection, equality (WHERE payload = X'โฆ'), time-window scans and parameters are solid.
Not yet supported: DECIMAL/DATE types, cross-statement server-side
cursors, and explicit window frames (ROWS/RANGE specs).
Full guides live in docs/:
- Quick start ยท Installation & config ยท SQL operations
- Batch operations ยท Transactions
- Indexes: overview ยท column ยท vector ยท text ยท spatial ยท timestamp
- Performance tuning ยท Data types ยท API reference ยท Best practices ยท FAQ
- Hybrid search & Arrow/pandas interop ยท Install troubleshooting
API docs: https://docs.rs/motedb
MIT โ see LICENSE.
