使用自定义_id值时在mongodb中进行upserts

时间:2012-01-13 00:32:37

标签: mongodb

如果文档不存在,我需要插入文档。我知道“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);
});

但我仍然想知道这是否是最佳方式。

4 个答案:

答案 0 :(得分:25)

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

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

改为使用:

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

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

答案 1 :(得分:5)

更新:使用_id的upsert可以在没有$setOnInsert的情况下完成,正如@Barrie上面的解释一样。

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

Only, there was a bug preventing this from working until v2.6 - 我刚刚在2.4上尝试过它并且它无效。

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

答案 2 :(得分:4)

你可以使用insert()。如果你指定的_id文件已经存在,那么insert()将失败,什么都不会被修改 - 所以“如果它不存在则创建”就是你在用户使用insert()时默认做的事情 - 创建_id。

答案 3 :(得分:1)

请注意,当您插入一个简单的密钥时,$ setOnInsert不会轻易工作=>值对象(不是$ set或其他)。 我需要使用它(在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);
}