【问题标题】:Mongoose findOneAndUpdate Upsert _id null?猫鼬 findOneAndUpdate Upsert _id null?
【发布时间】:2013-06-19 02:57:39
【问题描述】:

我想要一个方法来创建或更新政策文档。搜索和尝试不同的技术,如this one,我为我的文档想出了一个空 _id。使用 findByIdAndUpdate 也有类似的效果。

我看到集合中插入了一个文档,但 _id 字段为空:

exports.savePolicy = function (plcy, callback) {
    console.log('priority is : ' + plcy.priority)
    try {
        var policy = new Policy(plcy);
        var query = {_id: plcy._id};  //this may be null
        var update = {
            name: plcy.name || defaults.policyDefaults.name,
            longDescription: plcy.longDescription || defaults.policyDefaults.longDescription,
            shortDescription: plcy.shortDescription || defaults.policyDefaults.shortDescription,
            priority: plcy.priority, colorHex: plcy.colorHex || defaults.policyDefaults.colorHex,
            settings: plcy.settings || [],
            parentPolicyId: plcy.parentPolicyId || null
        }

        Policy.findOneAndUpdate(query, update, {upsert: true}, function (err, data) {
            callback(err, data);
        });

    } catch (e) {
        log.error('Exception while trying to save policy: ' + e.message);
        callback(e, null);
    }

当 _id 不是更新时,有什么办法可以让 _id 不为空?

【问题讨论】:

  • 你有没有想过这个问题?我遇到了同样的问题。我最终手动检查了 _id,然后调用 findOneAndUpdate 进行更新或调用 create() 获取新记录。

标签: mongodb mongoose


【解决方案1】:

null 在 MongoDB 中是一个有效的 _id 值,因此如果您不希望在新文档中使用它,则必须确保将 null 值替换为 query 中的新 ObjectID

var query = {_id: plcy._id};
if (!query._id) {
    query._id = new mongoose.mongo.ObjectID();
}

// the rest stays the same...

【讨论】:

  • 我们使用 ObjectIds 来推断创建时间戳。在服务器端创建 ObjectId 而不是客户端创建它们不是最好的吗?
【解决方案2】:

我遇到了同样的问题,但无法找到让它工作的方法。我最终像这样编写了自己的 upsert 方法....

var upsert = function(model, data, f){
  if (!data._id) {
    model.create(data, f);
  } else {
    var id = data._id;
    delete data._id;
    model.findOneAndUpdate({_id: id}, data, f);
  }
}

这让我可以用一行代码为我的任何模型调用它...

upsert(Team, team, f);

可能有更好的方法,但这对我有用。我可以进行更新和插入,但插入时不会得到空 _id。

【讨论】:

  • 嗯,你可以使用类似的方法来解决这个问题:Model.findOneAndUpdate({ _id: id || Mongoose.Types.ObjectId() }, {$set: attrs}, { upsert: true , 新: true })
【解决方案3】:

感谢 JohnnyHK 在上面提供的有用答案。我想出了一个单行,因为它被频繁使用:

query = args._id ? { _id: args._id } : { _id: new ObjectId() };

它依赖于以下要求:

const ObjectId = require('mongodb').ObjectID;

【讨论】:

    【解决方案4】:

    我没有使用 Mongoose,但我遇到了与 MongoDB 类似的问题。对于插入新对象时的 Upsert 操作,MongoDB 将 null 设置为 _id

    我在打电话:

    findOneAndUpdate({my_id:'my_unique_id'}, obj, {upsert: true})
    

    其中obj._idundefined

    问题是_id 出现在键列表Object.keys(obj) 上。我发现我分配了obj._id = some_variable,其中some_variableundefined,这导致_id 出现在键列表中。

    我通过在 upsert 之前调用来应用解决方法:

    if (_.isUndefined(obj._id)) {
      delete obj._id;
    }
    

    【讨论】:

      【解决方案5】:

      这可能很有用(来自the docs):

      如果您在查询参数或替换文档中指定 _id 字段,MongoDB 会在插入的文档中使用该 _id 字段。

      所以当你写的时候:

      model.findOneAndUpdate({_id: id}, data, f);
      

      idnull,它会插入一个以null为id的文档。

      【讨论】:

        【解决方案6】:

        如果有 ID,则更新文档或在没有 ID 时创建新文档的最简单方法是:

        Model.findOneAndUpdate({ _id: id ?? new mongoose.Types.ObjectId() }, updates, { upsert: true });
        
        

        注意nullish coalescing operator (??) 的使用,它允许 0 作为有效 id。 updates 对象不需要包含 id 属性,因为 mongoose 将 add it automatically

        如果没有文档匹配过滤器,MongoDB 将通过组合过滤器和更新插入一个,如下所示。

        【讨论】:

          【解决方案7】:

          由于我们现在可以在 JavaScript 中解构对象时添加默认属性,因此在检查文档是否存在的情况下,我发现这是通过现有 _id 查询或在同样的操作,避免了null id问题:

          // someController.js 使用 POST 路由

          async function someController(req, res) {
            try {
            const {
              // the default property will only be used if the _id doesn't exist
              _id: new mongoose.Types.ObjectId(),
              otherProp,
              anotherProp
            } = req.body;
            const createdOrUpdatedDoc = await SomeModel.findOneAndUpdate(
              {
                _id
              },
              {
                otherProp,
                anotherProp
              },
              {
                new: true,
                upsert: true
              }
            ).exec();
            return res.json(201).json(createdOrUpdatedDoc);
            } catch (error) {
              return res.json(400).send({
                error,
                message: "Could not do this"
              })
            }
          }
          

          【讨论】:

            【解决方案8】:

            尝试将'new'参数设置为true:

            { upsert: true, new: true }
            

            更多信息:https://github.com/Automattic/mongoose/issues/2939

            【讨论】:

              【解决方案9】:

              尝试在更新调用中将 upsert 参数设置为 true。来自 mongoid 文档:http://docs.mongodb.org/manual/reference/method/db.collection.update/#update-parameter

              可选。如果设置为 true,则在没有文档与查询条件匹配时创建一个新文档。默认值为false,没有找到匹配时不插入新文档。

              【讨论】:

                猜你喜欢
                • 2021-09-25
                • 2017-10-05
                • 2018-03-11
                • 2018-07-15
                • 2020-11-13
                • 2013-02-13
                • 2016-05-08
                • 2018-11-18
                • 2021-04-18
                相关资源
                最近更新 更多