如果文档不存在,我需要插入文档。我知道“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);
});
但我仍然想知道这是否是最佳方式。
答案 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);
}