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

Mongoose中如何在保存文档前获取该文档的_id值

提问内容

我有一个简单的控制器用于为用户创建帖子,该模型关联了另一个Schema。当我尝试新建帖子时,需要获取该帖子的id以关联其他Schema。

Schema代码如下:

const mongoose = require("mongoose");
const User = require("./User");
const View = require("./View");

const ArticleSchema = new mongoose.Schema({
  title: {
    type: String,
    required: true,
    trim: true,
  },
  body: {
    type: String,
    required: true,
  },
  status: {
    type: String,
    default: "public",
    enum: ["public", "private"],
  },
  user: {
    type: mongoose.Schema.Types.ObjectId,
    ref: "User",
  },
  views: {
    type: mongoose.Schema.Types.ObjectId,
    ref: "View",
  },
  createdAt: {
    type: Date,
    default: Date.now,
  },
});

module.exports = mongoose.model("Article", ArticleSchema);

关联user字段没有问题,因为该值已经存储在内存中。但views字段需要对应文章文档的postId,不先创建文档就无法获取到该id。

我的创建帖子控制器代码如下:

module.exports.createArticleController = async function (req, res) {
  try {
    req.body.user = req.User._id;
    const article = await Article.create(req.body).exec()
    res.redirect(`/${article.title}/${article._id}`);
  } catch (e) {
    console.error(e);
  }
};

我的问题是:如何在执行model.create()的过程中获取生成的id,从而将view关联到该id,能否通过this运算符实现?我不希望在创建完成后再执行更新操作。


解答

实现方案

有两种方案可以避免创建完成后再执行更新操作:

方案1:预生成文档ID,一次性写入

Mongoose 构造模型实例时就会自动生成_id,不需要等待数据库写入完成,你可以先构造实例、拿到ID处理关联后再统一存入数据库:

const mongoose = require("mongoose");
const View = require("./View");

module.exports.createArticleController = async function (req, res) {
  try {
    // 构造Article实例,此时_id已经自动生成
    const article = new Article({
      ...req.body,
      user: req.User._id
    });
    // 创建View文档,关联当前Article的ID
    const view = await View.create({
      article: article._id,
      // 补充View的其他初始字段
    });
    // 给Article的views字段赋值View的ID
    article.views = view._id;
    // 一次性保存Article到数据库
    await article.save();
    res.redirect(`/${article.title}/${article._id}`);
  } catch (e) {
    console.error(e);
    res.status(500).send('创建失败');
  }
};

该方式全程仅两次写入操作(一次View、一次Article),不需要对已创建的Article执行更新。

方案2:通过Schema pre中间件自动处理关联

如果所有Article创建时都需要自动关联View,可以把逻辑封装到Schema的pre('save')中间件中,控制器无需额外处理关联逻辑:
首先在Article的Schema文件中添加中间件:

const View = require("./View");

ArticleSchema.pre('save', async function(next) {
  // 仅新建文档时创建关联View
  if (this.isNew) {
    const view = await View.create({
      article: this._id, // this指向待保存的Article实例,可直接获取预生成的_id
      // 补充View的其他初始字段
    });
    this.views = view._id;
  }
  next();
});

之后控制器逻辑可以保持简洁:

module.exports.createArticleController = async function (req, res) {
  try {
    req.body.user = req.User._id;
    // create方法执行时会自动触发中间件处理关联
    const article = await Article.create(req.body);
    res.redirect(`/${article.title}/${article._id}`);
  } catch (e) {
    console.error(e);
    res.status(500).send('创建失败');
  }
};

注:你原代码中Article.create().exec()属于错误用法,create方法本身返回Promise,不需要调用exec(),exec仅适用于Query对象(如find()返回的查询对象)。

关于this运算符的问题

在Schema的pre save中间件中,this确实指向待保存的文档实例,可以直接拿到预生成的_id,也就是方案2的实现基础,完全可以满足你的需求。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.23 14:24:00