Create indexes on frequently queried fields for O(1) lookup performance:
// Single-field indexesUser.createIndex('name')User.createIndex('age')// Compound indexes (multiple fields)User.createIndex(['city','age'])// In schemauserSchema.index('name')// Single fielduserSchema.index(['city','age'])// Compound index// Single-field equality queries on indexed fields are O(1)awaitUser.findOne({name: 'Bob'})// O(1) with index// Multi-field queries with compound index are O(1)awaitUser.findOne({city: 'New York',age: 25})// O(1) with compound index// Partial index matching - uses index even if query has extra fieldsUser.createIndex('name')awaitUser.findOne({name: 'Bob',age: 32})// Uses 'name' index, filters 1 doc instead of all
memgoose supports both options-based and chainable query patterns:
Partial Index Matching: If a query includes indexed fields plus additional fields, the index will still be used to narrow down candidates before filtering. For example, if you have an index on 'name' and query { name: 'Bob', age: 32 }, it will use the index to get all documents with name='Bob', then filter those for age=32 (much faster than scanning all documents).
Example with 100,000 documents:
Index on 'status', query { status: 'active', age: { $gte: 30 } }
Gets ~33k documents via index (instant)
Filters those 33k for age condition (~29ms)
vs. Full scan: ~40ms on all 100k documents (40% faster with partial index)
async findOne(query: Query<T>): Promise<T | null>
Finds the first document matching the query. Returns null if no match is found.
async find(query?: Query<T>): Promise<T[]>
Finds all documents matching the query. Returns an empty array if no matches found. If no query is provided, returns all documents.
async create(doc: T): Promise<T>
Creates and inserts a new document into the model (mongoose-compatible). Returns the created document. Updates indexes automatically. Executes pre/post save hooks.
async insertMany(docs: T[]): Promise<T[]>
Inserts multiple documents into the model at once. Returns the inserted documents. Updates indexes automatically. Executes save hooks for each document.
async save(doc: T): Promise<T>
Saves a document to the model. Returns the saved document. Updates indexes automatically. Executes pre/post save hooks.
async deleteOne(query: Query<T>): Promise<{ deletedCount: number }>
Deletes the first document matching the query. Returns the count of deleted documents. Executes pre/post delete hooks.
async deleteMany(query: Query<T>): Promise<{ deletedCount: number }>
Deletes all documents matching the query. Returns the count of deleted documents. Executes pre/post delete hooks.
async updateOne(query: Query<T>, update: Update<T>): Promise<{ modifiedCount: number }>
Updates the first document matching the query. Supports update operators ($set, $unset, $inc, $dec, $push, $pull, $addToSet, $pop, $rename). Returns the count of modified documents. Executes pre/post update hooks.
async updateMany(query: Query<T>, update: Update<T>): Promise<{ modifiedCount: number }>
Updates all documents matching the query. Supports all update operators. Returns the count of modified documents. Executes pre/post update hooks.
Returns an array of unique values for the specified field, optionally filtered by query.
async findById(id): Promise<T | null>
Shorthand for findOne({ _id: id }). Useful when documents have an _id field.
Build the project for distribution:
Clean build artifacts:
memgoose uses Node.js's built-in test runner with TypeScript support via tsx. Tests are written in TypeScript and run directly on the source files without needing compilation.
Run all tests:
Watch mode (automatically re-run tests on file changes):
Run tests with coverage:
Run a specific test file:
npm run test:file tests/indexing.test.ts
Check out the examples folder for more usage examples.
Run the basic example:
Run the performance benchmark (100,000 documents):
Run the virtuals & hooks example:
Run the complete features demo:
Performance Benchmark Results
The performance example demonstrates the dramatic speedup from indexing on 100,000 documents (20 comprehensive tests):