Thin MongoDB ODM built for Standard Schema
mongodb zod deno
at main 5.2 kB view raw
1import type { 2 Collection, 3 CreateIndexesOptions, 4 DropIndexesOptions, 5 IndexDescription, 6 IndexSpecification, 7 ListIndexesOptions, 8} from "mongodb"; 9import type { Infer, Schema } from "../types.ts"; 10 11/** 12 * Index management operations for the Model class 13 * 14 * This module contains all index-related operations including creation, 15 * deletion, listing, and synchronization of indexes. 16 */ 17 18/** 19 * Create a single index on the collection 20 * 21 * @param collection - MongoDB collection 22 * @param keys - Index specification (e.g., { email: 1 } or { name: "text" }) 23 * @param options - Index creation options (unique, sparse, expireAfterSeconds, etc.) 24 * @returns The name of the created index 25 */ 26export async function createIndex<T extends Schema>( 27 collection: Collection<Infer<T>>, 28 keys: IndexSpecification, 29 options?: CreateIndexesOptions, 30): Promise<string> { 31 return await collection.createIndex(keys, options); 32} 33 34/** 35 * Create multiple indexes on the collection 36 * 37 * @param collection - MongoDB collection 38 * @param indexes - Array of index descriptions 39 * @param options - Index creation options 40 * @returns Array of index names created 41 */ 42export async function createIndexes<T extends Schema>( 43 collection: Collection<Infer<T>>, 44 indexes: IndexDescription[], 45 options?: CreateIndexesOptions, 46): Promise<string[]> { 47 return await collection.createIndexes(indexes, options); 48} 49 50/** 51 * Drop a single index from the collection 52 * 53 * @param collection - MongoDB collection 54 * @param index - Index name or specification 55 * @param options - Drop index options 56 */ 57export async function dropIndex<T extends Schema>( 58 collection: Collection<Infer<T>>, 59 index: string | IndexSpecification, 60 options?: DropIndexesOptions, 61): Promise<void> { 62 await collection.dropIndex(index as string, options); 63} 64 65/** 66 * Drop all indexes from the collection (except _id index) 67 * 68 * @param collection - MongoDB collection 69 * @param options - Drop index options 70 */ 71export async function dropIndexes<T extends Schema>( 72 collection: Collection<Infer<T>>, 73 options?: DropIndexesOptions, 74): Promise<void> { 75 await collection.dropIndexes(options); 76} 77 78/** 79 * List all indexes on the collection 80 * 81 * @param collection - MongoDB collection 82 * @param options - List indexes options 83 * @returns Array of index information 84 */ 85export async function listIndexes<T extends Schema>( 86 collection: Collection<Infer<T>>, 87 options?: ListIndexesOptions, 88): Promise<IndexDescription[]> { 89 const indexes = await collection.listIndexes(options).toArray(); 90 return indexes as IndexDescription[]; 91} 92 93/** 94 * Get index information by name 95 * 96 * @param collection - MongoDB collection 97 * @param indexName - Name of the index 98 * @returns Index description or null if not found 99 */ 100export async function getIndex<T extends Schema>( 101 collection: Collection<Infer<T>>, 102 indexName: string, 103): Promise<IndexDescription | null> { 104 const indexes = await listIndexes(collection); 105 return indexes.find((idx) => idx.name === indexName) || null; 106} 107 108/** 109 * Check if an index exists 110 * 111 * @param collection - MongoDB collection 112 * @param indexName - Name of the index 113 * @returns True if index exists, false otherwise 114 */ 115export async function indexExists<T extends Schema>( 116 collection: Collection<Infer<T>>, 117 indexName: string, 118): Promise<boolean> { 119 const index = await getIndex(collection, indexName); 120 return index !== null; 121} 122 123/** 124 * Synchronize indexes - create indexes if they don't exist, update if they differ 125 * 126 * This is useful for ensuring indexes match your schema definition 127 * 128 * @param collection - MongoDB collection 129 * @param indexes - Array of index descriptions to synchronize 130 * @param options - Options for index creation 131 * @returns Array of index names that were created 132 */ 133export async function syncIndexes<T extends Schema>( 134 collection: Collection<Infer<T>>, 135 indexes: IndexDescription[], 136 options?: CreateIndexesOptions, 137): Promise<string[]> { 138 const existingIndexes = await listIndexes(collection); 139 const indexesToCreate: IndexDescription[] = []; 140 141 for (const index of indexes) { 142 const indexName = index.name || generateIndexName(index.key); 143 const existingIndex = existingIndexes.find( 144 (idx) => idx.name === indexName, 145 ); 146 147 if (!existingIndex) { 148 indexesToCreate.push(index); 149 } else if ( 150 JSON.stringify(existingIndex.key) !== JSON.stringify(index.key) 151 ) { 152 // Index exists but keys differ - drop and recreate 153 await dropIndex(collection, indexName); 154 indexesToCreate.push(index); 155 } 156 // If index exists and matches, skip it 157 } 158 159 const created: string[] = []; 160 if (indexesToCreate.length > 0) { 161 const names = await createIndexes(collection, indexesToCreate, options); 162 created.push(...names); 163 } 164 165 return created; 166} 167 168/** 169 * Generate index name from key specification 170 * 171 * @param keys - Index specification 172 * @returns Generated index name 173 */ 174export function generateIndexName(keys: IndexSpecification): string { 175 if (typeof keys === "string") { 176 return keys; 177 } 178 const entries = Object.entries(keys as Record<string, number | string>); 179 return entries.map(([field, direction]) => `${field}_${direction}`).join("_"); 180}