2014-10-03 23 views
1

拉引用來自客戶端的集合考慮這個文件:

client: { 
    services: [ 
    { 
     _id: 111, 
     someField: 'someVal' 
    }, 
    { 
     _id: 222, 
     someField: 'someVal' 
    } 
    ... // More services 
    ] 

    staff: [ 
    { 
     _id: 'aaa', 
     someField: 'someVal', 
     servicesProvided: [111, 222, 333, ...] 
    }, 
    { 
     _id: 'bbb', 
     someField: 'someVal', 
     servicesProvided: [111, 555, 666, ...] 
    }, 
    { 
     _id: 'ccc', 
     someField: 'someVal', 
     servicesProvided: [111, 888, 999, ...] 
    } 
    ... // More staff 
    ] 
} 

客戶可以有許多工作人員。每位員工都提及他或她提供的服務。如果一項服務被刪除,所有員工也需要刪除對該服務的引用。

我想從services刪除(拉)的對象(服務),並在相同的查詢刪除servicesProvided可能的參考在所有staff objects`

例如,如果我與刪除的服務_id 111,我還想刪除提供此服務的員工中對此服務的所有引用。

我該如何寫這個查詢。

+0

現在評論你的其他鏈接,但我想我明白你的意圖。通過顯示共享相同「服務」ID值的「多個」「員工」條目,可能會更好地顯示出來。但那是你在這裏問的對嗎?如何從「數組」的「多個」成員下的子數組中刪除該條目。對? BTW。如果我沒有記錯的話,你在node.js和mongoskin上。 – 2014-10-03 12:45:54

+0

是的!這就是我要求的=)。 Nodejs和mongoskin正確! – 2014-10-03 12:51:04

+0

很酷。所以是的,這是「可能的」,但不是在單個查詢中。把時間放在一起,我想以前的問題的一部分:) – 2014-10-03 13:12:06

回答

1

所以這是事情變得有點討厭的地方。您如何更新「多個」數組項以匹配單個文檔中的條件?

這裏背景知識來自positional $操作者documentation

嵌套數組 的位置$操作者可以不被用於橫穿多個陣列的查詢,諸如遍歷嵌套陣列查詢在其他陣列中,因爲替換$佔位符是一個單一的值

這告訴「故事的一部分」,但這裏的重點是特定於這個問題n是「多一個」。

因此,即使「嵌套」部分由於需要做什麼而不是明確地true,重要的因素是「多於一個」。爲了演示,讓我們來看看這個:

{ 
    services: [ 
    { 
     _id: 111, 
     someField: 'someVal' 
    }, 
    { 
     _id: 222, 
     someField: 'someVal' 
    } 
    ], 

    staff: [ 
    { 
     _id: 'aaa', 
     someField: 'someVal', 
     servicesProvided: [111, 222, 333, ...] 
    }, 
    { 
     _id: 'bbb', 
     someField: 'someVal', 
     servicesProvided: [111, 555, 666, ...] 
    }, 
    { 
     _id: 'ccc', 
     someField: 'someVal', 
     servicesProvided: [111, 888, 999, ...] 
    } 
    ] 
} 

現在你問刪除111值。這是總是您的示例中提供的「第一個」值。所以,在這裏我們可以承擔這是這種情況,那麼在更新「似乎是:簡單:

db.collection.update(
    { 
     "_id": ObjectId("542ea4991cf4ad425615b84f"), 
    }, 
    { 
     "$pull": { 
      "services": { "_id": 111 }, 
      "staff.servicesProvided": 111 
     } 
    } 
) 

但不會做你所期望的元素不會從拉。 。所有「員工」數組正如你所預料的元素其實,他們沒有將工作的唯一事情是這樣的:

db.collection.update(
    { 
     "_id": ObjectId("542ea4991cf4ad425615b84f"), 
     "staff.servicesProvided": 111 
    }, 
    { 
     "$pull": { 
      "services": { "_id": 111 }, 
      "staff.$.servicesProvided": 111 
     } 
    } 
) 

但細想一下,只有「第一」的數組元素竟是更新!所以當你看到上面的陳述時,這基本上就是它所說的將會發生的事情

然後再說一遍,假設我們只是在一個MongoDB 2.6版本或更高版本的服務器上的現代MongoDB shell中測試它。那麼這就是我們得到的迴應:

WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) 

所以等一下。我們剛剛被告知有多少文件被上一條語句「修改」。所以,即使我們一次只能更改數組中的一個元素,這裏也有一些重要的反饋。

關於從「批量操作API」操作獲得的新「WriteResult」對象真的很棒,事實上這是在shell中執行的,實際上您會被告知是否有前一個語句「修改」某些內容或不。比「傳統」寫入響應更好,這爲我們提供了一個基礎,可以爲循環考慮做出一些重要決定。例如「我們最後的操作實際上是'修改'一個文檔,然後我們應該繼續嗎?」

所以這是一個重要的「流量控制」點,即使一般的MongoDB API本身不能一次全部「更新所有元素」。現在有一個可測試的例子來決定在哪裏「繼續」循環或不循環。這就是我最終通過「結合」你已經學到的東西的意思。所以最終我們可以來這樣的上市:

var bulk = db.collection.initializeOrderedBulkOp(); 
var modified = 1; 

async.whilst(
    function() { return modified }, 
    function(callback) { 
     bulk.find(
      { 
       "_id": ObjectId("542ea4991cf4ad425615b84f"), 
       "staff.servicesProvided": 111 
      } 
     ).updateOne(
      { 
       "$pull": { 
        "services": { "_id": 111 }, 
        "staff.$.servicesProvided": 111 
       } 
      } 
     ); 

     bulk.execute(function(err,result) { 
      modified = result.nModfified(); 
      callback(err); 
     }); 
    }, 
    function(err) { 
     // did I throw something! Suppose I should so something about it! 
    } 
); 

或基本上這樣可愛的東西。因此,您要求從「批量操作」.execute()中獲得的「結果」對象告訴您是否修改了某些內容。那裏仍然是,那麼你在這裏再次「重複」循環,並執行相同的更新並再次詢問結果。

最終,更新操作會告訴你「沒有」被修改過。這是您退出循環並繼續正常操作的時候。

現在的替代來處理這種方式很可能是在整個對象讀取,然後讓所有您需要的修改:

db.collection.findOne(
    { 
     "_id": ObjectId("542ea4991cf4ad425615b84f"), 
     "staff.servicesProvided": 111 
    }, 
    function(err,doc) { 
     doc.services = doc.services.filter(function(item) { 
      return item._id != 111; 
     }); 

     doc.staff = doc.staff.filter(function(item) { 
      item.serviceProvided = item.servicesProvided.filter(function(sub) { 
       return sub != 111; 
      }); 
      return item; 
     }); 
     db.collection.save(doc); 
    } 
); 

有點大材小用。不是完全原子的,但足夠接近度量。

所以你不能在單一的寫操作中真正做到這一點,至少不需要處理「閱讀」文檔,然後在修改內容後「寫」整個事情。但是你可以採取「迭代」的方法,並且有一些工具可以讓你控制它。

另一種可能的方式來處理,這是改變你的模型是這樣的方式:

{ 
    "services": [ 
    { 
     "_id": 111, 
     "someField": "someVal" 
    }, 
    { 
     "_id": 222, 
     "someField": "someVal" 
    } 
    ], 

    "provided": [ 
     { "_id": "aaa", "service": 111 }, 
     { "_id": "aaa", "service": 222 }, 
     { "_id": "aaa", "service": 111 } 
    ] 
} 

等。所以,那麼查詢變成這樣的事:

db.collection.update(
    { "_id": ObjectId("542ea4991cf4ad425615b84f") }, 
    { 
     "$pull": { 
      "services": { "_id": 111 }, 
      "provided": { "_id": 111 } 
     } 
    } 
); 

而且,真正的將是一個奇異的更新操作,由於每個元素都包含在單一陣列中一氣呵成刪除一切。

所以有辦法做到這一點,但你如何建模真的取決於你的應用程序數據訪問模式。選擇最適合您的解決方案。這就是您首先選擇MongoDB的原因。

+0

很多可能的解決方案,很好的答案。週末愉快! – 2014-10-03 15:58:33

相關問題