Pre-1.0 hoardDB is pre-1.0. Expect breaking changes.

hoardDB Query Language (HQL) reference

HQL is what you type at the hoardDB-cli prompt, or pipe into it. This page lists every statement the CLI accepts in this build and what each one prints. Every example on it was run against a real server, and the output shown is what that server printed. Where the CLI refuses a statement, the page quotes the refusal, because a refusal is usually the first thing you meet when you bring habits over from another database.

Each statement is in one of three states:

  • Works: the CLI accepts it and the server serves it. Most of this page.
  • Refused: the CLI or the server turns it down with a message that says what to do instead. These are quoted in full.
  • Planned: not accepted yet. Marked Planned wherever it appears, with a link to Limitations, which says when it is due.

The examples use a database called shop. Output lines are copied from the run; only the prompt differs between a terminal session and a piped one (see The interactive REPL).

Contents

Connect and authenticate

A server started with no configuration writes a root password to data/root.password and listens for clients on port 7433. Run the CLI from the same directory and it reads that file, so this is the shortest path to a prompt:

hoardDB-server &
hoardDB-cli -address 127.0.0.1:7433 -insecure
WARNING: TLS certificate verification is disabled (--insecure); the connection to 127.0.0.1:7433 cannot be trusted and may be intercepted. Use this only against a local development server.
2026/09/26 15:40:10 WARN TLS certificate verification disabled by an explicit insecure trust policy host=127.0.0.1:7433 reason="--insecure flag"
Authenticated as admin
Connected to 127.0.0.1:7433
hoardDB CLI devel
Type 'help' for help, 'exit' to quit.

[h]oardDB>

-insecure skips certificate verification, and the warning says so every time. Use it against a local development server only. Without it, the first connection to a server asks you to confirm the server’s fingerprint and records it in ~/.hoarddb/known_servers:

The authenticity of server '127.0.0.1:7433' cannot be established.
ED25519 key fingerprint is SHA256:00feb48e7331de075a1607916ce804a397440d80c99c0c01b1a23624897d93c8.
Trust this server? [y/N]: y
Authenticated as admin
Connected to 127.0.0.1:7433

A script has nobody to answer that question, so a piped session refuses an unknown server instead of guessing:

The authenticity of server '127.0.0.1:7433' cannot be established.
ED25519 key fingerprint is SHA256:00feb48e7331de075a1607916ce804a397440d80c99c0c01b1a23624897d93c8.
Refusing to trust it non-interactively: pre-populate ~/.hoarddb/known_servers or pass --insecure to bypass.
Failed to connect to 127.0.0.1:7433: connect to 127.0.0.1:7433: dial 127.0.0.1:7433: server 127.0.0.1:7433 not trusted (fingerprint: SHA256:00feb48e7331de075a1607916ce804a397440d80c99c0c01b1a23624897d93c8)

Connect once from a terminal and answer y. Every later run, piped or not, then connects without asking.

Credentials

Every statement needs an authenticated session, so the CLI authenticates when it connects. It takes credentials from, in order: the -user/-password flags, the config file, HOARDB_ROOT_USER/HOARDB_ROOT_PASSWORD, and finally data/root.password in the directory you run it from. Run it from anywhere else with none of those set and it says what to do:

Failed to connect to 127.0.0.1:7433: no credentials
       pass -user <name> -password <password>, or set the environment:
         HOARDB_ROOT_USER=admin HOARDB_ROOT_PASSWORD=<password> hoardDB-cli
       the server's root user comes from HOARDB_ROOT_USER/HOARDB_ROOT_PASSWORD
       when the server is started

A wrong password:

Failed to connect to 127.0.0.1:7433: authenticate as admin: authentication failed (CREDENTIALS): invalid username or password

Flags

FlagWhat it does
-address <host:port>The server’s client port. Default localhost:7433.
-user <name>, -password <pw>Credentials for this session.
-auth-token <token>Resume a session with a token instead of a password (also HOARDB_AUTH_TOKEN).
-insecureSkip certificate verification. Local development only.
-extjsonPrint dates in scan and search results as {"$date": "..."} instead of plain strings.
-theme <path or plain>Prompt theme; plain turns colour off.
-password-file <path>, -password-stdinWhere create user and alter user ... password read a new password in a script.
-config <path>A YAML configuration file.
-debugPrint each parsed statement before running it.
-v, -versionPrint the version and exit without connecting.

There is no flag that runs a single statement. Pipe statements into the CLI instead, as the next section shows.

How statements are read

The CLI reads one statement per line, from the terminal or from standard input:

hoardDB-cli -address 127.0.0.1:7433 -insecure <<'EOF'
use shop
db.Users.count()
EOF

