IVF with dense vectors (clustered)

August 2, 2026 · View on GitHub

MongrelDB logo

MongrelDB Tcl Client

Pure Tcl HTTP client for MongrelDB - embedded+server database with SQL, vector search, full-text search, and AI-native retrieval.
Built on the Tcl 8.6+ standard library (http) plus json from tcllib. Requires tcllib for JSON support.

CI MongrelDB server License

Package

SurfacePackageInstall
Tcl packagemongreldb 0.63.1lappend auto_path src; package require mongreldb

Requirements

  • Tcl 8.6 or newer (uses tailcall, try/trap, and dict)
  • The standard http package (bundled with Tcl 8.6+)
  • tcllib for the json package (the json package is part of tcllib, not the Tcl core). Install via your OS package manager (e.g. apt-get install tcllib), Homebrew (brew install tcl-tcllib), or teacup install json.
  • A running mongreldb-server daemon

What It Provides

  • Typed CRUD over the Kit transaction endpoint: mongreldb::put, mongreldb::upsert (insert-or-update on PK conflict), mongreldb::delete by row id and mongreldb::deleteByPk by primary key, with idempotency keys for safe retries.
  • Query builder that pushes conditions down to the engine's specialized indexes for sub-millisecond lookups: bitmap equality, learned-range, null checks, and FM-index full-text search. Conditions are AND-ed.
  • Idempotent batch transactions - all operations staged locally and committed atomically with mongreldb::transaction, with the engine enforcing unique, foreign key, and check constraints at commit time. Idempotency keys return the original response on duplicate commits, even after a crash.
  • Full SQL access through the DataFusion-backed /sql endpoint via mongreldb::sql: recursive CTEs, window functions, CREATE TABLE AS SELECT, materialized views, and multi-statement execution.
  • Schema management: typed table creation, full schema catalog, and per-table descriptors.
  • Typed exceptions: failures throw with an error code of the form {MONGRELDB <category>} so callers can match by category with try ... trap.

Examples

Runnable, commented examples live in the docs:

  • Quickstart - install, start the daemon, write and run a complete script.
  • Transactions - batch commits, idempotency keys, constraint handling.
  • Queries - every native condition type and the index it pushes down to.
  • SQL - recursive CTEs, window functions, advanced SQL.
  • Authentication - bearer token, HTTP Basic, and open modes.
  • Errors - error categories, the HTTP-status mapping, and recovery patterns.

Quick Example

lappend auto_path /path/to/MongrelDB-Tcl/src
package require mongreldb

