API Reference
Complete API reference for Sluice ORM.
Core Exports
Registry
import { registry } from "sluice-orm";
const dbRegistry = registry("8.0", {
users: UserSchema,
orders: OrderSchema,
});
Creates a registry bound to a specific MongoDB version and schemas.
Parameters:
version: MongoDB version string (e.g.,"8.0")schemas: Object mapping collection names to schemas
Returns: A function that accepts a MongoDB Db instance and returns typed collections.
Collection Binding
const { users, orders } = dbRegistry(client.db("myapp"));
Binds the registry to a specific database, returning typed collection instances. Each collection exposes both CRUD and aggregation methods.
Aggregation Pipeline Stages
All stages are imported from the main package:
import {
$match, $group, $project, $sort, $limit, $skip,
$unwind, $lookup, $addFields, $set, $unset,
$facet, $bucket, $count, $sortByCount, $densify,
$fill, $geoNear, $setWindowFields,
} from "sluice-orm";
$match
Filters documents based on conditions. The callback receives $ (expression builder) for $expr usage.
$match($ => ({ age: { $gte: 18 }, status: "active" }))
$match($ => ({ "profile.name": { $regex: "John" } }))
// With $expr for cross-field comparisons
$match($ => ({
$expr: $.gt("$salary", "$targetSalary"),
}))
$group
Groups documents by a key and performs accumulations. The callback receives $ (accumulator builder) with access to both accumulators and expression operators.
$group($ => ({
_id: "$department",
totalSalary: $.sum("$salary"), // Type: number
avgAge: $.avg("$age"), // Type: number | null
employeeCount: $.sum(1), // Type: number
names: $.push("$name"), // Type: string[]
uniqueRoles: $.addToSet("$role"), // Type: string[]
}))
$project
Reshapes documents by including/excluding fields and computing new ones.
$project($ => ({
name: $.include, // or 1
age: $.include, // or 1
_id: $.exclude, // or 0
fullName: $.concat("$firstName", " ", "$lastName"),
}))
$sort
Sorts documents by specified fields.
$sort({ age: -1, name: 1 }) // Descending age, ascending name
$sort({ createdAt: -1 }) // Most recent first
$addFields / $set
Adds new fields to documents. $set is an alias.
$addFields($ => ({
fullName: $.concat("$firstName", " ", "$lastName"),
ageInMonths: $.multiply("$age", 12),
isAdult: $.gte("$age", 18),
}))
$unset
Removes fields from documents.
$unset("temporaryField", "debugInfo")
$unwind
Deconstructs an array field into multiple documents.
$unwind("$tags")
$unwind({ path: "$items", includeArrayIndex: "itemIndex" })
$lookup
Performs left outer joins with other collections. from takes a typed collection reference from your bound registry.
// Simple lookup
$lookup({
from: boundRegistry.orders,
localField: "userId",
foreignField: "customerId",
as: "userOrders",
})
// With sub-pipeline
$lookup({
from: boundRegistry.orders,
localField: "_id",
foreignField: "userId",
as: "recentOrders",
pipeline: $ => $.pipe(
$match($ => ({ status: "paid" })),
$sort({ createdAt: -1 }),
$limit(5),
),
})
$facet
Processes multiple aggregation pipelines within a single stage. Each branch uses $.pipe(...).
$facet($ => ({
totalCount: $.pipe(
$count("total"),
),
byCategory: $.pipe(
$group($ => ({ _id: "$category", count: $.sum(1) })),
$sort({ count: -1 }),
),
}))
$limit / $skip
$limit(10) // Take first 10 documents
$skip(20) // Skip first 20 documents
$count
$count("total") // { total: number }
$sortByCount
$sortByCount("$category") // { _id: string; count: number }
$bucket
$bucket({
groupBy: "$price",
boundaries: [0, 50, 100, 200],
default: "other",
output: $ => ({
count: $.sum(1),
avgPrice: $.avg("$price"),
}),
})
Expression Operators
All operators are accessed via the $ callback argument in stages.
Arithmetic Operators
$.add("$price", "$tax") // Type: number
$.subtract("$total", "$discount") // Type: number
$.multiply("$quantity", "$price") // Type: number
$.divide("$total", "$count") // Type: number
$.mod("$number", 2) // Type: number
$.abs("$balance") // Type: number
$.ceil("$price") // Type: number
$.floor("$price") // Type: number
$.round("$price", 2) // Type: number
Comparison Operators
$.eq("$status", "active") // Type: boolean
$.ne("$role", "admin") // Type: boolean
$.gt("$age", 18) // Type: boolean
$.gte("$score", 80) // Type: boolean
$.lt("$price", 100) // Type: boolean
$.lte("$quantity", 10) // Type: boolean
$.cmp("$a", "$b") // Type: -1 | 0 | 1
Logical Operators
All logical operators take expression operator calls as arguments — not raw strings.
$.and($.gt("$age", 18), $.eq("$status", "active")) // Type: boolean
$.or($.lt("$age", 18), $.gt("$age", 65)) // Type: boolean
$.not("$active") // Type: boolean
// Nested composition
$.and(
$.gte("$age", 18),
$.or(
$.eq("$role", "admin"),
$.gt("$score", 90),
),
)
String Operators
$.concat("$firstName", " ", "$lastName") // Type: `${string} ${string}`
$.substr("$name", 0, 5) // Type: string
$.toLower("$email") // Type: string
$.toUpper("$department") // Type: string
$.strLenCP("$name") // Type: number
$.trim("$input") // Type: string
$.ltrim("$input") // Type: string
$.rtrim("$input") // Type: string
Array Operators
$.size("$tags") // Type: number
$.in("$status", ["active", "pending"]) // Type: boolean
$.arrayElemAt("$scores", 0) // Type: element type
$.slice("$items", 0, 5) // Type: array type
$.concatArrays("$arr1", "$arr2") // Type: merged array
$.reverseArray("$items") // Type: array type
$.sortArray({ input: "$scores", sortBy: -1 }) // Type: array type
Conditional Operators
// $cond — object syntax with if/then/else
$.cond({
if: $.gte("$age", 18),
then: "adult",
else: "minor",
})
// Type: "adult" | "minor"
// $ifNull — fallback for nullable fields
$.ifNull("$nickname", "$name", "Anonymous")
// Type: string (narrowed from string | null)
// $switch — multi-branch with operator calls in case
$.switch({
branches: [
{ case: $.lte("$score", 20), then: "low" },
{ case: $.lte("$score", 50), then: "medium" },
{ case: $.lte("$score", 80), then: "high" },
],
default: "excellent",
})
// Type: "low" | "medium" | "high" | "excellent"
Array Transformation Operators
// $map — transform each element
$.map({
input: "$items",
as: "item",
in: "$$item.name", // or in: $ => $.multiply("$$item.price", 2)
})
// $filter — keep matching elements (cond MUST be a callback)
$.filter({
input: "$items",
as: "item",
cond: $ => $.gte("$$item.price", 100),
})
// $reduce — fold array (in MUST be a callback)
$.reduce({
input: "$items",
initialValue: 0,
in: $ => $.add("$$value", "$$this.quantity"),
})
Object Operators
// $mergeObjects — merge with null removal and last-wins override
$.mergeObjects("$defaults", "$overrides", { extra: true })
Accumulators (inside $group)
$.sum(1) // Count documents → number
$.sum("$quantity") // Sum field values → number
$.avg("$price") // Average → number | null
$.min("$price") // Minimum → number
$.max("$price") // Maximum → number
$.first("$value") // First in group → field type
$.last("$value") // Last in group → field type
$.push("$item") // Collect all → field type[]
$.addToSet("$tag") // Collect unique → field type[]
$.stdDevPop("$score") // Population std dev → number | null
$.stdDevSamp("$score") // Sample std dev → number | null
CRUD Operations
All CRUD operations return builder objects. Call .execute() for mutations and .toList() / .toOne() for reads.
Find
// Find all (no filter)
const users = await collection.find().toList();
// Find with filter
const adults = await collection
.find($ => ({ age: { $gte: 18 } }))
.toList();
// Find with options
const page = await collection
.find($ => ({ department: "Engineering" }), {
sort: { name: 1 },
limit: 10,
skip: 5,
})
.toList();
// Find one
const user = await collection
.findOne($ => ({ _id: userId }))
.toOne();
Insert
// Insert one — must match schema exactly
await collection.insertOne({
_id: "user123",
name: "John Doe",
email: "john@example.com",
}).execute();
// Insert many
await collection.insertMany([
{ _id: "user1", name: "Alice", score: 10, tags: [], active: true },
{ _id: "user2", name: "Bob", score: 20, tags: ["a"], active: false },
]).execute();
Update
// Update one
await collection.updateOne(
$ => ({ _id: "user123" }),
{ $set: { name: "John Smith" }, $inc: { age: 1 } },
).execute();
// Update many
await collection.updateMany(
$ => ({ department: "Engineering" }),
{ $set: { building: "A" } },
).execute();
Replace
await collection.replaceOne(
$ => ({ _id: "user123" }),
{ _id: "user123", name: "John Doe", department: "Sales" },
).execute();
Delete
await collection.deleteOne($ => ({ _id: "user123" })).execute();
await collection.deleteMany($ => ({ status: "inactive" })).execute();
Find and Modify
// Find one and update — returns the document or null
const user = await collection.findOneAndUpdate(
$ => ({ _id: "user123" }),
{ $set: { lastLogin: new Date() } },
).execute();
// Find one and replace
const old = await collection.findOneAndReplace(
$ => ({ _id: "user123" }),
{ _id: "user123", name: "New Name", age: 25 },
).execute();
// Find one and delete
const deleted = await collection.findOneAndDelete(
$ => ({ _id: "user123" }),
).execute();
Count and Distinct
// Count all documents
const total = await collection.countDocuments().execute();
// Count with filter
const active = await collection.countDocuments($ => ({ active: true })).execute();
// Estimated count (faster, no filter)
const estimated = await collection.estimatedDocumentCount().execute();
// Distinct values — returns typed array
const names = await collection.distinct("name").execute();
// Type: string[]
const names2 = await collection.distinct("name", $ => ({ active: true })).execute();
Bulk Write
await collection.bulkWrite([
{ insertOne: { document: { _id: id1, name: "A", count: 1, active: true } } },
{ updateOne: { filter: { name: "X" }, update: { $set: { count: 100 } } } },
{ updateMany: { filter: { active: true }, update: { $inc: { count: 5 } } } },
{ deleteOne: { filter: { _id: id2 } } },
{ replaceOne: { filter: { _id: id3 }, replacement: { _id: id3, name: "Z", count: 0, active: false } } },
]).execute();
// Ordered execution
await collection.bulkWrite([
{ insertOne: { document: { _id: id, name: "seq", count: 1, active: true } } },
{ updateOne: { filter: { _id: id }, update: { $set: { count: 42 } } } },
]).execute({ ordered: true });
Aggregation Result Methods
.toList()
Returns all documents as an array.
const users = await collection
.aggregate($match($ => ({ age: { $gte: 18 } })))
.toList();
.toOne()
Returns the first document or null.
const user = await collection
.aggregate($match($ => ({ _id: "user123" })))
.toOne();
Effect Integration
Effect Registry
import { registryEffect } from "sluice-orm";
import { Effect } from "effect";
const program = Effect.gen(function* () {
const registry = yield* registryEffect("8.0", { users: UserSchema });
const users = yield* registry.users.find().toList();
yield* registry.users
.updateOne(() => ({ _id: "1" }), { $set: { age: 31 } })
.execute();
const deleted = yield* registry.users
.deleteOne(() => ({ _id: "1" }))
.execute();
return users;
});
All Effect CRUD operations return Effect<T, MongoError>.
TypeScript Configuration
Recommended tsconfig.json:
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"strict": true,
"exactOptionalPropertyTypes": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedIndexedAccess": true
}
}