Query & update language
LioranDB's embedded query engine uses plain JavaScript objects.
Text search ($text)
Text search is available when you create one or more text indexes (see Indexes & explain).
Use $text at the top level of your filter:
// minimal form (search all text-indexed fields)
{ $text: "hello world" }
or:
{
$text: {
$search: "hello world",
// optional: restrict to these text-indexed fields
$fields: ["title", "body"],
}
}
Notes:
$textis used as a candidate pre-filter and then the rest of the query runs normally.- When
$fieldsis provided, all listed fields must have a text index.
Dates and timestamps
LioranDB documents are persisted as JSON. In Node.js, if you insert a Date, JSON.stringify() converts it to an ISO string.
Recommendations:
- Prefer storing timestamps as
number(epoch milliseconds) usingDate.now()for easy$gte/$lterange queries. - If you store ISO strings, range queries compare strings (lexicographic), so always use full ISO-8601 format.
Query basics
- A query is usually an object:
{ field: value } - Keys can be dot-paths:
"profile.age","stats.logins" - Multiple keys mean "AND" (all conditions must match)
Equality
- TypeScript
- JavaScript
{ email: "a@b.com" }
{ email: "a@b.com" }
Sandbox output (example)
Matches docs where doc.email === "a@b.com"
Dot-paths
- TypeScript
- JavaScript
{ "profile.age": 21 }
{ "profile.age": 21 }
Sandbox output (example)
Matches docs where doc.profile?.age === 21
Operators (for a field x)
Supported operators:
$gt,$gte$lt,$lte$ne,$eq$in(array membership)
Range
- TypeScript
- JavaScript
{ age: { $gte: 18, $lt: 65 } }
{ age: { $gte: 18, $lt: 65 } }
Sandbox output (example)
Matches 18 <= doc.age < 65
$in
- TypeScript
- JavaScript
{ status: { $in: ["active", "trial"] } }
{ status: { $in: ["active", "trial"] } }
Sandbox output (example)
Matches docs where status is one of "active" or "trial"
Combine multiple fields (implicit AND)
- TypeScript
- JavaScript
{ plan: "pro", "profile.age": { $gte: 18 } }
{ plan: "pro", "profile.age": { $gte: 18 } }
Sandbox output (example)
Matches docs that satisfy both conditions
Functional queries
You can also pass a function instead of an object:
- TypeScript
- JavaScript
(doc) => doc.qty > 0 && doc.sku?.startsWith("A")
(doc) => doc.qty > 0 && doc.sku?.startsWith("A")
Sandbox output (example)
Matches docs where qty > 0 and sku begins with "A"
Find options: limit, offset, projection
- TypeScript
- JavaScript
await collection.find(
{ plan: "pro" },
{ limit: 10, offset: 0, projection: ["email", "profile.age"] }
)
await collection.find(
{ plan: "pro" },
{ limit: 10, offset: 0, projection: ["email", "profile.age"] }
)
limit: max documents returnedoffset: skip N matching docs (simple paging)projection: include only specific fields (top-level or dot-paths)
Sandbox output (example)
[ { email: "a@b.com", profile: { age: 21 } }, { email: "c@d.com", profile: { age: 35 } } ]
Updates
Updates can be either:
- Operator updates (contain
$keys), or - Replacement/merge updates (no
$keys)
Operator updates
$set
- TypeScript
- JavaScript
{ $set: { "profile.name": "New Name", plan: "pro" } }
{ $set: { "profile.name": "New Name", plan: "pro" } }
Sandbox output (example)
Sets profile.name and plan on the matching doc(s)
$inc
- TypeScript
- JavaScript
{ $inc: { attempts: 1, "stats.logins": 1 } }
{ $inc: { attempts: 1, "stats.logins": 1 } }
Sandbox output (example)
Increments numeric fields by the given amount
Replacement/merge updates (no $ keys)
If the update object contains no $ keys, it's treated as a shallow merge:
- TypeScript
- JavaScript
{ status: "active", plan: "pro" }
{ status: "active", plan: "pro" }
Sandbox output (example)
Merges into the existing doc (top-level keys)
Upserts
updateOne(filter, update, { upsert: true }) inserts a new document if nothing matches.
- TypeScript
- JavaScript
await users.updateOne(
{ email: "new@x.com" },
{ $set: { email: "new@x.com", plan: "free" } },
{ upsert: true }
)
await users.updateOne(
{ email: "new@x.com" },
{ $set: { email: "new@x.com", plan: "free" } },
{ upsert: true }
)
Sandbox output (example)
Inserts a new doc when no existing doc matches the filter
Complete example (query + update)
- TypeScript
- JavaScript
import { LioranManager } from "@liorandb/core";
const manager = new LioranManager({ rootPath: "./.liorandb" });
const db = await manager.db("app");
const users = db.collection("users");
await users.insertMany([
{ email: "a@b.com", profile: { age: 21 }, plan: "free" },
{ email: "c@d.com", profile: { age: 35 }, plan: "pro" },
]);
await users.updateOne(
{ "profile.age": { $gte: 18 }, plan: { $in: ["free", "pro"] } },
{ $inc: { "stats.logins": 1 }, $set: { active: true } }
);
console.log(await users.find({}, { projection: ["email", "stats.logins", "active"] }));
await manager.close();
import { LioranManager } from "@liorandb/core";
const manager = new LioranManager({ rootPath: "./.liorandb" });
const db = await manager.db("app");
const users = db.collection("users");
await users.insertMany([
{ email: "a@b.com", profile: { age: 21 }, plan: "free" },
{ email: "c@d.com", profile: { age: 35 }, plan: "pro" },
]);
await users.updateOne(
{ "profile.age": { $gte: 18 }, plan: { $in: ["free", "pro"] } },
{ $inc: { "stats.logins": 1 }, $set: { active: true } }
);
console.log(await users.find({}, { projection: ["email", "stats.logins", "active"] }));
await manager.close();
Sandbox output (example)
[ { email: "a@b.com", stats: { logins: 1 }, active: true }, { email: "c@d.com" } ]