您需要使用聚合框架。聚合最终看起来像这样:
db.stack.aggregate([
{ $match: { "samples.key" : "test-key" } },
{ $unwind : "$samples" },
{ $match : { "samples.key" : "test-key" } },
{ $project : { "new_key" : "$samples.key", "new_value" : "$samples.value" } },
{ $group : { `_id` : "$new_key", answer : { $avg : "$new_value" } } }
])
考虑聚合框架的最佳方式就像一条流水线。查询本身是一个 JSON 文档数组,其中每个子文档代表程序集中的不同步骤。
第 1 步:$ 匹配
第一步是一个基本的过滤器,就像 SQL 中的 WHERE 子句。我们首先放置这一步,以过滤掉所有不包含包含 的数组元素的文档test-key
。将它放在管道的开头允许聚合使用索引。
第 2 步:$ 放松
第二步,$unwind
,用于分离“samples”数组中的每个元素,以便我们可以对所有元素执行操作。如果您只使用该步骤运行查询,您就会明白我的意思。长话短说 :
{ name : "bob",
children : [ {"name" : mary}, { "name" : "sue" } ]
}
变成两个文件:
{ name : "bob", children : [ { "name" : mary } ] }
{ name : "bob", children : [ { "name" : sue } ] }
第 3 步:$ 匹配
第三步,$match
是第一$match
阶段的完全复制品,但目的不同。由于它遵循$unwind
,此阶段过滤掉与过滤条件不匹配的先前数组元素,现在是文档。在这种情况下,我们只保留文件samples.key = "test-key"
第 4 步:$project(可选)
第四步,$project
重构文档。在这种情况下,我将项目从数组中拉出,以便可以直接引用它们。使用上面的例子..
{ name : "bob", children : [ { "name" : mary } ] }
变成
{ new_name : "bob", new_child_name : mary }
请注意,此步骤完全是可选的;$project
经过一些小的改动,即使没有这个,后期阶段也可以完成。在大多数情况下$project
完全是装饰性的;聚合在底层有许多优化,$project
因此不需要手动包含或排除 a 中的字段。
第 5 步:$ 组
最后,$group
是魔法发生的地方。_id
您将在 SQL 世界中“分组依据”的值。第二个字段表示对我在$project
步骤中定义的值进行平均。您可以轻松地替换$sum
为执行求和,但计数操作通常通过以下方式完成:my_count : { $sum : 1 }
.
这里要注意的最重要的事情是,正在完成的大部分工作是将数据格式化到执行操作很简单的程度。
最后说明
最后,我想指出,这不适用于提供的示例数据,因为samples.value
它被定义为文本,不能用于算术运算。如果您有兴趣,这里描述了更改字段的类型:MongoDB How to change the type of a field