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

Mongoose如何自动生成除默认_id外的另一套面向用户的ID?

用户Schema示例图

MongoDB 生成10-12位对外展示publish_id的实现方案

下面提供两种常用的落地方式,可根据你的业务需求选择:


方案1:随机字符串生成(适合无需ID有序的场景)

如果不需要publish_id按新增顺序递增,优先选这个方案,实现简单,ID无规律不容易被恶意遍历。

实现步骤

  • 给用户Schema新增publish_id字段,配置唯一索引避免重复
  • 编写10-12位随机字符串生成函数
  • 配置Mongoose pre('save') 中间件,新文档入库前自动生成并校验ID唯一性

代码示例

const mongoose = require('mongoose');

// 1. 定义用户Schema
const userSchema = new mongoose.Schema({
  // 你原有的字段保留
  username: { type: String, required: true },
  email: { type: String, required: true },
  // 新增publish_id字段
  publish_id: {
    type: String,
    unique: true,
    index: true,
    required: true
  }
});

// 2. 随机ID生成函数:10-12位大小写字母+数字组合
function generateRandomId() {
  const charSet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  const idLength = Math.floor(Math.random() * 3) + 10; // 长度10-12随机
  let id = '';
  for (let i = 0; i < idLength; i++) {
    id += charSet.charAt(Math.floor(Math.random() * charSet.length));
  }
  return id;
}

// 3. 前置中间件:新文档保存前生成ID
userSchema.pre('save', async function(next) {
  // 只有新增文档需要生成ID,更新操作跳过
  if (!this.isNew) return next();
  
  const maxRetry = 5; // 碰撞最多重试5次
  let retryTimes = 0;
  while (retryTimes < maxRetry) {
    const tempId = generateRandomId();
    // 校验ID是否已存在
    const isExist = await mongoose.model('User').findOne({ publish_id: tempId });
    if (!isExist) {
      this.publish_id = tempId;
      return next();
    }
    retryTimes++;
  }
  next(new Error('publish_id生成失败,请稍后重试'));
});

const User = mongoose.model('User', userSchema);

方案优势

  • 实现简单,无需额外维护集合
  • 62种字符的10位ID有8e17种组合,加上重试机制,碰撞概率可以忽略不计

方案2:自增序列生成(适合需要ID有序的场景)

如果需要publish_id是纯数字、按新增顺序递增,用原子自增计数器的方案,完全不会出现重复。

实现步骤

  • 新增计数器集合,专门存储各业务的自增序列值
  • 编写原子获取下一个序列值的函数
  • 保存用户文档前调用函数生成ID

代码示例

const mongoose = require('mongoose');

// 1. 定义计数器Schema
const counterSchema = new mongoose.Schema({
  _id: { type: String, required: true },
  // 初始值设为10亿,保证生成的ID最少10位
  sequence: { type: Number, default: 1000000000 }
});
const Counter = mongoose.model('Counter', counterSchema);

// 2. 原子获取下一个publish_id
async function getNextPublishId() {
  const res = await Counter.findOneAndUpdate(
    { _id: 'user_publish_id' },
    { $inc: { sequence: 1 } }, // 原子自增1
    { new: true, upsert: true } // 不存在则自动创建计数器
  );
  return res.sequence.toString();
}

// 3. 用户Schema的pre中间件
userSchema.pre('save', async function(next) {
  if (!this.isNew) return next();
  try {
    this.publish_id = await getNextPublishId();
    next();
  } catch (err) {
    next(err);
  }
});

方案优势

  • ID完全有序,无碰撞问题,生成效率稳定
  • 适合高并发场景,不会出现重试消耗

通用注意事项

  • 无论用哪种方案,都必须给publish_id加上unique唯一索引,从数据库层面避免重复数据入库
  • 如果对外暴露的ID不希望被遍历爬取,不要用方案2的连续自增ID
  • 可根据业务需求调整ID生成规则,比如添加固定前缀、只保留数字等

内容的提问来源于stack exchange,提问作者Yellow Clock

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 22:15:03