【问题标题】:Upserts in mongodb when using custom _id values使用自定义 _id 值时 mongodb 中的 Upserts
【发布时间】:2012-02-09 07:00:33
【问题描述】:

如果文档不存在,我需要插入它。我知道“upsert”选项可以做到这一点,但我有一些特殊需求。

首先,我只需要创建带有 _id 字段的文档,但前提是它不存在。我的 _id 字段是我生成的数字(不是 ObjectId)。如果我使用“upsert”选项,那么我会得到“Mod on _id not allowed”

db.mycollection.update({ _id: id }, { _id: id }, { upsert: true });

我知道我们不能在 $set 中使用 _id。

所以,我的问题是:如果有任何方法可以在 mongodb 中原子地“创建如果不存在”?

编辑: 正如@Barrie 所提议的那样(使用 nodejs 和 mongoose):

var newUser = new User({ _id: id });
newUser.save(function (err) {               
    if (err && err.code === 11000) {            
            console.log('If duplicate key the user already exists', newTwitterUser);
        return;
    }
    console.log('New user or err', newTwitterUser);
});

但我仍然想知道这是否是最好的方法。

【问题讨论】:

  • 尝试使用save 操作?即 db.collection.save({"_id": your_id})
  • 如果已存在,则保存失败并出现重复键错误

标签: mongodb


【解决方案1】:

你可以只使用 insert()。如果您指定的 _id 的文档已经存在,则 insert() 将失败,不会修改任何内容 - 因此,“如果它不存在则创建”是默认情况下它已经在使用 insert() 和用户时所做的事情 -创建_id。

【讨论】:

  • 这仍然不能处理文档在插入之后和后续更新之前被删除的竞争条件。
【解决方案2】:

我遇到了同样的问题,但找到了更好的解决方案来满足我的需求。如果您只是从更新对象中删除 _id 属性,则可以使用相同的查询样式。因此,如果一开始您遇到以下错误:

db.mycollection.update({ _id: id }, {$set: { _id: id, name: 'name' }}, { upsert: true });

改用这个:

db.mycollection.update({ _id: id }, {$set: { name: 'name' }}, { upsert: true });

这更好,因为它适用于插入和更新。

【讨论】:

  • 这是否适用于不是 ObjectID 的 _id,如问题中所述?
  • 是的。它会导致错误 Mod on _id not allowed 使用 _id 作为 ObjectID 或自定义值。因此,您必须始终从 $set 对象中删除 _id。
  • @NateBarr :工作就像一个魅力!谢谢:)
  • 但是如果我使用 shortid 生成并且这种类型的 upsert 会从 mongo 创建一个 id 会发生什么,如果你习惯了 shortid 这将是一个问题
【解决方案3】:

更新:在没有$setOnInsert 的情况下可以使用 _id 进行更新插入,正如上面@Barrie 所解释的那样。

诀窍是将$setOnInsert:{_id:1} 与 upsert 一起使用,这样 _id 只会在插入时被写入,而永远不会用于更新。

Only, there was a bug preventing this from working until v2.6 - 我刚刚在 2.4 上尝试过,但无法正常工作。

我使用的解决方法是使用另一个具有唯一索引的 ID 字段。例如。 $setOnInsert:{myId:1}.

【讨论】:

  • setOnInsert 为我工作。而巴里的回答,直接插入不会做更新工作; Nate Barr's answer, update by _id 限制了查找标准。我想做的是: db.coll.update({a:1, b: 2}, {$set: {c: 3, d: 4}, $setOnInsert: {_id: 'xxxx'}}, { upsert: true})
【解决方案4】:

请注意,当您插入一个简单的键 => 值对象(不是 $set 或其他)时,$setOnInsert 并不容易工作。 我需要使用它(在 PHP 中):

public function update($criteria , $new_object, array $options = array()){
    // In 2.6, $setOnInsert with upsert == true work with _id field
    if(isset($options['upsert']) && $options['upsert']){
        $firstKey = array_keys($new_object)[0];
        if(strpos($firstKey, '$')===0){
            $new_object['$setOnInsert']['_id'] = $this->getStringId();
        }
        //Even, we need to check if the object exists
        else if($this->findOne($criteria, ['_id'])===null){
            //In this case, we need to set the _id
            $new_object['_id'] = $this->getStringId();
        }

    }
    return parent::update($criteria, $new_object, $options);
}

【讨论】:

    猜你喜欢
    • 2018-11-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-12
    • 2016-12-15
    • 2023-03-19
    • 2018-04-10
    • 1970-01-01
    相关资源
    最近更新 更多