MongoDB driver for bnl — pure bnl, no native deps. Implements the
MongoDB OP_MSG wire protocol directly over net + tls + crypto, so it
runs anywhere bnl runs.
bpm install mongodbimport "mongodb" as mongo;
function main() {
var client = wait mongo.connect({
host: "127.0.0.1",
port: 27017,
user: "appuser",
password: "secret"
});
var users = client.db("app").collection("users");
var ins = wait users.insert_one({name: "Alice", age: 30});
print("inserted:", ins.inserted_id);
var alice = wait users.find_one({name: "Alice"});
print("found:", alice.name, alice.age);
var rows = wait users.find({age: {"$gte": 18}});
print("adults:", rows.length);
wait users.update_one({name: "Alice"}, {"$inc": {age: 1}});
var n = wait users.count_documents({});
print("count:", n);
client.close();
}
main();
Every method on client / db / collection returns a Future — pair
with wait.
Accepts either a mongodb:// URI string or an opts map.
// URI form
var client = wait mongo.connect("mongodb://root:secret@127.0.0.1:27017/myapp?authSource=admin");
// Opts form (equivalent)
var client = wait mongo.connect({
host: "127.0.0.1",
port: 27017,
user: "root",
password: "secret",
database: "myapp",
auth_source: "admin"
});
URI: mongodb://[user[:password]@]host[:port][/db][?param=value&…]
| URI query param | Maps to | Notes |
|---|---|---|
authSource |
auth_source |
falls back to the path /db if absent, then "admin" |
tls / ssl |
ssl |
accepts true / 1 / yes |
tlsAllowInvalidCertificates |
ssl_verify=false |
for self-signed dev servers |
mongodb+srv:// (Atlas SRV discovery) is not supported in v0.1 — resolve
the SRV record yourself and pass the explicit host:port. Multiple hosts
(replica set) parse but only the first is used.
Opts map keys:
| Key | Type | Default |
|---|---|---|
host |
string | "127.0.0.1" |
port |
number | 27017 |
user |
string | none (omit for unauthenticated mongod) |
password |
string | none (required when user is set) |
auth_source |
string | "admin" |
ssl |
bool | false |
ssl_verify |
bool | true |
Resolves to a client once the SCRAM-SHA-256 handshake completes.
| Method | Returns | Notes |
|---|---|---|
client.db(name) |
db | namespace handle |
client.run_command(db_name, cmd) |
Future<reply> |
raw command for admin / unsupported ops |
client.ping() |
Future<reply> |
{ping: 1} against admin |
client.server_info() |
map | the hello reply (server version, max wire version, …) |
client.close() |
— | tear down the socket |
var db = client.db("app");
| Method | Returns |
|---|---|
db.collection(name) |
collection |
db.run_command(cmd) |
Future<reply> |
db.list_collections() |
Future<list of map> (firstBatch) |
db.drop() |
Future<reply> |
var users = db.collection("users");
| Method | Returns | Notes |
|---|---|---|
insert_one(doc) |
Future<{inserted_id, n}> |
auto-generates _id ObjectId if absent |
insert_many(docs) |
Future<{inserted_ids, n}> |
same |
find(filter?, opts?) |
Future<list of map> |
drains the cursor (getMore loop) |
find_one(filter?, opts?) |
Future<map | null> |
first match or null |
update_one(filter, update) |
Future<{matched, modified}> |
|
update_many(filter, update) |
Future<{matched, modified}> |
|
replace_one(filter, replacement) |
Future<{matched, modified}> |
|
delete_one(filter) |
Future<{deleted}> |
|
delete_many(filter) |
Future<{deleted}> |
|
count_documents(filter?) |
Future<number> |
|
aggregate(pipeline, opts?) |
Future<list of map> |
drains the cursor |
drop() |
Future<reply> |
opts (when accepted) recognises: projection, sort, limit, skip, batch_size.
Same opts as connect, plus max (default 10). Exposes:
var pool = mongo.pool({host: "...", user: "...", password: "...", max: 20});
wait pool.run(function (client) {
return client.db("app").collection("users").insert_one({name: "x"});
});
pool.size();
pool.idle_size();
pool.close();
run(fn) acquires a client, hands it to fn, releases on settle.
| Function | Returns |
|---|---|
mongodb.new_object_id() |
{$oid: "<24-hex>"} ready to drop into documents |
mongodb.encode_bson(doc) |
raw BSON bytes (mostly useful for debugging) |
mongodb.decode_bson(buf, off?) |
[doc, end_offset] |
mongodb.version() |
"0.1.0" |
Most bnl values map naturally to BSON. Special types use a marker map convention (MongoDB Extended JSON) so they survive a round trip:
| bnl | BSON |
|---|---|
null |
null |
true / false |
bool |
| integer-valued number, |n| < 2^31 | int32 |
| integer-valued number, 2^31 ≤ |n| ≤ 2^53 - 1 | int64 |
| non-integer number | double (IEEE-754 LE, encoded in pure bnl) |
| string | utf8 |
| map | document |
| list | array |
{$oid: "<24-hex>"} |
ObjectId |
{$date: ms} |
UTC datetime |
{$binary: bytes, $subtype: n} |
binary (subtype defaults to 0) |
{$regex: "...", $options: "..."} |
regex |
{$timestamp: {t: secs, i: incr}} |
timestamp |
{$minKey: 1} / {$maxKey: 1} |
MinKey / MaxKey |
{$numberLong: "<digits>"} |
int64 (for values outside safe-int range) |
Int64 values whose absolute value exceeds 2^53 - 1 are decoded as
{$numberLong: "..."} so precision survives — bnl numbers are doubles.
Server errors reject with a structured map:
try {
wait users.insert_one(...);
} catch (e) {
if (type(e) == "map" and e.code == 11000) {
print("duplicate key:", e.message);
} else {
throw e;
}
}
| Field | Notes |
|---|---|
code |
Mongo numeric error code (e.g. 11000 duplicate key) |
codeName |
Symbolic name ("DuplicateKey") when sent |
message |
Server's errmsg |
Client-side errors (TCP failure, malformed BSON, etc.) reject with a
string prefixed "mongo: ".
MIT.