Thin MongoDB ODM built for Standard Schema
mongodb
zod
deno
1import { z } from "@zod/zod";
2import { MongoMemoryServer } from "mongodb-memory-server-core";
3import mongoose from "mongoose";
4import { connect, disconnect, Model } from "../mod.ts";
5
6/**
7 * Benchmark basic CRUD operations for Nozzle vs Mongoose.
8 *
9 * Run with:
10 * deno bench -A bench/nozzle_vs_mongoose.bench.ts
11 */
12
13const userSchema = z.object({
14 name: z.string(),
15 email: z.string().email(),
16 age: z.number().int().positive().optional(),
17 createdAt: z.date().default(() => new Date()),
18});
19
20const mongoServer = await MongoMemoryServer.create();
21const uri = mongoServer.getUri();
22
23// Use separate DBs to avoid any cross-driver interference
24const nozzleDbName = "bench_nozzle";
25const mongooseDbName = "bench_mongoose";
26
27await connect(uri, nozzleDbName);
28const NozzleUser = new Model("bench_users_nozzle", userSchema);
29
30const mongooseConn = await mongoose.connect(uri, { dbName: mongooseDbName });
31const mongooseUserSchema = new mongoose.Schema(
32 {
33 name: String,
34 email: String,
35 age: Number,
36 createdAt: { type: Date, default: Date.now },
37 },
38 { collection: "bench_users_mongoose" },
39);
40const MongooseUser = mongooseConn.models.BenchUser ||
41 mongooseConn.model("BenchUser", mongooseUserSchema);
42
43// Start from a clean state
44await NozzleUser.delete({});
45await MongooseUser.deleteMany({});
46
47// Seed base documents for read/update benches
48const nozzleSeed = await NozzleUser.insertOne({
49 name: "Seed Nozzle",
50 email: "seed-nozzle@example.com",
51 age: 30,
52});
53const mongooseSeed = await MongooseUser.create({
54 name: "Seed Mongoose",
55 email: "seed-mongoose@example.com",
56 age: 30,
57});
58
59const nozzleSeedId = nozzleSeed.insertedId;
60const mongooseSeedId = mongooseSeed._id;
61
62let counter = 0;
63const nextEmail = (prefix: string) => `${prefix}-${counter++}@bench.dev`;
64
65Deno.bench("mongoose insertOne", { group: "insertOne" }, async () => {
66 await MongooseUser.insertOne({
67 name: "Mongoose User",
68 email: nextEmail("mongoose"),
69 age: 25,
70 });
71});
72
73Deno.bench(
74 "nozzle insertOne",
75 { group: "insertOne", baseline: true },
76 async () => {
77 await NozzleUser.insertOne({
78 name: "Nozzle User",
79 email: nextEmail("nozzle"),
80 age: 25,
81 });
82 },
83);
84
85Deno.bench("mongoose findById", { group: "findById" }, async () => {
86 await MongooseUser.findById(mongooseSeedId);
87});
88
89Deno.bench(
90 "nozzle findById",
91 { group: "findById", baseline: true },
92 async () => {
93 await NozzleUser.findById(nozzleSeedId);
94 },
95);
96
97Deno.bench("mongoose updateOne", { group: "updateOne" }, async () => {
98 await MongooseUser.updateOne(
99 { _id: mongooseSeedId },
100 { $set: { age: 31 } },
101 );
102});
103
104Deno.bench(
105 "nozzle updateOne",
106 { group: "updateOne", baseline: true },
107 async () => {
108 await NozzleUser.updateOne(
109 { _id: nozzleSeedId },
110 { age: 31 },
111 );
112 },
113);
114
115// Attempt graceful shutdown when the process exits
116async function cleanup() {
117 await disconnect();
118 await mongooseConn.disconnect();
119 await mongoServer.stop();
120}
121
122globalThis.addEventListener("unload", () => {
123 void cleanup();
124});