Mongoose findOneAndUpdate Upsert _id null?

时间:2013-06-21 21:11:26

标签: mongodb mongoose

我想要一个方法来创建或更新策略的文档。搜索并尝试不同的技术,如this one,我为我的文档提出了一个null _id。使用findByIdAndUpdate也有类似的影响。

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

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不为空的东西?

8 个答案:

答案 0 :(得分:14)

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

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

// the rest stays the same...

答案 1 :(得分:5)

我遇到了同样的问题,无法找到让它发挥作用的方法。我最后编写了自己的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);

可能有更好的方法,但这对我有用。我可以做更新和插入,我没有在插入时得到null _id。

答案 2 :(得分:1)

我没有使用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;
}

答案 3 :(得分:1)

感谢JohnnyHK提供了有用的答案。我提出了一个单行,因为它经常被使用:

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

它依赖于以下要求:

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

答案 4 :(得分:1)

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

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

所以当你写:

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

idnull,它将插入一个以null作为ID的文档。

答案 5 :(得分:0)

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

{ upsert: true, new: true }

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

答案 6 :(得分:0)

由于我们现在可以在破坏JavaScript中的对象的同时添加默认属性,因此在检查文档是否存在的情况下,我发现这是通过现有_id进行查询或在_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"
    })
  }
}

答案 7 :(得分:-3)

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

  

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