Queries
July 9, 2026 ยท View on GitHub
The mongreldb_query function pushes conditions down to MongrelDB's native
indexes for sub-millisecond lookups - bitmap, learned-range, FM-index full
text, and more. Each condition type maps to one specialized index; conditions
are AND-ed together.
mongreldb_condition cond = {
.kind = MDB_COND_RANGE, .column_id = 3,
.lo = 100.0, .lo_set = 1, .hi = 500.0, .hi_set = 1,
};
int64_t proj[] = {1, 2};
mongreldb_result res;
mongreldb_query(db, "orders", &cond, 1, proj, 2, 100, &res);
This guide covers every condition type, projection, limits and truncation, and combining conditions.
The basics
Every query call takes the table, an array of conditions, a projection, a limit, and an output result:
| Argument | Purpose |
|---|---|
conditions (or NULL) | An array of native conditions. All are AND-ed. |
projection (or NULL) | Return only these column ids (NULL means all columns). |
limit (or 0) | Cap the number of rows. |
out_result | Receives the rows and a truncated flag. |
The request body the client builds matches the daemon's /kit/query shape:
{
"table": "orders",
"conditions": [{"range": {"column_id": 3, "lo": 100.0, "hi": 500.0}}],
"projection": [1, 2],
"limit": 100
}
Result memory is owned by the client and valid until the next call (or
mongreldb_close()). Copy anything you need to keep.
Condition types
Each mongreldb_condition has a kind and a set of fields. Column references
use the numeric column id, never the column name.
MDB_COND_PK - exact primary-key match
The fastest lookup. Supply the primary-key value as int_value (for integer
PKs) or str_value (for string PKs).
mongreldb_condition cond = {
.kind = MDB_COND_PK, .int_value = 42, .int_set = 1,
};
mongreldb_query(db, "orders", &cond, 1, NULL, 0, 0, &res);
MDB_COND_RANGE - numeric range (learned-range index)
Inclusive bounds. Leave lo_set / hi_set at 0 for an open end.
mongreldb_condition cond = {
.kind = MDB_COND_RANGE, .column_id = 3,
.lo = 100.0, .lo_set = 1,
.hi = 500.0, .hi_set = 1,
};
mongreldb_query(db, "orders", &cond, 1, NULL, 0, 0, &res);
/* Open-ended: amount >= 100 */
mongreldb_condition open_cond = {
.kind = MDB_COND_RANGE, .column_id = 3,
.lo = 100.0, .lo_set = 1,
};
MDB_COND_BITMAP_EQ - equality on a bitmap-indexed column
Best for low-cardinality columns (status, category, booleans).
mongreldb_condition cond = {
.kind = MDB_COND_BITMAP_EQ, .column_id = 2, .str_value = "Alice",
};
mongreldb_query(db, "orders", &cond, 1, NULL, 0, 0, &res);
MDB_COND_IS_NULL / MDB_COND_IS_NOT_NULL - null checks
mongreldb_condition is_null = {
.kind = MDB_COND_IS_NULL, .column_id = 3,
};
mongreldb_condition not_null = {
.kind = MDB_COND_IS_NOT_NULL, .column_id = 3,
};
MDB_COND_FM_CONTAINS - full-text substring search (FM-index)
Substring match within a column. The str_value becomes the on-wire pattern.
mongreldb_condition cond = {
.kind = MDB_COND_FM_CONTAINS, .column_id = 2,
.str_value = "database performance",
};
mongreldb_query(db, "documents", &cond, 1, NULL, 0, 10, &res);
For vector similarity (ann), sparse match, and MinHash similarity, use SQL
or extend the condition kinds - the server supports them on the wire; this
client covers the most common index conditions. See sql.md for the
ones not yet exposed as native helpers.
Projection (column selection)
Pass a projection array to restrict the columns in each returned row. Pass
NULL for all columns. Projecting to only the columns you need cuts bandwidth
and decode cost.
int64_t proj[] = {1, 2}; /* id and customer only */
mongreldb_query(db, "orders", &cond, 1, proj, 2, 100, &res);
Returned cells are decoded into the mongreldb_value tagged union. Check
cell->value.tag to read the right union arm:
for (size_t i = 0; i < res.count; i++) {
for (size_t j = 0; j < res.rows[i].count; j++) {
mongreldb_cell *c = &res.rows[i].cells[j];
switch (c->value.tag) {
case MDB_VAL_INT64: printf("col %lld = %lld\n",
(long long)c->column_id,
(long long)c->value.v.i64); break;
case MDB_VAL_DOUBLE: printf("col %lld = %g\n",
(long long)c->column_id,
c->value.v.f64); break;
case MDB_VAL_STRING: printf("col %lld = %s\n",
(long long)c->column_id,
c->value.v.str); break;
case MDB_VAL_NULL: printf("col %lld = null\n",
(long long)c->column_id); break;
default: break;
}
}
}
Limit and the truncated flag
A non-zero limit caps the result. When the server has more matches than the
limit allows, it returns the first limit and sets truncated to 1.
mongreldb_result res;
mongreldb_query(db, "orders", &cond, 1, NULL, 0, 100, &res);
if (res.truncated) {
/* 100 rows came back but more exist on the server. Either raise the
* limit, page with a range predicate on the PK, or accept the cap. */
}
Multiple AND conditions
Pass an array of conditions. Every condition must match; the server intersects the index results.
/* Customer is Alice AND amount is between 100 and 500. */
mongreldb_condition conds[2];
conds[0].kind = MDB_COND_BITMAP_EQ; conds[0].column_id = 2;
conds[0].str_value = "Alice";
conds[1].kind = MDB_COND_RANGE; conds[1].column_id = 3;
conds[1].lo = 100.0; conds[1].lo_set = 1;
conds[1].hi = 500.0; conds[1].hi_set = 1;
int64_t proj[] = {1, 3};
mongreldb_query(db, "orders", conds, 2, proj, 2, 50, &res);
Because each condition targets a different specialized index, the engine can pick the most selective one to drive the lookup and intersect the rest.
Putting it together
A realistic combined lookup - bitmap equality + range + projection + limit + truncation check:
void top_spenders(mongreldb_client *db, const char *customer) {
mongreldb_condition conds[2];
memset(conds, 0, sizeof(conds));
conds[0].kind = MDB_COND_BITMAP_EQ; conds[0].column_id = 2;
conds[0].str_value = customer;
conds[1].kind = MDB_COND_RANGE; conds[1].column_id = 3;
conds[1].lo = 100.0; conds[1].lo_set = 1;
int64_t proj[] = {1, 3};
mongreldb_result res;
if (mongreldb_query(db, "orders", conds, 2, proj, 2, 50, &res) != MDB_OK) {
return;
}
if (res.truncated) {
fprintf(stderr, "warning: result capped at 50\n");
}
/* ... read res.rows ... */
}
For arbitrary predicates, joins, and aggregations that the native indexes do not cover, use SQL instead - see sql.md.