···5555 connect,
5656 disconnect,
5757 InferModel,
5858- InsertType,
5858+ Input,
5959 Model,
6060} from "@nozzle/nozzle";
6161import { userSchema } from "./schemas/user";
6262import { ObjectId } from "mongodb"; // v6+ driver recommended
63636464type User = InferModel<typeof userSchema>;
6565-type UserInsert = InsertType<typeof userSchema>;
6565+type UserInsert = Input<typeof userSchema>;
66666767async function main() {
6868 // Use the latest connection string format and options
···83838484```ts
8585// Insert one
8686+// Note: createdAt has a default, so it's optional in the input type
8687const newUser: UserInsert = {
8788 name: "John Doe",
8889 email: "john.doe@example.com",
8990 age: 30,
9191+ // createdAt is optional because of z.date().default(() => new Date())
9092};
9193const insertResult = await UserModel.insertOne(newUser);
9294···140142 { age: { $gte: 18 } },
141143 { skip: 0, limit: 10, sort: { age: -1 } },
142144);
145145+146146+// Index Management
147147+// Create a unique index
148148+await UserModel.createIndex({ email: 1 }, { unique: true });
149149+150150+// Create a compound index
151151+await UserModel.createIndex({ name: 1, age: -1 });
152152+153153+// Create multiple indexes at once
154154+await UserModel.createIndexes([
155155+ { key: { email: 1 }, name: "email_idx", unique: true },
156156+ { key: { name: 1, age: -1 }, name: "name_age_idx" },
157157+]);
158158+159159+// List all indexes
160160+const indexes = await UserModel.listIndexes();
161161+console.log("Indexes:", indexes);
162162+163163+// Check if index exists
164164+const exists = await UserModel.indexExists("email_idx");
165165+166166+// Drop an index
167167+await UserModel.dropIndex("email_idx");
168168+169169+// Sync indexes (useful for migrations - creates missing, updates changed)
170170+await UserModel.syncIndexes([
171171+ { key: { email: 1 }, name: "email_idx", unique: true },
172172+ { key: { createdAt: 1 }, name: "created_at_idx" },
173173+]);
143174```
144175145176---
+2-2
examples/user.ts
···44 connect,
55 disconnect,
66 type InferModel,
77- type InsertType,
77+ type Input,
88 Model,
99} from "../mod.ts";
1010···18181919// Infer the TypeScript type from the Zod schema
2020type User = InferModel<typeof userSchema>;
2121-type UserInsert = InsertType<typeof userSchema>;
2121+type UserInsert = Input<typeof userSchema>;
22222323async function runExample() {
2424 try {
+1-1
mod.ts
···11-export { type InferModel, type InsertType } from "./schema.ts";
11+export { type InferModel, type Input } from "./schema.ts";
22export { connect, disconnect } from "./client.ts";
33export { Model } from "./model.ts";
+138
model.ts
···11import type { z } from "@zod/zod";
22import type {
33 Collection,
44+ CreateIndexesOptions,
45 DeleteResult,
56 Document,
77+ DropIndexesOptions,
68 Filter,
99+ IndexDescription,
1010+ IndexSpecification,
711 InsertManyResult,
812 InsertOneResult,
1313+ ListIndexesOptions,
914 OptionalUnlessRequiredId,
1015 UpdateResult,
1116 WithId,
···131136 .limit(options.limit ?? 10)
132137 .sort(options.sort ?? {})
133138 .toArray();
139139+ }
140140+141141+ // Index Management Methods
142142+143143+ /**
144144+ * Create a single index on the collection
145145+ * @param keys - Index specification (e.g., { email: 1 } or { name: "text" })
146146+ * @param options - Index creation options (unique, sparse, expireAfterSeconds, etc.)
147147+ * @returns The name of the created index
148148+ */
149149+ async createIndex(
150150+ keys: IndexSpecification,
151151+ options?: CreateIndexesOptions,
152152+ ): Promise<string> {
153153+ return await this.collection.createIndex(keys, options);
154154+ }
155155+156156+ /**
157157+ * Create multiple indexes on the collection
158158+ * @param indexes - Array of index descriptions
159159+ * @param options - Index creation options
160160+ * @returns Array of index names created
161161+ */
162162+ async createIndexes(
163163+ indexes: IndexDescription[],
164164+ options?: CreateIndexesOptions,
165165+ ): Promise<string[]> {
166166+ return await this.collection.createIndexes(indexes, options);
167167+ }
168168+169169+ /**
170170+ * Drop a single index from the collection
171171+ * @param index - Index name or specification
172172+ * @param options - Drop index options
173173+ */
174174+ async dropIndex(
175175+ index: string | IndexSpecification,
176176+ options?: DropIndexesOptions,
177177+ ): Promise<void> {
178178+ // MongoDB driver accepts string or IndexSpecification
179179+ await this.collection.dropIndex(index as string, options);
180180+ }
181181+182182+ /**
183183+ * Drop all indexes from the collection (except _id index)
184184+ * @param options - Drop index options
185185+ */
186186+ async dropIndexes(options?: DropIndexesOptions): Promise<void> {
187187+ await this.collection.dropIndexes(options);
188188+ }
189189+190190+ /**
191191+ * List all indexes on the collection
192192+ * @param options - List indexes options
193193+ * @returns Array of index information
194194+ */
195195+ async listIndexes(
196196+ options?: ListIndexesOptions,
197197+ ): Promise<IndexDescription[]> {
198198+ const indexes = await this.collection.listIndexes(options).toArray();
199199+ return indexes as IndexDescription[];
200200+ }
201201+202202+ /**
203203+ * Get index information by name
204204+ * @param indexName - Name of the index
205205+ * @returns Index description or null if not found
206206+ */
207207+ async getIndex(indexName: string): Promise<IndexDescription | null> {
208208+ const indexes = await this.listIndexes();
209209+ return indexes.find((idx) => idx.name === indexName) || null;
210210+ }
211211+212212+ /**
213213+ * Check if an index exists
214214+ * @param indexName - Name of the index
215215+ * @returns True if index exists, false otherwise
216216+ */
217217+ async indexExists(indexName: string): Promise<boolean> {
218218+ const index = await this.getIndex(indexName);
219219+ return index !== null;
220220+ }
221221+222222+ /**
223223+ * Synchronize indexes - create indexes if they don't exist, update if they differ
224224+ * This is useful for ensuring indexes match your schema definition
225225+ * @param indexes - Array of index descriptions to synchronize
226226+ * @param options - Options for index creation
227227+ */
228228+ async syncIndexes(
229229+ indexes: IndexDescription[],
230230+ options?: CreateIndexesOptions,
231231+ ): Promise<string[]> {
232232+ const existingIndexes = await this.listIndexes();
233233+234234+ const indexesToCreate: IndexDescription[] = [];
235235+236236+ for (const index of indexes) {
237237+ const indexName = index.name || this._generateIndexName(index.key);
238238+ const existingIndex = existingIndexes.find(
239239+ (idx) => idx.name === indexName,
240240+ );
241241+242242+ if (!existingIndex) {
243243+ indexesToCreate.push(index);
244244+ } else if (
245245+ JSON.stringify(existingIndex.key) !== JSON.stringify(index.key)
246246+ ) {
247247+ // Index exists but keys differ - drop and recreate
248248+ await this.dropIndex(indexName);
249249+ indexesToCreate.push(index);
250250+ }
251251+ // If index exists and matches, skip it
252252+ }
253253+254254+ const created: string[] = [];
255255+ if (indexesToCreate.length > 0) {
256256+ const names = await this.createIndexes(indexesToCreate, options);
257257+ created.push(...names);
258258+ }
259259+260260+ return created;
261261+ }
262262+263263+ /**
264264+ * Helper method to generate index name from key specification
265265+ */
266266+ private _generateIndexName(keys: IndexSpecification): string {
267267+ if (typeof keys === "string") {
268268+ return keys;
269269+ }
270270+ const entries = Object.entries(keys as Record<string, number | string>);
271271+ return entries.map(([field, direction]) => `${field}_${direction}`).join("_");
134272 }
135273}