我有一个案例,我想查询在数组字段“forms”下有许多项目的文档集合。要解决的问题是希望返回仅具有“表单”中包含的文档全部且具有特定状态“已关闭”的文档。
以下是该集合中两个不同文档的示例:
{
"_id" : "Tvq444454j",
"name" : "Jim",
"forms" : [
{
"name" : "Jorney",
"status" : "closed"
},
{
"name" : "Women",
"status" : "void"
},
{
"name" : "Child",
"status" : "closed"
},
{
"name" : "Farm",
"status" : "closed"
}
]
},
{
"_id" : "Tvq579754r",
"name" : "Tom",
"forms" : [
{
"name" : "PreOp",
"status" : "closed"
},
{
"name" : "Alert",
"status" : "closed"
},
{
"name" : "City",
"status" : "closed"
},
{
"name" : "Country",
"status" : "closed"
}
]
}
预期结果:
{
"_id" : "Tvq579754r",
"name" : "Tom",
"forms" : [
{
"name" : "PreOp",
"status" : "closed"
},
{
"name" : "Alert",
"status" : "closed"
},
{
"name" : "City",
"status" : "closed"
},
{
"name" : "Country",
"status" : "closed"
}
]
}
由于在此条件下没有标准查询运算符来匹配数组元素的所有,因此使用聚合找到了解决方案。这将返回集合中的文档的_id,其中所有“表单”元素都设置为“已关闭”状态。
db.forms.aggregate([
{$unwind: "$forms" },
{$group: { _id: "$_id", status: {$addToSet: "$forms.status" }}},
{$unwind: "$status"},
{$sort: { _id: 1, status: -1 }},
{$group: {_id: "$_id", status: {$first: "$status"}}},
{$match:{ status: "closed" }}
])
因为我希望在结果中返回许多文档,我想避免发出另一个查找或一系列查找只是为了获取与返回的_id匹配的文档。
考虑到这一点,是否有任何方法可以使我们能够以与集合中完全相同的形式从聚合中获取原始文档,同时仍然进行此类过滤?
答案 0 :(得分:6)
属于愚蠢聚合技巧的类别是一种经常被忽视的技巧。
查询对文档_id进行全部分组,作为此文档的唯一标识符。所以要考虑的主要观点是整个文档实际上已经是一个唯一的标识符。因此,不要只隐藏在_id键中,而是使用整个文档。
{$project: {
_id: { _id: "$_id", name: "$name", forms: "$forms" }, forms: "$forms"}
},
如果这样做,_id卷起的任何内容都会保留原始格式的文档。在所有其他聚合阶段结束时,发出最终$project以恢复真正的原始文档格式:
{$project: { _id: "$_id._id", name: "$_id.name", forms: "$_id.forms"}}
然后您将获得所需的过滤结果。当与高级过滤一起使用时,此技术非常方便,例如在此查询的情况下,因为它无需对所有结果发出额外的查找。
此外,如果您知道自己只是在寻找一组符合特定条件的结果,请使用$match运算符作为第一个聚合管道的阶段。这不仅有助于减少工作集大小,而且还是唯一阶段,您可以在此阶段使用索引,并且可以显着提高查询性能
整个过程:
db.forms.aggregate([
{$match: { "forms.status": "closed" } },
{$project: {
_id: { _id: "$_id", name: "$name", forms: "$forms" }, forms: "$forms"}
},
{$unwind: "$forms"},
{$group: { _id: "$_id", status: {$addToSet: "$forms.status"}}},
{$unwind: "$status"},
{$sort: { _id: 1, status: -1} },
{$group: { _id: "$_id", status: {$first: "$status"} }},
{$match: { status: "closed"}},
{$project: { _id: "$_id._id", name: "$_id.name", forms: "$_id.forms"}}
])