Deleting Documents and Getting Started with Node.js
Deleting documents is a core operation in data cleanup—this course also introduces hands-on practice with Node.js and Mongoose.
Master deleteOne/deleteMany, connecting Node.js and Mongoose, schema definition, and model CRUD operations.
1. What You'll Learn
- deleteOne / deleteMany: Delete documents
- findOneAndDelete atomically returns the deleted document
dropset anddropDatabaseto delete a database- Node.js + Mongoose: Connecting to MongoDB
- Defining Mongoose Schemas and Creating Models
- Mongoose CRUD Operations (Create/Read/Update/Delete)
2. A True Story of a Full-Stack Engineer
(1) Pain Point: Cleaning up expired data requires complex scripts
Bob needs to regularly clean up expired orders and deactivated user accounts on the e-commerce platform:
// ❌ Counterexample:Query multiple times, then delete(Slow + Race Condition)
const expiredOrders = await Order.find({ expiryDate: { $lt: new Date() } });
for (const order of expiredOrders) {
await Order.deleteOne({ _id: order._id });
}
// N Sub-network round trip,N Second deletion operation
(2) Solution for Batch Deletions in MongoDB + Mongoose
// ✅ Correct Example:Bulk Deletion in One Go
const result = await Order.deleteMany({
expiryDate: { $lt: new Date() }
});
// Delete all expired orders in a single operation
// mongoose Schema Definition
const OrderSchema = new mongoose.Schema({
userId: { type: mongoose.Schema.Types.ObjectId, required: true },
items: [{ sku: String, qty: Number, price: mongoose.Schema.Types.Decimal128 }],
total: { type: mongoose.Schema.Types.Decimal128, required: true },
status: { type: String, enum: ['pending', 'paid', 'shipped', 'delivered'], default: 'pending' },
expiryDate: Date,
createdAt: { type: Date, default: Date.now }
}, { timestamps: true });
const Order = mongoose.model('Order', OrderSchema);
graph LR
A[Delete Operation] --> B[deleteOne<br/>Delete a single item]
A --> C[deleteMany<br/>Bulk Delete]
A --> D[findOneAndDelete<br/>Atom Returns]
A --> E[drop<br/>Delete Set]
A --> F[dropDatabase<br/>Delete the database]
style D fill:#d4edda
3. deleteOne: Delete a Single Document
Concept Explanation: deleteOne Deletes the first document that matches the filter criteria; this is the most basic deletion method in MongoDB. Deletion is irreversible—there is no "recycle bin" mechanism, and deleted documents cannot be directly restored (unless a backup or oplog is available). Therefore, deletion operations must be performed with extreme caution in production environments.
How It Works: deleteOne The execution flow is as follows: Matching phase (find the first document based on the filter) → Deletion phase (remove the document from the collection) → Index update (delete the relevant index entries) → Write Concern confirmation. The entire operation is atomic for a single document. After deletion, disk space is not immediately freed up but is marked as reusable space.
sequenceDiagram
participant App as Applications
participant Mongo as MongoDB
participant WT as WiredTiger
App->>Mongo: deleteOne({ sku: "PHONE-001" })
Mongo->>Mongo: Match filter (Index Scan)
Mongo->>WT: Delete Document + Update Index
WT-->>Mongo: Confirm Deletion
Mongo-->>App: { acknowledged: true, deletedCount: 1 }
| Parameter | Type | Description |
|---|---|---|
filter |
Document | Search Criteria (Required) |
options |
Document | writeConcern etc.(optional) |
| Return Field | Type | Description |
|---|---|---|
acknowledged |
Boolean | Whether the write has been confirmed |
deletedCount |
Number | Number of documents deleted (0 or 1) |
// === deleteOne Basic Usage ===
db.products.deleteOne({ sku: 'PHONE-001' });
// Return Results:
// { acknowledged: true, deletedCount: 1 }
| Return Field | Meaning |
|---|---|
acknowledged |
Has this been confirmed? |
deletedCount |
Number of deleted documents (0 or 1) |
▶ Example 1: deleteOne in Action
// === Delete Specified _id the document ===
db.users.deleteOne({ _id: ObjectId('507f1f77bcf86cd799439011') });
// === Delete Based on Specified Criteria(First match)===
db.logs.deleteOne({ level: 'debug' });
// === mongoose Equivalent ===
const result = await Product.deleteOne({ sku: 'PHONE-001' });
console.log(result.deletedCount); // 1
4. deleteMany: Batch Deletion
Concept Description: deleteMany deletes all documents that match the filter criteria and is the core method for batch data cleanup. Unlike deleteOne, which deletes only the first matching document, deleteMany can delete tens of thousands of documents at once. Common use cases include cleaning up expired logs, deleting data for deactivated users, and removing test data.
How It Works: deleteMany First, it retrieves all matching documents, then deletes them one by one. The deletion process is not transactional—if it fails midway, the documents that have already been deleted will not be restored. For bulk deletions, it is recommended to execute them in batches to avoid locking the collection for an extended period.
| Dimension | deleteOne | deleteMany |
|---|---|---|
| Match Range | First Match | All Matches |
| Number of Deletions | 0 or 1 | 0 to N |
| Use Cases | Delete a Single Item | Bulk Cleanup |
| Risk | Low | Moderate (significant impact from user error) |
// === deleteMany Basic Usage ===
db.products.deleteMany({ category: 'Discontinued' });
// === Delete all expired orders ===
db.orders.deleteMany({
expiryDate: { $lt: new Date() }
});
// === Delete Multiple Documents That Meet Specific Criteria ===
db.logs.deleteMany({
level: { $in: ['debug', 'info'] },
createdAt: { $lt: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000) }
});
▶ Example 2: deleteMany in Practice
// === Cleanup 30 Entries from a few days ago ===
const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);
const result = await Log.deleteMany({ createdAt: { $lt: thirtyDaysAgo } });
console.log(`Deleted ${result.deletedCount} old logs`);
// === Delete the session of a logged-out user ===
await Session.deleteMany({ userId: deletedUserId });
// === Delete all documents from the entire collection(Use with caution!)===
db.products.deleteMany({});
// ⚠️ This will delete products All documents in the collection
5. findOneAndDelete Atomic Return
Concept Description: findOneAndDelete is a special deletion method that returns the content of the deleted document at the same time it deletes it. This resolves the race condition associated with "query first, then delete"—the traditional approach requires first findOne retrieving the document and then deleteOne deleting it, during which time the document may be modified or deleted by other operations. findOneAndDelete combines the query and delete into a single atomic operation.
How It Works: findOneAndDelete performs an atomic operation at the document level: locate the matching document → record the document content → delete the document → return the recorded content. By default, it returns the document’s state prior to deletion; the projection option can be used to control which fields are returned.
graph TB
A[Need to delete and retrieve a document] --> B{Method Selection}
B --> C[❌ Check First, Then Delete<br/>findOne + deleteOne<br/>Competitive Conditions Risk]
B --> D[✅ findOneAndDelete<br/>Atomic Manipulation<br/>No risk of competition]
B --> E[✅ findOneAndDelete + sort<br/>Atomic Manipulation + Order<br/>FIFO Queue]
style D fill:#d4edda
style E fill:#d4edda
| Advantage | Description |
|---|---|
| Atomicity | Query and delete are performed in a single operation, avoiding race conditions |
| Return Document | Retrieve the deleted document directly, without the need for a secondary query |
| Sorting Support | Enables ordered consumption when used with the sort option |
| Use Cases | Queue tasks, message consumption, inventory deduction |
// === findOneAndDelete Atomic Manipulation ===
const deletedDoc = db.products.findOneAndDelete({ sku: 'PHONE-001' });
// Restore Deleted Documents(Status before default deletion)
console.log(deletedDoc);
// { _id: ..., sku: 'PHONE-001', title: 'Phone', price: 599, ... }
// === Return if it does not exist null ===
const result = db.products.findOneAndDelete({ sku: 'NOT_EXIST' });
console.log(result); // null
| Advantage | Description |
|---|---|
| Atomicity | Query and delete are performed in a single operation, avoiding race conditions |
| Return Document | Retrieve the deleted document directly, without the need for a secondary query |
| Use Cases | Queue tasks, message consumption, inventory deduction |
▶ Example 3: findOneAndDelete in Action
// === Scene:Message Queue(FIFO)===
const message = await Queue.findOneAndDelete(
{ status: 'pending' },
{ sort: { createdAt: 1 } } // Consume the oldest ones first
);
// === Scene:Claim a Mission ===
const task = await Task.findOneAndDelete({
status: 'available',
assignee: null
});
if (task) {
console.log(`Claimed task: ${task._id}`);
}
// === mongoose Equivalent ===
const message = await Queue.findOneAndDelete(
{ status: 'pending' },
{ sort: { createdAt: 1 } }
);
6. drop Collection and dropDatabase
Conceptual Explanation: drop and dropDatabase are the most thorough deletion operations—drop deletes an entire collection (including all documents and indexes), and dropDatabase deletes the entire database (including all collections). Unlike deleteMany({}), the drop operation not only deletes the data but also deletes the collection’s metadata (index definitions, schema validation rules, capped settings, etc.).
Comparative Analysis:
| Dimension | deleteMany({}) | drop() | dropDatabase() |
|---|---|---|---|
| Deletion Scope | All documents in the collection | The entire collection | The entire database |
| Retain Index | ✅ Retain | ❌ Delete All | ❌ Delete All |
| Keep "capped" setting | ✅ Keep | ❌ Delete | ❌ Delete |
| Keep Schema Validation | ✅ Keep | ❌ Delete | ❌ Delete |
| Speed | Slow (deletes one by one) | Fast (frees up space immediately) | Fast |
| Recoverability | Can be recovered via the oplog | Extremely difficult to recover | Extremely difficult to recover |
// === Delete Set ===
db.products.drop();
// true(Success)or false(The set does not exist)
// === Delete the database ===
db.dropDatabase();
// { "dropped" : "shopdb", "ok" : 1 }
// === Use with caution: Delete all data from the entire collection but keep the collection itself ===
db.products.deleteMany({});
// Equivalent but preserves the set structure(Index、capped Settings)
▶ Example 4: Choosing Between drop and deleteMany
// Scene:Cleaning Up Temporary Test Sets
// ✅ Recommendations:drop(Delete Set+Index,Clean)
db.test_results.drop();
// ✅ Preserve the collection structure:deleteMany(Clear the document only)
db.user_sessions.deleteMany({});
7. Node.js + Mongoose: Connecting to MongoDB
Concept Overview: Mongoose is the most popular MongoDB ODM (Object Document Modeling) in the Node.js ecosystem, offering advanced features such as schema definition, data validation, middleware, and join queries. This section begins with connecting to MongoDB and gradually introduces the core concepts of Mongoose.
How It Works: The process by which Mongoose connects to MongoDB consists of the following steps: creating a connection instance → establishing a TCP connection → authentication (if required) → selecting a database → initializing the connection pool → triggering the connected event. By default, Mongoose maintains a connection pool (typically 5–100 connections) and reuses connections to avoid frequently establishing and closing TCP connections.
sequenceDiagram
participant App as Node.js Applications
participant Mongoose as mongoose
participant Mongo as MongoDB
App->>Mongoose: mongoose.connect(uri)
Mongoose->>Mongo: Establish TCP Connect
Mongo-->>Mongoose: Connection Confirmation
Mongoose->>Mongo: Certification(If you need)
Mongo-->>Mongoose: Authentication Successful
Mongoose->>Mongoose: Initialize the connection pool
Mongoose-->>App: Trigger 'connected' Event
Note over App,Mongoose: Connection Ready,Executable CRUD
| Connection Method | URI Format | Use Cases |
|---|---|---|
| Local Standalone | mongodb://localhost:27017/shopdb |
Development Environment |
| Atlas Cloud | mongodb+srv://user:pass@cluster0.mongodb.net/mydb |
Production/Team |
| Instance Set | mongodb://host1,host2,host3/shopdb?replicaSet=rs0 |
Production Environment |
| Docker | mongodb://192.168.1.100:27017/shopdb |
Containerized Deployment |
(1) Install Mongoose
npm install mongoose --save
(2) Connecting to MongoDB
// === Basic Connections ===
const mongoose = require('mongoose');
async function connectDB() {
await mongoose.connect('mongodb://localhost:27017/shopdb');
console.log('✅ MongoDB connected');
}
connectDB().catch(err => console.error('❌ Connection error:', err));
(3) Connection Options
Concept Explanation: Mongoose connection options control key parameters that govern connection behavior—such as timeout, connection pool size, and authentication methods. These options must be configured appropriately in production environments; otherwise, they may result in connection leaks, timeout failures, or authentication errors.
Key Parameter Descriptions:
| Parameter | Default Value | Description | Production Recommendation |
|---|---|---|---|
serverSelectionTimeoutMS |
30000 | Server selection timeout (milliseconds) | 5000 |
socketTimeoutMS |
30,000 | Socket timeout | 45,000 |
maxPoolSize |
100 | Maximum connection pool size | 50–100 |
minPoolSize |
0 | Minimum connection pool size | 5 |
heartbeatFrequencyMS |
10,000 | Heart Rate Monitoring Frequency | 10,000 |
retryWrites |
true | Automatic write retry | true |
authSource |
admin | Authentication Database | As configured |
Connection Pooling Principle: Mongoose maintains a TCP connection pool to avoid the overhead of frequently creating and destroying connections. Each concurrent request retrieves a connection from the pool and returns it once the request is complete. maxPoolSize Control the maximum number of concurrent connections—setting this value too low will cause requests to queue up, while setting it too high will consume excessive server resources.
// === Complete Connection Configuration ===
await mongoose.connect('mongodb://localhost:27017/shopdb', {
// Server selection timeout
serverSelectionTimeoutMS: 5000,
// Socket Timeout
socketTimeoutMS: 45000,
// Connection Pool Size
maxPoolSize: 50,
minPoolSize: 5,
// Automatic Reconnection
autoReconnect: true,
// Certification(If enabled)
user: 'admin',
pass: 'password',
// Certification Database
authSource: 'admin'
});
(4) Connect to Atlas
// === Atlas Concatenate Strings ===
await mongoose.connect(
'mongodb+srv://user:pass@cluster0.mongodb.net/mydb?retryWrites=true&w=majority'
);
// === With environment variables ===
require('dotenv').config();
await mongoose.connect(process.env.MONGODB_URI);
▶ Example 5: Complete Connection Management
// db.js
const mongoose = require('mongoose');
const connectDB = async () => {
try {
const conn = await mongoose.connect(process.env.MONGODB_URI || 'mongodb://localhost:27017/shopdb', {
serverSelectionTimeoutMS: 5000,
maxPoolSize: 50
});
console.log(`✅ MongoDB connected: ${conn.connection.host}`);
// Listening for Connection Events
mongoose.connection.on('error', (err) => console.error('❌ MongoDB error:', err));
mongoose.connection.on('disconnected', () => console.warn('⚠️ MongoDB disconnected'));
mongoose.connection.on('reconnected', () => console.log('🔄 MongoDB reconnected'));
} catch (err) {
console.error('❌ Connection failed:', err.message);
process.exit(1);
}
};
const disconnectDB = async () => {
await mongoose.disconnect();
console.log('MongoDB disconnected');
};
module.exports = { connectDB, disconnectDB, mongoose };
8. Mongoose Schema Definition
Concept Explanation: A schema is a core concept in Mongoose—it defines a document’s field types, validation rules, default values, indexes, and more. Although MongoDB itself is schema-less, Mongoose provides schema constraints at the application layer to prevent dirty data and type errors. Once defined, a schema is compiled into a model, which serves as the interface for interacting with the database.
How It Works: Schema → Model → Document is Mongoose’s three-tier architecture. The Schema defines the structure (field types and validation); the Model is the compiled result of the Schema (corresponding to a collection); and the Document is an instance of the Model (corresponding to a document). The Schema does not directly interact with the database; CRUD operations can only be performed through the Model.
Schema Options: The second parameter of the Schema constructor controls global behavior—timestamps: true automatically adds createdAt/updatedAt, strict: true ignores undeclared fields, and versionKey: false removes the __v version key.
graph LR
A[Schema<br/>Define Field Types+Verification] -->|mongoose.model| B[Model<br/>Interfaces for Operation Sets]
B -->|new Model| C[Document<br/>An Example Document]
B -->|Model.find| D[Search Results<br/>Document Array]
style A fill:#cce5ff
style B fill:#d4edda
| Schema Field Options | Type | Description | Example |
|---|---|---|---|
type |
Constructor | Field type | String, Number, Date |
required |
Boolean/Array | Required | [true, 'Email is required'] |
default |
Any/Function | Default Value | Date.now, 0, true |
unique |
Boolean | Whether to create a unique index | true |
index |
Boolean/Object | Create Index | true, { sparse: true } |
enum |
Array | List of allowed values | ['pending', 'paid'] |
min / max |
Number | Value Range | min: 0, max: 999999 |
minlength / maxlength |
Number | String length range | minlength: 3 |
match |
RegExp | Regular Expression Validation | /^.+@.+$/ |
select |
Boolean | Whether the default query returns | false (e.g., passwordHash) |
validate |
Function | Custom validation function | v => v.length >= 8 |
get / set |
Function | Virtual getter/setter | get: v => v.toString() |
(1) Basic Schema
// === Schema Defining the User Model ===
const UserSchema = new mongoose.Schema({
// Field Definitions
email: {
type: String,
required: true,
unique: true,
lowercase: true,
trim: true
},
username: {
type: String,
required: true,
unique: true,
minlength: 3,
maxlength: 30
},
passwordHash: {
type: String,
required: true,
select: false // The default query returns no results.
},
age: {
type: Number,
min: 0,
max: 150
},
role: {
type: String,
enum: ['customer', 'admin', 'moderator'],
default: 'customer'
},
isActive: {
type: Boolean,
default: true
}
}, {
// Schema Options
timestamps: true, // Auto-add createdAt/updatedAt
collection: 'users', // Explicitly Specify the Set Name
strict: true, // Strict Mode(Do not save undeclared fields)
versionKey: false // Disable __v
});
(2) Schema Types
const ProductSchema = new mongoose.Schema({
// String
sku: String,
// Numbers
stock: Number,
price: mongoose.Schema.Types.Decimal128,
// Date
releaseDate: Date,
// Boolean
isActive: Boolean,
// Array
tags: [String],
// Nested Documents
specs: {
screen: String,
battery: String
},
// Buffer(Binary)
thumbnail: Buffer,
// ObjectId Quote
categoryId: mongoose.Schema.Types.ObjectId,
// Mixed Type(Any)
metadata: mongoose.Schema.Types.Mixed,
// Map(Key-value pairs)
translations: {
type: Map,
of: String
}
});
▶ Example 6: Comprehensive Schema Design
const ProductSchema = new mongoose.Schema({
sku: {
type: String,
required: [true, 'SKU is required'],
unique: true,
index: true,
match: /^[A-Z0-9-]+$/
},
title: {
type: String,
required: true,
trim: true,
maxlength: 200
},
description: {
type: String,
maxlength: 5000
},
price: {
type: mongoose.Schema.Types.Decimal128,
required: true,
min: 0,
get: v => v ? v.toString() : v // Serialize to a string
},
category: {
type: String,
enum: ['Electronics', 'Books', 'Clothing', 'Home'],
required: true,
index: true
},
tags: [String],
attributes: {
type: Map,
of: mongoose.Schema.Types.Mixed,
default: {}
},
stock: {
type: Number,
default: 0,
min: 0
},
rating: {
type: Number,
default: 0,
min: 0,
max: 5
},
isActive: {
type: Boolean,
default: true,
index: true
}
}, {
timestamps: true,
toJSON: { virtuals: true, getters: true },
toObject: { virtuals: true }
});
// Virtual Fields
ProductSchema.virtual('isInStock').get(function() {
return this.stock > 0;
});
// Index
ProductSchema.index({ category: 1, price: 1 });
ProductSchema.index({ title: 'text', description: 'text' });
const Product = mongoose.model('Product', ProductSchema);
9. mongoose CRUD Operations
Concept Explanation: CRUD (Create/Read/Update/Delete) is the basic pattern for database operations. Mongoose provides two CRUD styles—Model static methods (such as Model.create() and Model.find()) and Document instance methods (such as doc.save() and doc.remove()). Static methods operate directly on the database, while instance methods first modify the in-memory object and then synchronize the changes to the database.
Comparative Analysis:
| Dimension | Model static methods | Document instance methods |
|---|---|---|
| Calling method | Model.create(data) |
new Model(data); doc.save() |
| Trigger Verification | ✅ | ✅ |
| Triggered Middleware | Partial | ✅ All |
| Return Value | Document or result object | Document |
| Use Cases | Simple CRUD | Complex Business Logic |
graph TB
A[mongoose CRUD] --> B[Create<br/>create() / save()]
A --> C[Read<br/>find() / findOne() / findById()]
A --> D[Update<br/>updateOne() / findByIdAndUpdate()]
A --> E[Delete<br/>deleteOne() / findByIdAndDelete()]
style A fill:#cce5ff
(1) Create
// === model.create() Create a Single Document ===
const user = await User.create({
email: 'alice@example.com',
username: 'alice_chen',
passwordHash: 'hashed_password',
age: 28
});
console.log(user._id); // ObjectId
// === Create Multiple Documents ===
const users = await User.create([
{ email: 'bob@example.com', username: 'bob' },
{ email: 'charlie@example.com', username: 'charlie' }
]);
// === new + save Pattern ===
const user = new User({
email: 'alice@example.com',
username: 'alice_chen'
});
await user.save();
(2) Read
// === find Search for multiple ===
const users = await User.find({ isActive: true });
// === findOne Query a single ===
const user = await User.findOne({ email: 'alice@example.com' });
// === findById Through _id Search ===
const user = await User.findById('507f1f77bcf86cd799439011');
// === Chain Query ===
const products = await Product.find({ category: 'Electronics' })
.select('sku title price')
.sort({ price: 1 })
.limit(20)
.lean();
(3) Update
// === findByIdAndUpdate ===
const user = await User.findByIdAndUpdate(
userId,
{ $set: { lastLoginAt: new Date() } },
{ new: true, runValidators: true } // Return to the updated document
);
// === updateOne ===
const result = await User.updateOne(
{ email: 'alice@example.com' },
{ $set: { age: 29 } }
);
// === save() Replace the entire document ===
const user = await User.findById(userId);
user.age = 30;
await user.save();
(4) Delete
// === findByIdAndDelete ===
const user = await User.findByIdAndDelete(userId);
// === deleteOne ===
const result = await User.deleteOne({ email: 'alice@example.com' });
// === deleteMany ===
const result = await User.deleteMany({ isActive: false });
▶ Example 7: A Complete CRUD Practical Exercise
// === 1. Create a User ===
const alice = await User.create({
email: 'alice@example.com',
username: 'alice_chen',
passwordHash: await bcrypt.hash('password123', 10),
age: 28
});
// === 2. Query User ===
const users = await User.find({ age: { $gte: 18 } })
.select('email username age')
.lean();
// === 3. Update User ===
await User.updateOne(
{ _id: alice._id },
{ $set: { lastLoginAt: new Date() }, $inc: { loginCount: 1 } }
);
// === 4. Delete Test User ===
await User.deleteMany({ email: { $regex: '@test\\.com$' } });
10. Troubleshooting Common Errors
Concept Overview: The four most common types of errors encountered when developing with Node.js and Mongoose are: connection errors (ECONNREFUSED), unique index conflicts (E11000), schema validation failures (ValidationError), and type conversion failures (CastError). Understanding the root causes of these errors and how to handle them is a fundamental skill in Mongoose development.
Error Handling Strategy: Connection errors require a retry mechanism; unique index conflicts require an UPSERT or front-end validation; schema validation failures require improved form validation; and CastErrors require checking the ObjectId format. All errors should be caught at the application layer and return user-friendly error messages—do not expose the raw MongoDB error stack trace to front-end users.
graph TB
A[mongoose Error] --> B[Connection Error<br/>ECONNREFUSED]
A --> C[Unique Index Conflict<br/>E11000]
A --> D[Verification Failed<br/>ValidationError]
A --> E[Type conversion failed<br/>CastError]
B --> B1[Inspection mongod Enable or Disable<br/>Check the connection string]
C --> C1[Usage upsert<br/>Front-End Uniqueness Validation]
D --> D1[Improve Schema Verification<br/>Front-End Form Validation]
E --> E1[Inspection ObjectId Format<br/>Verify Parameter Types]
| Error | Cause | Solution |
|---|---|---|
MongooseServerSelectionError: connect ECONNREFUSED |
MongoDB is not running | brew services start mongodb-community@7.0 |
MongoError: E11000 duplicate key error |
Unique Index Conflict | Check for duplicate field values |
ValidationError: Path 'email' is required |
Required fields are missing | Please fill in the required fields |
CastError: Cast to ObjectId failed |
_id format error | Check the ObjectId string format |
❓ FAQ
deleteOne or findOneAndDelete?findOneAndDelete if you need to return the deleted document; use deleteOne if you just want to delete it. Both have comparable performance.mongoose.disconnect() or mongoose.connection.close() when the process exits to ensure that all connections are gracefully closed.undefined.lean()?lean() skips Mongoose document hydration and returns a pure JavaScript object. This results in a 3-5x performance boost, but you lose access to Mongoose document methods (such as save() and populate()). It is suitable for pure query APIs.Model.create and new Model + save?Model.create() syntax is more concise, while new + save is better suited for scenarios requiring step-by-step operations.📖 Summary
- deleteOne deletes a single document; deleteMany deletes multiple documents
- findOneAndDelete: An atomic operation that returns the deleted document; suitable for queues and task retrieval
dropdeletes a collection;dropDatabasedeletes a database- mongoose connect connects to MongoDB and supports both Atlas and local instances
- Schema: Defines data types, validations, default values, and indexes
- Model CRUD:create / find / findOne / findByIdAndUpdate / findByIdAndDelete
- Mongoose Document Methods: save() / lean() / populate() / toJSON()
📝 Exercises
- Basic Question (⭐): Use
deleteOneto delete the product with the SKU 'TEST-001'. - Basic Questions (⭐): Define the
Productmodel using Mongoose Schema (includingsku,title,price,category, andstock), and executecreate,find,update, anddeleteoperations. - Advanced Problem (⭐⭐): Write a Node.js script to periodically clean up expired orders that are more than 30 days old (using
deleteMany). - Advanced Problem (⭐⭐): Implement a message queue: Use
findOneAndDeletewithsort: { createdAt: 1 }to implement FIFO consumption. - Challenge (⭐⭐⭐): Implement a user registration feature using Mongoose, Schema, and middleware (including email uniqueness validation, bcrypt password hashing, and timestamping).



