既然有返回的文件,只是所選擇的「子文檔」的細節之間存在明顯的差異,你有嵌套的數組,你最好的辦法是使用aggregate()
方法代替:
所以如果考慮下面的文檔作爲樣本:
{
"_id" : ObjectId("5380709ab5caa8c27c8a1392"),
"name" : "Foobar",
"subs" : [
{
"imageName" : "name",
"foreignNames" : [
{
"tagname" : "value"
},
{
"tagname" : "notvalue"
}
]
}
]
}
然後總的說法是:
db.collection.aggregate([
// Actually match the documents containing the matched value
{ "$match": {
"subs.foreignNames.tagname": "value"
}},
// Unwind both of your arrays
{ "$unwind": "$subs" },
{ "$unwind": "$subs.foreignNames" },
// Now filter only the matching array element
{ "$match": {
"subs.foreignNames.tagname": "value"
}},
// Group back one level of data
{ "$group": {
"_id": {
"_id": "$_id",
"name": "$name",
"imageName": "$subs.imageName"
},
"foreignNames": { "$push": "$subs.foreignNames" }
}},
// Group back to the original level
{ "$group": {
"_id": "$_id._id",
"name": { "$first": "$_id.name" },
"subs": {
"$push": {
"imageName": "$_id.imageName",
"foreignNames": "$foreignNames"
}
}
}}
])
而結果將是:
{
"_id" : ObjectId("5380709ab5caa8c27c8a1392"),
"name" : "Foobar",
"subs" : [
{
"imageName" : "name",
"foreignNames" : [
{
"tagname" : "value"
}
]
}
]
}
的優勢在那裏的是,如果你可能會擁有一個以上的比賽,甚至在一個以上的水平,所以說,在「潛艇的其他項目「,那麼這將實際上將它們放在一起,同時過濾不匹配的結果。
如果你實際上並不需要這一點,只需要那些特定的「文件」或文檔作爲一個整體只在特定的領域,那麼你就可以縮短,在該$group`階段,你只需要$project結果:
db.newdoc.aggregate([
{ "$match": {
"subs.foreignNames.tagname": "value"
}},
{ "$unwind": "$subs" },
{ "$unwind": "$subs.foreignNames" },
{ "$match": {
"subs.foreignNames.tagname": "value"
}},
{ "$project": {
"_id": 0,
"matched": "$subs.foreignNames"
}}
])
作爲一個例子,但將返回:
{ "matched" : { "tagname" : "value" } }
所以這是相當多的處理事情的方式。
注意:你問就在,因爲很多人,爲什麼$match
語句管線期間進行兩次,它是那種在評論中解釋,但這裏的點。
即使在10000個文檔的集合中只有1個文檔實際上具有匹配條件的內部數組文檔,在執行此數組展開之前執行此操作也是有意義的$match
。
這很簡單,因爲即使您打算將此後面的結果過濾爲1個結果,您不希望執行的操作是$unwind
所有10000個文檔,其數組可能包含100,000個或更多條目,然後搜索找到那個1.你想把它縮小到最小的集合並丟棄任何永遠不會包含你想要的子文檔的文檔。
此外,如已經提到,一個聚合管道的初始階段使用$match
是只有機會,你爲了提高查詢性能選擇索引。一旦開始解構/重構文檔,索引不再可用。
所以指數第一,即:
db.collection.ensureIndex({ "subs.foreignNames.tagname": 1 })
你可以添加一個(短路)演示文檔顯示對象的架構。 'db.collection.findOne()' – Simulant