所以這是事情變得有點討厭的地方。您如何更新「多個」數組項以匹配單個文檔中的條件?
這裏背景知識來自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的原因。
現在評論你的其他鏈接,但我想我明白你的意圖。通過顯示共享相同「服務」ID值的「多個」「員工」條目,可能會更好地顯示出來。但那是你在這裏問的對嗎?如何從「數組」的「多個」成員下的子數組中刪除該條目。對? BTW。如果我沒有記錯的話,你在node.js和mongoskin上。 – 2014-10-03 12:45:54
是的!這就是我要求的=)。 Nodejs和mongoskin正確! – 2014-10-03 12:51:04
很酷。所以是的,這是「可能的」,但不是在單個查詢中。把時間放在一起,我想以前的問題的一部分:) – 2014-10-03 13:12:06