如何为Mongoose Schema中的internalId字段实现自增累计值
internalId for Mongoose Person Schema Let's solve your problem of adding a sequential auto-incrementing internalId to your Person documents. Using a global variable isn't reliable—restarting your server would reset the count, and you'd hit duplicate ID issues with multiple server instances. Instead, we'll use a dedicated counter collection to safely persist and increment the ID value.
Step 1: Create a Counter Schema & Model
First, we need a separate collection to track the current maximum ID for each model. This ensures the count stays intact across server restarts and handles concurrency safely with atomic database operations.
const mongoose = require('mongoose'); const Schema = mongoose.Schema; // Counter schema to track auto-increment IDs const counterSchema = new Schema({ modelName: { type: String, required: true, unique: true // Ensures one counter per model }, currentId: { type: Number, default: 0 } }); const Counter = mongoose.model('Counter', counterSchema);
Step 2: Modify the Person Schema's Pre-Save Hook
Update your existing pre('save') hook to fetch and increment the counter before saving a new Person document. We'll use findOneAndUpdate—an atomic operation that prevents race conditions when multiple documents are created at the same time.
const personSchema = new Schema({ name: { type: String }, age: { type: Number }, internalId: { type: Number }, }); personSchema.pre('save', async function(next) { // Only run this logic for new documents if (this.isNew) { try { // Find or create the counter for Person model, then increment it const counter = await Counter.findOneAndUpdate( { modelName: 'Person' }, { $inc: { currentId: 1 } }, // Atomically increment the current ID { new: true, upsert: true } // Return updated doc; create if it doesn't exist ); // Assign the incremented ID to internalId this.internalId = counter.currentId; // Note: Mongoose automatically adds an `id` field (string version of _id) // So your original line `this.id = this._id;` is redundant and can be removed next(); } catch (err) { // Pass any errors to the next middleware next(err); } } else { // Skip for existing documents next(); } }); const Person = mongoose.model("Person", personSchema); exports.Person = Person; exports.Counter = Counter; // Export if you need to initialize counters manually
Key Notes & Considerations
- Persistence: The counter is stored in your database, so it won't reset when your server restarts.
- Concurrency Safety:
findOneAndUpdateis an atomic operation, so even if multiple requests try to create a Person at the same time, you won't get duplicateinternalIdvalues. - Existing Collections: If you already have Person documents in your database, you'll need to initialize the counter manually. Run this once (e.g., in a script or your server startup):
// Replace 100 with the highest existing internalId in your Person collection await Counter.findOneAndUpdate( { modelName: 'Person' }, { currentId: 100 }, { upsert: true } ); - Reusability: You can reuse this counter pattern for any other models that need auto-incrementing IDs—just use a different
modelNamevalue.
内容的提问来源于stack exchange,提问作者colla




