2012-01-13 29 views
11

如果文檔不存在,則需要插入文檔。我知道「upsert」選項可以做到這一點,但我有一些特殊的需求。使用自定義_id值時插入mongodb

首先,我只需要創建帶有_id字段的文檔,但前提是它不存在。我的_id字段是由我生成的數字(不是ObjectId)。如果我使用「更新插入」選項,然後我得到「MOD上_id不允許」

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

我知道,我們不能在$組使用_id。

所以,我的問題是:如果有任何方式在MongoDB中原子地創建「如果不存在」?

編輯: 提議@Barrie這工作(使用和的NodeJS貓鼬):

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

嘗試使用'save'操作嗎?即db.collection.save({「_ ID」:YOUR_ID}) – 2012-01-13 02:13:18

+0

保存失敗,重複的鍵錯誤,如果已經存在 – aartiles 2012-01-13 08:25:27

回答

5

你可以使用insert()。如果指定_id的文檔已經存在,那麼insert()將會失敗,不會修改任何內容 - 所以如果它不存在,就創建它是默認情況下在使用insert()創建_id。

+0

有道理,我想它。 – aartiles 2012-01-13 08:25:59

+2

這仍然不處理文檔在插入後和後續更新之前被刪除的競爭條件。 – Lucian 2013-05-14 11:57:42

22

我有同樣的問題,但找到了一個更好的解決方案,以滿足我的需求。如果只是從更新對象中刪除_id屬性,則可以使用相同的查詢樣式。所以,如果一開始你得到一個錯誤與此:

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

改用此:

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

這是更好,因爲它同時適用於插入和更新。

+1

這是否與一個不是ObjectID的_id一起工作,如問題中所述? – 2013-03-08 18:49:40

+1

是的。它使用_id作爲ObjectID或自定義值導致錯誤「Mod on _id not allowed」。所以你必須總是從'$ set'對象中刪除_id。 – 2013-03-08 19:29:55

+0

謝謝,從$ set對象中移除_id適用於我。 – ATilara 2013-06-10 23:35:04

3

UPDATE:帶_id的Upsert可以在沒有$setOnInsert的情況下完成,正如上面的@Barrie的說明。

訣竅是使用$setOnInsert:{_id:1} with upsert,這樣_id只會寫入,如果它是插入,並且永遠不會更新。

Only, there was a bug preventing this from working until v2.6 - 我剛剛在2.4上試過了,它不工作。

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

+1

setOnInsert爲我工作。而巴里的回答,直接插入不會做更新作品; Nate Barr的回答,由_id更新限制了查找標準。我想要做的是:db.coll.update({a:1,b:2},{$ set:{c:3,d:4},$ setOnInsert:{_id:'xxxx'}},{ upsert:true}) – vdonkey 2017-09-22 15:17:07

0

請注意,當您UPSERT一個簡單的鍵=>值對象(未$設置或其他)$ 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); 
} 
相關問題