You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 02:21:16