set db [mongreldb::connect http://127.0.0.1:8453]

# Create a table.
set cols [list \
    [dict create id 1 name id       ty int64   primary_key 1 nullable 0] \
    [dict create id 2 name customer ty varchar primary_key 0 nullable 0] \
    [dict create id 3 name amount   ty float64 primary_key 0 nullable 0] \
]
set constraintsJson {{"checks":[{"id":1,"name":"ck_status","expr":{"IsNotNull":3}}]}}
mongreldb::createTable $db orders $cols $constraintsJson

# Insert rows (cells is an even-length list {colId value ...}).
mongreldb::put $db orders {1 1 2 Alice 3 99.50}
mongreldb::put $db orders {1 2 2 Bob   3 150.00}

# Query with a native index condition (learned-range index).
set cond [mongreldb::condition range [dict create column_id 3 lo 100.0]]
set res [mongreldb::query $db orders [list $cond]]
puts "rows: [llength [dict get $res rows]]"

puts "count: [mongreldb::count $db orders]"  ;# 2

# Run SQL.
mongreldb::sql $db "UPDATE orders SET amount = 200.0 WHERE customer = 'Bob'"

Authentication

# Bearer token (--auth-token mode)
set db [mongreldb::connectWithToken http://127.0.0.1:8453 my-secret-token]

# HTTP Basic (--auth-users mode)
set db [mongreldb::connectWithBasicAuth http://127.0.0.1:8453 admin s3cret]

A token takes precedence over basic auth if both are supplied.

Batch transactions

Operations are staged locally and committed atomically. The engine enforces unique, foreign key, and check constraints at commit time.

set ops [list \
    [dict create put [dict create table orders cells {1 10 2 Dave 3 50.0}]] \
    [dict create put [dict create table orders cells {1 11 2 Eve 3 75.0}]] \
    [dict create delete_by_pk [dict create table orders pk 2]] \
]

# Atomic - all or nothing. The idempotency key makes it safe to retry.
try {
    mongreldb::transaction $db $ops batch-1
} trap {MONGRELDB conflict} {e opts} {
    puts "constraint violated: $e"
}

Native query builder

Conditions push down to the engine's specialized indexes. Build them with mongreldb::condition; multiple conditions are AND-ed.

# Bitmap equality (low-cardinality columns)
set bitmap [mongreldb::condition bitmap_eq [dict create column_id 2 value Alice]]

# Range query (learned-range index)
set range [mongreldb::condition range [dict create column_id 3 lo 50.0 hi 150.0]]

set res [mongreldb::query $db orders [list $bitmap $range] {1 3} 100]
if {[dict get $res truncated]} {
    # result set hit the limit; more matches exist on the server
}

Schema constraints

Optional fields on a column dict let you constrain what goes into a column at create time. All are omitted from the wire JSON when left unset, so existing schemas are unaffected.

# An enum column whose values must come from this fixed set.
# Wire emit: "enum_variants": ["active","inactive","paused"]
set cols [list \
    [dict create id 1 name id       ty int64   primary_key 1 nullable 0] \
    [dict create id 2 name customer ty varchar primary_key 0 nullable 0] \
    [dict create id 3 name status   ty enum    primary_key 0 nullable 0 \
               enum_variants [list active inactive paused] default_value active] \
]
mongreldb::createTable $db orders $cols

enum_variants is a Tcl list of strings; omitting it means "absent". default_value is a string. Use default_value_json for raw null, boolean, or number defaults, and default_expr for dynamic now or uuid. Literal "now" and "uuid" strings are expressed through default_value, not default_expr. The constraint is enforced server-side, so a row whose value falls outside the listed variants surfaces as a conflict error on mongreldb::put / mongreldb::transaction. The optional fourth argument is a validated JSON object in the daemon's constraints shape. Its checks array is sent as constraints.checks.

SQL

mongreldb::sql $db "INSERT INTO orders (id, customer, amount) VALUES (99, 'Zoe', 999.0)"
mongreldb::sql $db "CREATE TABLE archive AS SELECT * FROM orders WHERE amount > 500"

# Recursive CTEs and window functions
mongreldb::sql $db "WITH RECURSIVE r(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM r WHERE n<10) SELECT n FROM r"
mongreldb::sql $db "SELECT id, ROW_NUMBER() OVER (PARTITION BY customer ORDER BY amount DESC) FROM orders"

ANN index backends

The engine's ann index is swappable across three backends - hnsw (the default), diskann, and ivf - selected with the algorithm option. Quantization is independently configurable: dense, binary_sign, or product (product quantization, with num_subvectors, bits_per_subvector, pq_training_samples, pq_seed, and pq_rerank_factor). These are ordinary DDL strings run through sql, so no client changes are needed.

# DiskANN (in-memory Vamana graph)
mongreldb::sql $db "CREATE INDEX orders_emb_diskann ON orders USING ann (embedding) WITH (algorithm = 'diskann', quantization = 'dense', diskann_l = 50, diskann_r = 64, beam_width = 8)"

# IVF with dense vectors (clustered)
mongreldb::sql $db "CREATE INDEX orders_emb_ivf ON orders USING ann (embedding) WITH (algorithm = 'ivf', quantization = 'dense', nlist = 1024, nprobe = 16)"

# HNSW with product quantization (recall-tuned)
mongreldb::sql $db "CREATE INDEX orders_emb_hnsw_pq ON orders USING ann (embedding) WITH (algorithm = 'hnsw', quantization = 'product', m = 16, ef_construction = 200, ef_search = 50, num_subvectors = 32, pq_training_samples = 50000, pq_rerank_factor = 8)"

Error handling

Methods throw on failure with an error code of the form {MONGRELDB <category>}. Use try ... trap to catch by category.

try {
    mongreldb::schemaFor $db missing_table
} trap {MONGRELDB not_found} {e opts} {
    puts "not found: $e"
} trap {MONGRELDB conflict} {e opts} {
    puts "constraint: $e"
} trap {MONGRELDB auth} {e opts} {
    puts "not authorized: $e"
} trap {MONGRELDB network} {e opts} {
    puts "can't reach daemon: $e"
}

API reference

Client lifecycle

CommandDescription
mongreldb::connect urlConstruct a client (empty url defaults to http://127.0.0.1:8453)
mongreldb::connectWithToken url tokenBearer token auth (--auth-token mode)
mongreldb::connectWithBasicAuth url user passHTTP Basic auth (--auth-users mode)
mongreldb::close dbClose the client and free per-handle state
mongreldb::lastError dbMessage for the most recent failure

Database operations

CommandDescription
mongreldb::health dbCheck daemon health
mongreldb::tables dbList table names
mongreldb::createTable db name cols ?constraintsJson? ?indexesJson?Create a table with optional constraints and all index definitions
mongreldb::dropTable db nameDrop a table
mongreldb::count db tableRow count
mongreldb::put db table cells keyInsert a row
mongreldb::upsert db table cells upd keyUpsert a row
mongreldb::delete db table rowIdDelete by row id
mongreldb::deleteByPk db table pkDelete by primary key
mongreldb::transaction db ops keyCommit a batch atomically
mongreldb::query db table conds proj limit offsetRun a paged native query
mongreldb::condition type paramsBuild a query condition
mongreldb::conditionJson jsonBuild any complete condition, including ANN, sparse, and MinHash
mongreldb::sql db statementExecute SQL
mongreldb::schema dbFull schema catalog
mongreldb::schemaFor db tableSingle-table descriptor
mongreldb::historyRetentionEpochs dbCurrent history-retention window
mongreldb::earliestRetainedEpoch dbOldest epoch still readable with AS OF EPOCH
mongreldb::setHistoryRetentionEpochs db epochsSet the durable MVCC window
mongreldb::lastEpoch dbCommit epoch of the most recent /kit/txn

Building and testing

# Verify the package loads
tclsh <<< 'lappend auto_path src; package require mongreldb; puts ok'

# Run the offline wire-shape unit tests (no daemon needed)
tclsh tests/wire_shape_test.tcl

# Run the live integration suite. Set MONGRELDB_URL to use an already-running
# daemon. Tests self-skip when no daemon is reachable.
tclsh tests/live_test.tcl

Fetch a prebuilt server binary from the MongrelDB releases:

mkdir -p bin
curl -fsSL -o bin/mongreldb-server \
  https://github.com/visorcraft/MongrelDB/releases/download/v0.63.1/mongreldb-server-linux-x64
chmod +x bin/mongreldb-server

History retention

Use historyRetentionEpochs, setHistoryRetentionEpochs, earliestRetainedEpoch, and lastEpoch with MongrelDB 0.48.0+. The retention window controls how far back AS OF EPOCH time-travel queries can read; increasing it cannot bring back history that has already been pruned.

# Inspect the current durable MVCC window.
puts [mongreldb::historyRetentionEpochs $db]  ;# e.g. 1024
puts [mongreldb::earliestRetainedEpoch $db]   ;# e.g. 3

# Widen the window. The response contains the updated values.
set resp [mongreldb::setHistoryRetentionEpochs $db 1000]
puts [dict get $resp history_retention_epochs]  ;# 1000

# After a write, lastEpoch holds the commit epoch of the most recent put,
# upsert, delete, or transaction commit.
mongreldb::put $db orders {1 1 2 99.5}
set insertEpoch [mongreldb::lastEpoch $db]
set rows [mongreldb::sql $db "SELECT id, amount FROM orders AS OF EPOCH $insertEpoch"]

Contributing

Contributions are welcome. Please:

  1. Open an issue first for non-trivial changes.
  2. Add focused tests near your change - the suite must stay green.
  3. Keep the code pure Tcl 8.6+; the only external dependency allowed is tcllib (for the json package).
  4. Match the existing style: mongreldb:: namespace, snake/camelCase commands.
  • Mongrel — Commercial multi-system workbench with native MongrelDB support.
  • MongrelDB Viewer — Free, open-source MongrelDB GUI and MCP server.

License

Dual-licensed under the MIT License or the Apache License, Version 2.0, at your option. See MIT OR Apache-2.0 for the full text.

SPDX-License-Identifier: MIT OR Apache-2.0