既然有返回的文件,只是所选择的“子文档”的细节之间存在明显的差异,你有嵌套的数组,你最好的办法是使用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