如何在Mongoose中实现多格式自增ID?原auto-increment包已停更
实现多格式专属自增ID(学生/教师)
不用依赖已停更的auto-increment包,我们可以自己通过MongoDB的原子操作维护一个计数器集合,给不同类型的ID单独管理自增序列,完美实现学生5位(00000起)、教师3位(000起)的格式需求。
1. 先建计数器模型
创建一个Counter集合,用来记录每种ID类型的当前最大值:
const mongoose = require('mongoose'); const Schema = mongoose.Schema; const counterSchema = new Schema({ type: { type: String, required: true, unique: true // 保证每个ID类型只存一条记录 }, seq: { type: Number, default: -1 // 初始设为-1,第一次自增后得到0,对应00000/000 } }); module.exports = mongoose.model('Counter', counterSchema);
2. 修改Student模型,实现5位自增ID
方案A:ID存字符串(保留前导零)
如果需要直接存储00000、00001这类带前导零的格式,把studentId改成String类型,通过前置钩子自动生成:
const mongoose = require('mongoose'); const Counter = require('./Counter'); // 引入计数器模型 const Schema = mongoose.Schema; const studentSchema = new Schema({ studentId: { type: String, unique: true // 确保ID不重复 }, name: { type: String, required: true, }, password: { type: String, required: true, }, }); // 前置保存钩子:新建学生时自动生成ID studentSchema.pre('save', async function(next) { if (!this.studentId) { try { // 原子操作:找到学生类型计数器,自增并返回新值,不存在则自动创建 const counter = await Counter.findOneAndUpdate( { type: 'student' }, { $inc: { seq: 1 } }, { new: true, upsert: true } ); // 格式化为5位数字,不足补零 this.studentId = counter.seq.toString().padStart(5, '0'); next(); } catch (err) { next(err); } } else { next(); } }); module.exports = mongoose.model("Student", studentSchema);
方案B:ID存数字(输出时补零)
如果坚持用Number类型存储(实际存的是0、1、2...),可以在查询后通过虚拟字段输出格式化后的ID:
const mongoose = require('mongoose'); const Counter = require('./Counter'); const Schema = mongoose.Schema; const studentSchema = new Schema({ studentId: { type: Number, unique: true }, name: { type: String, required: true, }, password: { type: String, required: true, }, }); studentSchema.pre('save', async function(next) { if (!this.studentId) { try { const counter = await Counter.findOneAndUpdate( { type: 'student' }, { $inc: { seq: 1 } }, { new: true, upsert: true } ); this.studentId = counter.seq; // 存原始数字 next(); } catch (err) { next(err); } } else { next(); } }); // 虚拟字段:输出时自动补零为5位格式 studentSchema.virtual('formattedStudentId').get(function() { return this.studentId.toString().padStart(5, '0'); }); module.exports = mongoose.model("Student", studentSchema);
使用时,查询到学生文档后,通过student.formattedStudentId就能拿到00000这类格式的ID。
3. 教师ID的实现(同理)
教师ID是3位格式,只需要把计数器类型改成'teacher',格式化时用padStart(3, '0')即可:
const mongoose = require('mongoose'); const Counter = require('./Counter'); const Schema = mongoose.Schema; const teacherSchema = new Schema({ teacherId: { type: String, unique: true }, name: { type: String, required: true }, // 其他字段按需添加 }); teacherSchema.pre('save', async function(next) { if (!this.teacherId) { try { const counter = await Counter.findOneAndUpdate( { type: 'teacher' }, { $inc: { seq: 1 } }, { new: true, upsert: true } ); this.teacherId = counter.seq.toString().padStart(3, '0'); next(); } catch (err) { next(err); } } else { next(); } }); module.exports = mongoose.model("Teacher", teacherSchema);
关键注意点
- 用
findOneAndUpdate的原子操作,避免并发创建文档时出现ID重复的问题 - 计数器的
type字段设为唯一索引,确保不同类型的ID序列互不干扰 - 初始
seq设为-1,是为了第一次自增后得到0,对应00000/000的起始格式
内容的提问来源于stack exchange,提问作者Mohammad ARZ
相关产品推荐
相关产品推荐