These rules come from the parser, and each one was checked against it:

  • One statement per line. A trailing ; is optional, and so are several of them (db.Users.count();; works). A ; in the middle does not separate statements: use shop; show buckets looks for a database called shop;.
  • A statement cannot span lines. A document broken across lines is refused at the first line: parse: parse args: expected parentheses, got: ({. Write documents on one line.
  • Put the ; straight after the closing parenthesis. db.Users.find({Name: "Ada"}) ;, with a space before the semicolon, is refused as expected parentheses.
  • Keywords and verbs are case-insensitive; names are not. SHOW DATABASES, DB.Users.count() and db.Users.COUNT() all work. db.users.count() looks for a bucket called users and does not find Users.
  • There are no comments. -- and /* */ are refused as parse: unknown command: -- and parse: unknown command: /*. (The CLI’s own help text still lists them; the help text is wrong.)
  • An argument cannot contain (. The parser finds the method call at the last opening parenthesis on the line, so a ( inside an argument breaks the statement. This is why ISODate("...") does not work at the prompt; see Dates.
  • Documents accept relaxed syntax. Strict JSON, unquoted keys, single quotes and a trailing comma are all accepted:
[h]oardDB [shop]> db.Users.insert({"Name": "Grace", "Email": "grace@example.com", "Age": 17, "Status": "trial"})
OK  id=01a0de60-9e4c-77c4-aea1-1274ceba1389
[h]oardDB [shop]> db.Users.insert({Name: 'Linus', Email: 'linus@example.com', Age: 29, Status: 'banned',})
OK  id=01a0de60-9e4c-799e-8bee-d9b6b2dc4d1b

A script stops at the first failing statement. Piped input is non-interactive: the CLI prints CLI error: <reason>, runs nothing after it, and exits with status 1, so a script never carries on from a wrong assumption. At a terminal the same failure prints Error: <reason> and the session continues.

Databases

Works. create database, use, show databases, describe database and drop database.

[h]oardDB> create database shop
OK  created database shop
Next: use shop
Then: create bucket <name> {type: hash | btree | fifo | lifo | heap | blob}   (hash is the default)
[h]oardDB> use shop
Switched to database 'shop'
[h]oardDB [shop]> show databases
DATABASES
---
NAME  BUCKETS  PATH
shop  0        data/shop
[h]oardDB [shop]> describe database shop
DATABASE shop
---
Buckets: 0

show dbs and create db <name> are accepted spellings. Creating a database does not switch to it; run use next, as the hint says. use checks that the database exists, so a typo fails at once instead of at the next statement:

[h]oardDB> use Nope
Error: database "Nope" not found — this server has no databases yet; create one with 'create database Nope'
[h]oardDB> create database shop
Error: database "shop" already exists

drop database <name> deletes the database and every bucket in it.

Refused: a replication factor on a database. The factor belongs to the bucket, and the refusal explains why the clause is not simply ignored:

[h]oardDB> create database app2 with replication_factor 3
Error: parse: replication_factor is not a database-level setting: this clause was accepted and ignored, so database "app2" would have been created with the node-wide factor and no extra copies. The factor belongs to the bucket — run 'create database app2', then 'create bucket <name> { replication_factor: N }'. That per-bucket clause is parsed but the server does not honour it yet; until it does, HOARDB_REPLICATION_FACTOR is what decides how many copies a bucket has

Buckets

A bucket has exactly one store type, chosen when you create it. There are six: hash, btree, fifo, lifo, heap and blob. Store types explains how to choose between them.

create bucket

Works. The braces carry the type and, optionally, the fields to index:

create bucket <name> [in <database>] { type: <store type> [, indices: ["<field>", ...]] }
[h]oardDB [shop]> create bucket Users { type: hash, indices: ["Email"] }
OK  created bucket shop.Users (type hash)
Indexes: [Email]
[h]oardDB [shop]> create bucket Ledger { type: btree }
OK  created bucket shop.Ledger (type btree)
[h]oardDB [shop]> create bucket Jobs { type: fifo }
OK  created bucket shop.Jobs (type fifo)
[h]oardDB [shop]> create bucket Undo { type: lifo }
OK  created bucket shop.Undo (type lifo)
[h]oardDB [shop]> create bucket Alerts { type: heap }
OK  created bucket shop.Alerts (type heap)
[h]oardDB [shop]> create bucket Files { type: blob }
OK  created bucket shop.Files (type blob)

Other forms the CLI accepts:

  • No braces: create bucket Cache makes a hash bucket, the default.
  • No braces, with a type: create bucket Events type fifo.
  • b+tree is accepted as a spelling of btree.
  • in <database> creates the bucket without use: create bucket Remote in shop { type: hash }.

An unknown type is refused, and the refusal lists the valid ones:

[h]oardDB [shop]> create bucket Bad { type: queue }
Error: unknown bucket type "queue" — valid types: hash, btree, fifo, lifo, heap, blob

Indexes are declared at creation and cannot be added later (see alter bucket). An index serves search, which is equality lookup.

Three traps in create bucket, all checked against this build:

  • Creating a bucket that already exists answers OK and changes nothing. create bucket Users { type: fifo } on the existing hash bucket above prints OK created bucket shop.Users (type fifo), and describe bucket Users still says Type: hash, with the same entries and index. Check with show buckets before you rely on a create.
  • replication_factor in the braces is accepted and not honoured. create bucket Rf { type: hash, replication_factor: 3 } answers OK, and the bucket gets the node-wide factor (HOARDB_REPLICATION_FACTOR). The clause is parsed so that it works unchanged once the server honours it.
  • A dotted name is one flat bucket. create bucket Post.Comments { type: fifo } creates a single top-level bucket whose name contains a dot, stored at data/shop/Post.Comments. It is not a sub-bucket of Post. Avoid dots in bucket names: when nested buckets ship (below), a dotted name will be ambiguous.

Planned: fields and per-field store types (nested buckets)

Planned — the CLI does not accept this grammar yet. A bucket that declares its fields, where a field can be a store of its own, is due in v1.0; see Limitations: bucket engines. This is the shape it will take:

-- Planned. Not accepted by this build.
create bucket Post {
    Caption    string,
    Photos     blobStore,
    Comments   fifoStore
};

The six field store types are fifoStore, lifoStore, hashStore, btreeStore, heapStore and blobStore. A store field belongs to each document, not to the bucket: Comments fifoStore on Post means every post has its own comments queue. That is the point of the design, and it is a different thing from a bucket that contains a queue. The nested-buckets specification describes the model in full.

What the current build does if you type it anyway. It does not refuse the statement, and this is the part to know. Written on one line, the statement creates a plain hash bucket and silently drops every declaration:

[h]oardDB [app]> create bucket User { _id_ uuid auto, FirstName string required, Email string unique, Friends heapStore };
OK  created bucket app.User (type hash)
[h]oardDB [app]> describe bucket User
BUCKET app.User
---
Type:     hash
Entries:  0
On disk:  0 B
Indexes:  (none — declare them when you create the bucket)
Path:     data/app/User

Written across lines as above, the first line creates Post as a hash bucket before the second line is refused (parse: unknown command: _id_). In both cases you have a bucket with no fields, no field stores and no constraints. Use the one-type form until nested buckets ship, and drop any bucket you created with this grammar.

show buckets, describe bucket, stats

Works.

[h]oardDB [shop]> show buckets
BUCKETS in shop
---
NAME    TYPE   ENTRIES
Alerts  heap   0
Files   blob   0
Jobs    fifo   0
Ledger  btree  0
Undo    lifo   0
Users   hash   0
[h]oardDB [shop]> describe bucket Users
BUCKET shop.Users
---
Type:     hash
Entries:  0
On disk:  0 B
Indexes:  Email (equality)
Path:     data/shop/Users

show buckets in <database> works without use. db.<bucket>.stats() prints the same facts as describe bucket, without the path. A bucket that does not exist is named in the refusal, with the command to create it:

[h]oardDB [shop]> describe bucket Nope
Error: describing bucket "Nope": server error (1): bucket not found: bucket "Nope" not found in database "shop". Run 'show buckets in shop' to list what exists, or 'create bucket Nope in shop { type: hash }' to create it

drop bucket

Works. Dropping deletes the bucket’s data. The space comes back in the background:

[h]oardDB [shop]> drop bucket Cache
OK  bucket "Cache" dropped from "shop". Its space is reclaimed in the background; watch hoarddb_reclaim_pending_records to follow it.

alter bucket

Refused. A bucket’s type and indexes are fixed at creation. The refusal names the workaround and its cost:

[h]oardDB [shop]> alter bucket Users { indices: ["Name"] }
Error: altering bucket "Users" is not supported yet
       declare indexes when you create the bucket:
         create bucket Users { type: hash, indices: ["Email"] }
       to change indexes on an existing bucket, drop and recreate it —
       note that dropping deletes its data: 'drop bucket Users'

Changing a bucket’s store type in place is planned for v1.1; see Limitations: bucket engines.

define bucket

Refused, by design. define bucket is the old name of create bucket. It is kept as a named error rather than removed, so that old scripts fail loudly:

[h]oardDB [shop]> define bucket Users { type: hash }
Error: parse: define was renamed: use 'create bucket <name> { type: hash }'
(run 'help' for the full command list)

Documents: hash and btree buckets

Every bucket verb has the form db.<bucket>.<verb>(<arguments>) and applies to the database chosen with use. Without one:

[h]oardDB> db.Users.find()
Error: no database selected — run 'use <database>' first ('show databases' lists what exists)

insert and _id_

Works. insert stores a document and prints its key. The key is the _id_ field; leave it out and the server generates a time-ordered UUID:

[h]oardDB [shop]> db.Users.insert({_id_: "ada", Name: "Ada", Email: "ada@example.com", Age: 36, Status: "active"})
OK  id=ada
[h]oardDB [shop]> db.Users.insert({_id_: "alan", Name: "Alan", Email: "alan@example.com", Age: 41, Status: "active"})
OK  id=alan
[h]oardDB [shop]> db.Users.insert({"Name": "Grace", "Email": "grace@example.com", "Age": 17, "Status": "trial"})
OK  id=01a0de60-9e4c-77c4-aea1-1274ceba1389

_id_ must be a string. db.Users.insert({_id_: 7, n: 1}) answers OK with a generated key, and the 7 is not stored anywhere. Write _id_: "7".

Only _id_ is the key on insert. A field called id or key is stored as an ordinary field, and that has a consequence for find, described next.

find: one document by key

Works. A bare key, or a document naming the key, is a point read:

[h]oardDB [shop]> db.Users.find("ada")
{"Age": 36, "Email": "ada@example.com", "Name": "Ada", "Status": "active", "_id_": "ada"}
[h]oardDB [shop]> db.Users.find({_id_: "alan"})
{"Age": 41, "Email": "alan@example.com", "Name": "Alan", "Status": "active", "_id_": "alan"}
[h]oardDB [shop]> db.Users.find("nobody")
NOT FOUND

get is the same point read on hash and btree buckets: db.Users.get("ada") prints the same line.

A filter that names _id_, id, key or _key_ is a point read, and its other fields are ignored. The CLI treats all four names as the key. find({Status: "active", _id_: "ada"}) returns Ada without checking Status. Worse, a document stored with a field called key cannot be filtered on it:

[h]oardDB [shop]> db.Users.insert({_id_: "sku-1", key: "blue", id: 7})
OK  id=sku-1
[h]oardDB [shop]> db.Users.find({key: "blue"})
NOT FOUND

That find looked up a document whose key is blue. Avoid id, key and _key_ as field names.

find: scan with a filter

Works. With no argument, or a filter that names no key field, find scans the bucket and returns up to 100 documents:

[h]oardDB [shop]> db.Users.find()
{"Age": 17, "Email": "grace@example.com", "Name": "Grace", "Status": "trial", "_id_": "01a0de60-9e4c-77c4-aea1-1274ceba1389"}
{"Age": 29, "Email": "linus@example.com", "Name": "Linus", "Status": "banned", "_id_": "01a0de60-9e4c-799e-8bee-d9b6b2dc4d1b"}
{"Age": 36, "Email": "ada@example.com", "Name": "Ada", "Status": "active", "_id_": "ada"}
{"Age": 41, "Email": "alan@example.com", "Name": "Alan", "Status": "active", "_id_": "alan"}
[h]oardDB [shop]> db.Users.find({Status: "active"})
{"Age": 36, "Email": "ada@example.com", "Name": "Ada", "Status": "active", "_id_": "ada"}
{"Age": 41, "Email": "alan@example.com", "Name": "Alan", "Status": "active", "_id_": "alan"}
[h]oardDB [shop]> db.Users.find({Status: "nobody"})
NO MATCHES

find({}) is the same as find(). A filter needs no index: the server walks the bucket in its natural order and applies the filter itself. Hash and btree buckets walk by key ascending; fifo walks oldest first, lifo newest first, and heap highest priority first. On a large bucket that is a walk of the whole bucket, so use search on an indexed field for a hot query. The filter operators are listed under Query and update operators.

A bare value is an exact match. There is no wildcard. find({Name: "A*"}) looks for the literal two characters A* and prints NO MATCHES. Use $regex: find({Name: {$regex: "^A"}}).

Paging, sort and projection

Works. A second document carries skip, limit, sort and projection. When more matches exist than the page holds, the CLI prints the exact total and the next command to run:

[h]oardDB [shop]> db.Users.find({}, {limit: 2})
{"Age": 17, "Email": "grace@example.com", "Name": "Grace", "Status": "trial", "_id_": "01a0de60-9e4c-77c4-aea1-1274ceba1389"}
{"Age": 29, "Email": "linus@example.com", "Name": "Linus", "Status": "banned", "_id_": "01a0de60-9e4c-799e-8bee-d9b6b2dc4d1b"}
showing 2 of 4 — next: find({}, {skip: 2})
[h]oardDB [shop]> db.Users.find({}, {sort: {Age: -1}, limit: 2})
{"Age": 41, "Email": "alan@example.com", "Name": "Alan", "Status": "active", "_id_": "alan"}
{"Age": 36, "Email": "ada@example.com", "Name": "Ada", "Status": "active", "_id_": "ada"}
showing 2 of 4 — next: find({}, {skip: 2})
[h]oardDB [shop]> db.Users.find({Status: "active"}, {projection: {Name: 1}})
{"Name": "Ada"}
{"Name": "Alan"}
[h]oardDB [shop]> db.Users.find({}, {projection: {Email: 0, Age: 0}, limit: 1})
{"Name": "Grace", "Status": "trial", "_id_": "01a0de60-9e4c-77c4-aea1-1274ceba1389"}
showing 1 of 4 — next: find({}, {skip: 1})

A page that is exactly full, with nothing after it, prints no next line. In sort, 1 means ascending and -1 descending. In projection, 1 includes a field and 0 excludes it. An inclusion projection leaves out _id_ unless you name it.

Refused: sorting by more than one field.

[h]oardDB [shop]> db.Users.find({}, {sort: {Age: 1, Name: -1}})
Error: find failed: server error (5): sorting by more than one field is not supported yet: this query sorts by Age, Name. To fix it, sort by one field (for example {sort: {Age: 1}}), or sort the result in your client

sort does not make paging safe. There is no server-side cursor and no snapshot. Each page is a fresh scan that sorts the whole matched set and only then applies skip and limit, so a write between two pages renumbers every document after it. An insert ahead of the page boundary shows you a document twice, and a delete ahead of it hides one entirely. Stop writes before paging through a bucket you must read completely, or read it in one page with a limit above the match count. See Limitations: queries.

update

Works. update takes the key, as a string or as {_id_: ...}, and an operator document. It prints the whole document after the change:

[h]oardDB [shop]> db.Users.update("ada", {$set: {City: "London"}, $inc: {Age: 1}})
OK  updated "ada"
{"Age": 37, "City": "London", "Email": "ada@example.com", "Name": "Ada", "Status": "active", "_id_": "ada"}
[h]oardDB [shop]> db.Users.update({_id_: "ada"}, {$unset: ["City"]})
OK  updated "ada"
{"Age": 37, "Email": "ada@example.com", "Name": "Ada", "Status": "active", "_id_": "ada"}

update changes one document, named by its key. There is no update by filter. A key that does not exist is refused, not created:

[h]oardDB [shop]> db.Users.update("ghost", {$set: {City: "Paris"}})
Error: document "ghost" not found in shop.Users — insert it first or use find to check the key

The operators are $set, $unset and $inc; see update operators for their rules and the ones that are refused.

delete

Works. delete takes one key:

[h]oardDB [shop]> db.Users.delete("sku-1")
OK  deleted "sku-1"

delete answers OK whether or not the key existed. Deleting a key that was never there prints OK deleted "..." too. Use find first if you need to know.

Refused: delete by filter.

[h]oardDB [shop]> db.Users.delete({Status: "banned"})
Error: delete needs a key: db.Users.delete("my-key")

count, length, distinct

Works. count and length are the same verb. Both take an optional filter:

[h]oardDB [shop]> db.Users.count()
4
[h]oardDB [shop]> db.Users.count({Status: "active"})
2
[h]oardDB [shop]> db.Users.distinct("Status")
"trial"
"banned"
"active"
[h]oardDB [shop]> db.Users.distinct("Status", {Age: {$gt: 20}})
"banned"
"active"

search: the indexed fast path

Works, for equality on a declared index. search answers from an index instead of walking the bucket:

[h]oardDB [shop]> db.Users.search({Email: "alan@example.com"})
Found 1 results:
{"Age": 41, "Email": "alan@example.com", "Name": "Alan", "Status": "active", "_id_": "alan"}

On a field with no index, search does not fall back to a scan. The server says so:

[h]oardDB [shop]> db.Users.search({Name: "Ada"})
Error: search failed: field Name is not indexed

That is the server’s reply, not a bug: use find({Name: "Ada"}) to scan, or recreate the bucket with indices: ["Name"]. Operators do not work in search. $in and $gt on an indexed field are refused:

[h]oardDB [shop]> db.Users.search({Email: {$gt: "b"}})
Error: search failed: encoding query value for Email: value type cannot be a btree key or an indexed field: map[string]interface {} (supported: null, bool, number, date, string, binary)

(help operators lists range operators for search. That help text is wrong.)

Query and update operators

Filter operators

These work in find and count filters, and in the filter argument of distinct:

OperatorMeaningExample
bare valueexact match{Status: "active"}
$gt $gte $lt $ltecomparison: numbers numerically, strings in byte order{Age: {$gte: 36}}
$invalue is one of a list{Status: {$in: ["trial", "banned"]}}
$ninvalue is none of a list{Status: {$nin: ["active"]}}
$nenot equal{Status: {$ne: "active"}}
$existsfield present (true) or absent (false){Nick: {$exists: false}}
$regexGo regular expression, unanchored{Name: {$regex: "^A"}}
$andevery condition matches (top level){$and: [{Status: "active"}, {Age: {$gte: 40}}]}
$orany condition matches (top level){$or: [{Name: "Ada"}, {Age: 17}]}

Each row was run against the Users bucket above:

[h]oardDB [shop]> db.Users.find({$and: [{Status: "active"}, {Age: {$gte: 40}}]})
{"Age": 41, "Email": "alan@example.com", "Name": "Alan", "Status": "active", "_id_": "alan"}
[h]oardDB [shop]> db.Users.find({$or: [{Name: "Ada"}, {Age: 17}]})
{"Age": 17, "Email": "grace@example.com", "Name": "Grace", "Status": "trial", "_id_": "01a0de60-9e4c-77c4-aea1-1274ceba1389"}
{"Age": 36, "Email": "ada@example.com", "Name": "Ada", "Status": "active", "_id_": "ada"}
[h]oardDB [shop]> db.Users.find({Status: {$in: ["trial", "banned"]}})
{"Age": 17, "Email": "grace@example.com", "Name": "Grace", "Status": "trial", "_id_": "01a0de60-9e4c-77c4-aea1-1274ceba1389"}
{"Age": 29, "Email": "linus@example.com", "Name": "Linus", "Status": "banned", "_id_": "01a0de60-9e4c-799e-8bee-d9b6b2dc4d1b"}

$regex is unanchored, so {$regex: "o"} matches every value containing an o. Anchor it with ^ and $ to match a whole value.

Refused, by name. Any other operator is an error that lists what is supported, never an empty result. $eq, $not, $all, $elemMatch, $size and $nor are all refused:

[h]oardDB [shop]> db.Users.find({Age: {$eq: 36}})
Error: find failed: server error (5): unsupported filter operator "$eq": this version supports $gt, $gte, $lt, $lte, $in, $nin, $ne, $exists and $regex on a field
[h]oardDB [shop]> db.Users.find({$nor: [{Name: "Ada"}]})
Error: find failed: server error (5): unsupported filter operator "$nor": this version supports $and, $or, $ne, $exists, $nin at the top level

For $eq, write the bare value: {Age: 36}.

Dates

A date is written in its Extended JSON form, {"$date": "<RFC 3339>"}. It is stored as a date, not a string, and compares by instant:

[h]oardDB [shop]> create bucket Events { type: hash }
OK  created bucket shop.Events (type hash)
[h]oardDB [shop]> db.Events.insert({_id_: "e1", ts: {"$date": "2026-02-01T00:00:00Z"}})
OK  id=e1
[h]oardDB [shop]> db.Events.find({ts: {$gt: {"$date": "2026-01-01T00:00:00Z"}}})
{"_id_": "e1", "ts": "2026-02-01T00:00:00Z"}

A date field only matches a date operand. {ts: {$gt: "2026-01-01"}} compares a date with a string and prints NO MATCHES.

Dates print as RFC 3339 strings. With -extjson, dates in find() scans and search results print as {"$date": "..."} instead, which keeps their type when you copy the output back in. A point read, find("<key>"), prints the plain string with or without the flag.

Refused at the prompt: ISODate("..."), new Date("...") and now(). The document parser understands them, but each contains a ( inside the argument, which the statement parser cannot handle (see How statements are read). The statement fails before it reaches the server:

[h]oardDB [shop]> db.Events.find({ts: {$gt: ISODate("2026-01-01")}})
Error: unknown bucket method "find({ts: {$gt: isodate" — run 'help' for the supported operations
[h]oardDB [shop]> db.Events.insert({_id_: "now", at: now()})
Error: unknown bucket method "insert({_id_: \"now\", at: now" — run 'help' for the supported operations

Use {"$date": "..."} until this is fixed. There is no server-clock function you can use from the prompt.

Update operators

OperatorFormRule
$set{$set: {field: value}}sets one or more fields
$unset{$unset: ["field", ...]}a list of field names
$inc{$inc: {field: N}}adds a whole number

$unset takes a list, not a document:

[h]oardDB [shop]> db.Users.update("ada", {$unset: {City: ""}})
Error: $unset needs a list of field names: {$unset: ["a", "b"]}

$inc truncates a fraction without saying so. {$inc: {Age: 1.5}} answers OK and adds 1. Pass whole numbers.

Refused: every other update operator, and upsert. The refusal lists the three that work:

[h]oardDB [shop]> db.Users.update("ada", {$push: {Tags: "x"}})
Error: $push is not supported — supported update operators: $set, $unset, $inc
[h]oardDB [shop]> db.Users.update("ada", {$set: {City: "Paris"}}, {upsert: true})
Error: upsert is not supported — supported update operators: $set, $unset, $inc

(help operators also lists $push, $addToSet, $mul, $min, $max and $rename as update operators. They are refused, as above; the help text is wrong.)

Queues: fifo and lifo

Works. A fifo bucket returns the oldest entry first and a lifo bucket the newest. Both use the same verbs:

[h]oardDB [shop]> db.Jobs.push({task: "resize", img: "a.jpg"})
OK  id=01a0de61-859c-78cc-ab71-0306b893ec9f
[h]oardDB [shop]> db.Jobs.push({task: "resize", img: "b.jpg"})
OK  id=01a0de61-85d6-7146-8d62-ff10337a3d1d
[h]oardDB [shop]> db.Jobs.length()
2
[h]oardDB [shop]> db.Jobs.peek()
{"_id_": "01a0de61-859c-78cc-ab71-0306b893ec9f", "img": "a.jpg", "task": "resize"}
[h]oardDB [shop]> db.Jobs.pop()
{"_id_": "01a0de61-859c-78cc-ab71-0306b893ec9f", "img": "a.jpg", "task": "resize"}
[h]oardDB [shop]> db.Jobs.pop()
{"_id_": "01a0de61-85d6-7146-8d62-ff10337a3d1d", "img": "b.jpg", "task": "resize"}
[h]oardDB [shop]> db.Jobs.pop()
EMPTY

The same verbs on a lifo bucket return the newest entry first:

[h]oardDB [shop]> db.Undo.push({op: "type", text: "hello"})
OK  id=01a0de61-8864-705e-a7f7-111cacd00a0b
[h]oardDB [shop]> db.Undo.push({op: "delete", text: "o"})
OK  id=01a0de61-889c-7185-8dd9-a99c12b3a9a2
[h]oardDB [shop]> db.Undo.pop()
{"_id_": "01a0de61-889c-7185-8dd9-a99c12b3a9a2", "op": "delete", "text": "o"}

find() lists a queue without consuming it, in pop order, and insert on a queue is the same as push.

Refused: peek(N), and get on a queue.

[h]oardDB [shop]> db.Jobs.peek(2)
Error: peek(N) is not supported: the server returns one item per peek
       walk the queue with db.Jobs.peek() and db.Jobs.pop()
[h]oardDB [shop]> db.Jobs.get("x")
Error: db.Jobs.get() is not defined for fifo buckets — they are read by position:
       db.Jobs.peek()  shows the next entry without removing it
       db.Jobs.pop()   removes and returns the next entry

Heap

Works. A heap bucket returns the highest priority first. priority is a whole number that orders the entry; it is not stored in the document:

[h]oardDB [shop]> db.Alerts.push({priority: 5, msg: "High CPU", host: "node-2"})
OK  id=01a0de61-89c0-7d7c-8b50-9ff40ccfa833
[h]oardDB [shop]> db.Alerts.push({priority: 10, msg: "Disk full", host: "node-1"})
OK  id=01a0de61-89f9-7e08-bf1e-80403a1448cc
[h]oardDB [shop]> db.Alerts.push({priority: 8, msg: "Slow peer", host: "node-3"})
OK  id=01a0de61-8a2e-7143-a76f-e931773c5231
[h]oardDB [shop]> db.Alerts.peek()
{"_id_": "01a0de61-89f9-7e08-bf1e-80403a1448cc", "host": "node-1", "msg": "Disk full"}
[h]oardDB [shop]> db.Alerts.pop()
{"_id_": "01a0de61-89f9-7e08-bf1e-80403a1448cc", "host": "node-1", "msg": "Disk full"}
[h]oardDB [shop]> db.Alerts.pop()
{"_id_": "01a0de61-8a2e-7143-a76f-e931773c5231", "host": "node-3", "msg": "Slow peer"}

A push with no priority is accepted at priority 0. length(), count() and find() work as on a queue. get is refused, as on a queue: db.Alerts.get() is not defined for heap buckets — use db.Alerts.peek() for the highest-priority entry, or db.Alerts.pop() to remove it.

B-tree ranges

Works. A btree bucket keeps its keys in order. insert, find, get, update, delete, count and the filters all work as on a hash bucket. It adds range, first and last:

[h]oardDB [shop]> db.Ledger.insert({_id_: "2026-01-03", amount: 120})
OK  id=2026-01-03
[h]oardDB [shop]> db.Ledger.insert({_id_: "2026-01-01", amount: 40})
OK  id=2026-01-01
[h]oardDB [shop]> db.Ledger.insert({_id_: "2026-01-02", amount: 75})
OK  id=2026-01-02
[h]oardDB [shop]> db.Ledger.insert({_id_: "2026-02-10", amount: 5})
OK  id=2026-02-10
[h]oardDB [shop]> db.Ledger.range({$gte: "2026-01-01", $lte: "2026-01-31"})
3 entries in range:
  2026-01-01           {"_id_": "2026-01-01", "amount": 40}
  2026-01-02           {"_id_": "2026-01-02", "amount": 75}
  2026-01-03           {"_id_": "2026-01-03", "amount": 120}
[h]oardDB [shop]> db.Ledger.range({$gte: "2026-02-01"})
1 entry in range:
  2026-02-10           {"_id_": "2026-02-10", "amount": 5}
[h]oardDB [shop]> db.Ledger.first()
first: 2026-01-01
{"_id_": "2026-01-01", "amount": 40}
[h]oardDB [shop]> db.Ledger.last()
last: 2026-02-10
{"_id_": "2026-02-10", "amount": 5}

The bounds of range apply to the key, and are written at the top level: {$gte: low, $lte: high}. Either one may be left out. Both are inclusive. Keys compare as strings, which is why dates written as YYYY-MM-DD sort correctly. A document inserted without an _id_ gets a generated key, and that key sorts among yours.

The field form, which names a field before the bounds, is refused:

[h]oardDB [shop]> db.Ledger.range({_id_: {$gte: "2026-01-01"}})
Error: range needs bounds: db.Ledger.range({$gte: "a", $lte: "z"})

To order by a field that is not the key, use find with that field in sort, or make that field the key. range, first and last on a bucket that is not a btree are refused with the bucket’s actual type:

[h]oardDB [shop]> db.Users.range({$gte: "a"})
Error: range failed: server error (5): bucket "Users" is type hash; this operation requires type btree

Blob

Works. A blob bucket stores a payload and its metadata under a key. put takes the payload from the data field; every other field except the key is stored as metadata:

[h]oardDB [shop]> db.Files.put({_id_: "hello.txt", data: "hello, world", type: "text/plain"})
OK  key=hello.txt  blob=01a0de61-f88c-730a-b5c6-a00262986153  12 B
[h]oardDB [shop]> db.Files.get("hello.txt")
Key:    hello.txt
Blob:   01a0de61-f88c-730a-b5c6-a00262986153
Size:   12 B
Metadata: {"type": "text/plain"}
Data:
hello, world
[h]oardDB [shop]> db.Files.info("hello.txt")
Key:      hello.txt
Blob:     01a0de61-f88c-730a-b5c6-a00262986153
Size:     12 B
Metadata: {"type": "text/plain"}
[h]oardDB [shop]> db.Files.get({_id_: "hello.txt", out: "hello-copy.txt"})
OK  wrote hello-copy.txt (12 B)
[h]oardDB [shop]> db.Files.delete("hello.txt")
OK  deleted "hello.txt"
  • get prints the payload when it is text. For binary content it prints the size and asks you to re-run with {out: "file"}.
  • out writes the file on the machine running the CLI, relative to the directory you started it in, readable only by you (mode 0600).
  • Without data, the whole document is stored as the payload, as JSON. With no _id_, a key is generated.
  • find("<key>") on a blob bucket returns the metadata as a document, and count() counts the keys.

Refused: scanning a blob bucket, and streaming.

[h]oardDB [shop]> db.Files.find()
Error: db.Files.find() does not scan blob buckets: a blob's value is binary content, not a document. Use db.Files.info("<key>") for one key's metadata, or db.Files.get("<key>") to read the content.
[h]oardDB [shop]> db.Files.stream({_id_: "hello.txt"})
Error: streaming is not available yet
       the transport defines OpStreamOpen but the server does not implement
       chunked reads. Read the whole blob instead:
         db.Files.get({_id_: "key"})              print it
         db.Files.get({_id_: "key", out: "f.bin"}) write it to a file

A single request is capped at 32 MiB, so a larger payload has to be split before you put it. A dump does not capture blob payloads yet; see Backup and restore.

Verbs that are not what they look like

Each of these was run against this build. Most of them are also covered in the section for their verb.

  • push, pop and peek are the same three requests for every store type. The CLI always sends the queue requests, and the server decides by the bucket’s type. That is why one set of verbs serves fifo, lifo and heap. On other types the result varies. push on a hash bucket is an ordinary insert (OK id=...), while pop and peek on a hash bucket print EMPTY even when it holds documents. push on a blob bucket prints ERR push not supported for bucket type blob. In a script that refusal does not stop the run, and the exit status stays 0.
  • get depends on the store type. On hash and btree it is a point read, the same as find("<key>"). On blob it returns the payload. On fifo, lifo and heap it is refused with the verb that works there.
  • search never scans. On a field without an index it answers field X is not indexed rather than walking the bucket. That is the server’s reply, not a bug. find is the verb that scans.
  • A filter naming _id_, id, key or _key_ is a point read. Its other fields are ignored. See find: one document by key.
  • define bucket is a rename error, kept on purpose so old scripts fail loudly. Use create bucket.
  • create bucket on an existing name answers OK and changes nothing.
  • A dotted bucket name is one flat bucket, not a sub-bucket.
  • help <topic> prints the general help. The topic word is dropped, so help hash prints the same page as help.
  • get-token and rotate-token act on the machine you run the CLI on. They read and rewrite the cluster.token file in the CLI’s data directory (by default ./data). They do not ask the server.

Users and roles

User changes are served by the cluster’s master node, which a single server is for itself. Roles are read, readWrite, dbAdmin, userAdmin, clusterAdmin, backup, restore and root. Each is bound to a database as role@database; a role with no database means every database. What each role grants, and how changes reach open sessions, are covered in User management.

create user

Works. A password is never part of the statement. At a terminal the CLI asks twice, without echo:

[h]oardDB> create user alice roles [readWrite@shop, read@app]
New password:
Repeat new password:
user "alice" created (config version 2)

In a script, start the CLI with -password-file <path> (a file readable only by you) or -password-stdin, and it reads the new password from there instead of asking. create user <name> with no roles creates a user who can do nothing until granted a role.

Refused: anything after the name other than roles [...], which includes a password or a braced options document:

[h]oardDB> create user bob { roles: ["readWrite@shop"] }
Error: parse: usage: create user <name> roles [role@db, ...]

Read users back

Works. user list and show users are the same. Neither ever prints a password hash:

[h]oardDB> user list
USERS (master: dalek-dev at 0.0.0.0:4433, config version 3)
---
admin: enabled, roles [root@*], epoch 0
alice: enabled, roles [read@app, readWrite@shop], epoch 0
carol: enabled, roles [(none)], epoch 0
[h]oardDB> user describe alice
user:  alice
status: enabled
roles:  [read@app, readWrite@shop]
epoch:  0
created: 2026-09-26T15:42:59Z
password changed: 2026-09-26T15:42:59Z

master prints the node that serves user changes. On a single server it is that server, here named after its host: cluster master for user changes: dalek-dev at 0.0.0.0:4433.

grant and revoke

Works. Each adds or removes one grant. The word role is optional:

[h]oardDB> grant role dbAdmin@shop to alice
granted dbAdmin@shop to "alice" (config version 4)
[h]oardDB> revoke role read@app from alice
revoked read@app from "alice" (config version 5)

Refused: the access form, and a role with an empty name.

[h]oardDB> grant access to "shop" for "alice"
Error: the 'access' form is not supported; grant a role instead:
  grant role readWrite@shop to alice
[h]oardDB> grant role @shop to alice
Error: parse: role "@shop" must be <role>@<database> or a bare <role>

alter user, disable, enable, drop

Works.

alter user <name> password                   -- prompted twice, or -password-file / -password-stdin
alter user <name> disable                    -- also: disable user <name>
alter user <name> enable                     -- also: enable user <name>
alter user <name> roles [role@db, ...]       -- replaces the whole role set
drop user <name>
[h]oardDB> alter user alice disable
user "alice" altered (config version 7)
[h]oardDB> enable user alice
user "alice" altered (config version 8)
[h]oardDB> drop user carol
user "carol" dropped
[h]oardDB> drop user carol
Error: dropping user "carol": server error (5): drop user "carol": user not found

alter user <name> roles [...] lowercases the role names in this build, and a lowercased role grants nothing. alter user alice roles [readWrite@shop] answers altered and stores readwrite@shop. alice is then refused:

Error: find failed: server error (5): permission denied: user "alice" lacks "read" on "shop"
       alice's roles: readwrite@shop
       ask an administrator to run: grant role read@shop to alice

Until this is fixed, change roles with grant and revoke, which keep the name as you typed it. For the same reason, use a single-case role such as read or root if you do use alter user ... roles.

Refused: alter user <name> databases [...]. Database access is part of the role (readWrite@shop), so there is nothing separate to alter:

[h]oardDB> alter user alice databases ["shop"]
Error: usage: alter user <name> disable|enable|password|roles [role@db, ...]

Cluster and administration

status

Works. The node’s identity, its counts, and a capacity row per resource:

[h]oardDB> status
STATUS
---
Node ID:      n1
Version:      0.1.0
Uptime:       11s
Buckets:      1
Entries:      1
Healthy:      true
Capacity:
  fds         0.00  threshold 0.80  ok
  mappings    0.00  threshold 0.80  ok
  memory      0.00  threshold 0.80  ok
  disk        0.33  threshold 0.85  ok

What the capacity rows mean is covered in Is this node full?.

Cluster verbs

The examples in this section ran on a three-node cluster on one host, each node on its own ports. How to form a cluster is covered in Replication and Configuration.

nodes: works.

[h]oardDB> nodes
NODES
---
  n1                    127.0.0.1:14433            active      2026-09-26 15:44:57
  n2                    127.0.0.1:14443            active      2026-09-26 15:44:59

nodes prints the node records the node you are connected to holds. It is not the committed membership: right after a remove-node, a node can still list the removed member. The server log line cluster membership change committed is the authority.

join <address>: works on a cluster member. It adds a running node and starts rebalancing. Name the node as the cluster’s seed list does (id@host:port):

[h]oardDB> join n3@127.0.0.1:14453
Added n3@127.0.0.1:14453. added n3; cluster now at epoch 2 (data rebalancing)

remove-node <node-id>: works on a cluster member. Run it from a node other than the one you are removing:

[h]oardDB> remove-node n3
Removed n3. removed n3; cluster now at epoch 3. Migrate the re-homed buckets to their new owners ('migrate <database> <bucket>' on each new owner)

hoardDB never removes a node on its own. See Limitations: cluster.

migrate <database> <bucket>: works on a cluster member. It pulls a bucket onto the node you are connected to, when that node is its owner:

[h]oardDB> migrate app Orders
bucket "Orders" in "app" is now served by n1

consistent-get <database> <bucket> <key>: works. It reads the freshest copy across a quorum of the bucket’s holders, and repairs a stale copy on the way. It prints the document as a list of key and value pairs, not in the one-line form find uses:

[h]oardDB> consistent-get app Orders o-1
[
  {
    "Key": "total",
    "Value": 42
  },
  {
    "Key": "_id_",
    "Value": "o-1"
  }
]

A copy that was repaired is marked (repaired), and a missing key prints not found.

write-concern <one|majority|all>: works. It sets how many copies must confirm each write for the rest of the session:

[h]oardDB [app]> write-concern all
Write concern set to all for subsequent writes.
[h]oardDB [app]> db.Orders.insert({_id_: "o-1", total: 42})
OK  id=o-1
[h]oardDB [app]> write-concern two
Error: unknown write concern "two" — use one, majority, or all

On a single server, join, remove-node and migrate are refused by the server, and the refusal says why:

[h]oardDB> join 127.0.0.1:59999
Error: join "127.0.0.1:59999": server error (5): replication is not enabled on this node; the node must be a cluster member to add another
[h]oardDB> migrate shop Users
Error: migrate shop Users: server error (5): replication is not enabled on this node; a standalone node has no bucket to migrate

(The CLI’s help lists join and remove-node as “not available yet”. They are available on a cluster; the help text is out of date.)

connect, get-token, rotate-token

  • connect <host:port> moves the session to another server and authenticates again. A failed connect leaves the session with no connection at all, not on the server it was on. The next statement answers not connected to server, so connect again to the address you came from.
  • get-token prints the cluster token from the cluster.token file in the CLI’s data directory. rotate-token writes a new one there. Both work on the local file; see Verbs that are not what they look like.
[h]oardDB> rotate-token
Old token: 22f6d9ae1f5cb855cc75f46ee29d6cbd746713d5a7d571ae67218803f6fa757d
New token: 0e77be7f1680837c2717aed272e0acb668ef4c59638e19876fcf20b15bc192c9
Note: The old token is valid for 1 hour during transition.

dump and restore

Works, both as a statement at the prompt and as a command of its own. Flags come before the database and bucket names, because flag parsing stops at the first name:

[h]oardDB> dump --out ./dump-shop shop
dumping shop/Alerts...
  shop/Alerts: 2 documents, 314 bytes, sha256:7481de010b1c99cc6827720fb30c1887d885e6e64638b8ebb3edce10fdce6888
(one pair of lines per bucket, then a warning that blob payloads are not captured)
dump: 1 database(s), 7 bucket(s), 17 document(s), 1780 byte(s)
[h]oardDB> restore --dir ./dump-shop --dry-run
(the same blob warning)
  shop/Alerts: would merge into existing bucket shop/Alerts (type heap)
(one line per bucket)
restore --dry-run: 1 database(s), 7 bucket(s), 0 document(s) applied, 0 skipped

The lines in parentheses stand in for output cut here for length.

hoardDB-cli dump --out ./dump-one --insecure --json shop Users
{"ok":true,"databases":["shop"],"buckets":1,"documents":5,"users_recreated":0,"users_awaiting_password":0,"bytes":507,"errors":null}

Written the other way round, dump shop --out ./dump-shop is refused with hoardDB dump [database [bucket]] [flags] — too many positional arguments. Every flag, and what a dump does and does not capture, is covered in Backup and restore.

The interactive REPL

Started from a terminal, the CLI is an interactive session with line editing, history and Tab completion of verbs and bucket names (db.Us then Tab becomes db.Users). The prompt shows the current database:

[h]oardDB> use shop
Switched to database 'shop'
[h]oardDB [shop]> db.Users.count()
4
[h]oardDB [shop]> bogus
Error: parse: unknown command: bogus
[h]oardDB [shop]> exit
Goodbye.
StatementWhat it does
helpThe command summary. help <topic> prints the same summary; see above.
versionhoardDB version=devel commit= build_date= protocol=1, without asking the server.
themeThe prompt theme in use, where it came from, and whether colour is on.
exit, quitEnd the session (Goodbye.). Ctrl-D also ends it. Ctrl-C abandons the line you are typing and keeps the session.

An unknown statement is refused as parse: unknown command: <word>, an unknown bucket verb as unknown bucket method "<verb>" — run 'help' for the supported operations, and a verb without parentheses as parse: expected parentheses for method call.

The prompt is coloured on a terminal. Colour is off when output goes to a pipe, when NO_COLOR is set, when TERM=dumb, or with -theme plain. Your own theme goes in ~/.hoarddb/theme.yml; see Configuration: prompt themes.

Configuration as environment variables

The CLI itself reads these:

VariableUsed for
HOARDB_ROOT_USER, HOARDB_ROOT_PASSWORDCredentials when -user/-password are not given.
HOARDB_AUTH_TOKENA session token instead of a password (-auth-token).
HOARDB_CLUSTER_TOKENThe cluster token for cluster-management verbs (-token).
HOARDB_THEMEThe prompt theme: a path, or plain.
NO_COLOR, TERM=dumbTurn the prompt’s colour off.

These server settings decide what the statements on this page do:

VariableDefaultEffect on HQL
HOARDB_TCP_LISTEN0.0.0.0:7433The address -address connects to.
HOARDB_DATA_DIR./dataWhere databases, buckets and root.password live; the Path in describe.
HOARDB_REPLICATION_FACTOR1How many copies every bucket has. replication_factor in create bucket does not change it yet.
HOARDB_WRITE_CONCERNset by the server; the cluster above logged majorityThe default that write-concern overrides for a session.
HOARDB_SEED_NODES, HOARDB_SEED_FINGERPRINTS, HOARDB_NODE_IDnoneCluster membership, which nodes, join and migrate need.

Every setting, with its YAML key, is in Configuration.

Where each statement is accepted

For contributors and for anyone checking this page against a build. The parser entry point is cli/parser.go:ParseCommand, and bucket verbs are dispatched by cli/handlers.go:handleBucket.

StatementParsed byHandled by
use, show, describecli/parser.go:parseUse, parseShow, parseDescribecli/handlers.go:handleUse, handleShow, handleDescribe
create database, drop databasecli/parser.go:parseCreate, parseDrop; refuseDatabaseReplicationFactorcli/handlers.go:handleCreateDatabase, handleDropDatabase
create bucketcli/parser.go:parseCreateBucketcli/handlers.go:handleCreateBucket, parseBucketSchema; types from cli/store_types.go:storeTypes
drop bucketcli/parser.go:parseDropcli/handlers.go:handleDropBucket
alter bucket (refused)cli/parser.go:parseAltercli/unsupported_features.go:errAlterBucket
define (refused)cli/parser.go:ParseCommandtested by cli/create_bucket_test.go:TestDefineBucketIsARenameError
db.<bucket>.<verb>(...)cli/parser.go:parseBucketMethod, parseArgs; documents by cli/documents.go:parseDocumentcli/handlers.go:handleBucket
insertcli/handlers.go:handleInsert
find, getcli/handlers.go:handleFind, planFind, handleGet; key names in cli/documents.go:keyFieldNames
filter operatorsserver/scan_filter.go:matchesFilter, evalOperator
updatecli/handlers.go:handleUpdate, applyUpdateOperators
deletecli/handlers.go:handleDelete
count, length, distinctcli/handlers.go:handleCount, handleDistinct
searchcli/handlers.go:handleSearch; store/index_manager.go:IndexManager.Search
push, pop, peekcli/handlers.go:handlePush, handlePop, handlePeek; server/service.go:Server.PushCtx
range, first, lastcli/handlers.go:handleRange, handleEdge
put, info, blob getcli/handlers.go:handleBlobPut, handleBlobInfo, handleBlobContent
stream (refused)cli/unsupported_features.go:errStreamUnsupported
statscli/handlers.go:handleBucketStats
create usercli/parser.go:parseCreateUsercli/user_commands.go:handleUserCreate
alter user, disable, enablecli/parser.go:parseAlter, parseDisableEnablecli/user_commands.go:handleUserAlter
drop usercli/parser.go:parseDropcli/user_commands.go:handleUserDrop
grant, revokecli/parser.go:parseGrant, parseRevoke, parseRoleSpeccli/user_commands.go:handleUserGrant, handleUserRevoke
user list, user describe, show users, mastercli/parser.go:parseUser, parseShowcli/user_commands.go:handleUserList, handleUserDescribe, handleMaster
status, nodescli/parser.go:ParseCommandcli/handlers.go:handleStatus, handleNodes
join, remove-node, migratecli/parser.go:parseJoin, parseRemoveNode, parseMigratecli/handlers.go:handleJoin, handleRemoveNode, handleMigrate
consistent-get, write-concerncli/parser.go:parseConsistentGet, parseWriteConcerncli/handlers.go:handleConsistentGet, handleWriteConcern
connectcli/parser.go:parseConnectcli/handlers.go:handleConnect
get-token, rotate-tokencli/parser.go:ParseCommandcli/handlers.go:handleGetToken, handleRotateToken
dump, restorecli/parser.go:ParseCommandcli/dump.go:handleDump, handleRestore; standalone RunDumpCommand, RunRestoreCommand
help, version, theme, exitcli/parser.go:ParseCommandcli/handlers.go:handleHelp; cli/cli.go:handle, printTheme

hoardDB is an independent implementation. HQL’s syntax is JSON-shaped, its wire protocol is its own, and it is not a compatibility layer for any other database. No other database’s tools connect to hoardDB.

Source: docs/user/hql-spec.md in the repository.