1

对于给定的记录 id,如果我在 MongoDB 中有以下内容,我如何获得子文档字段的平均值:

/* 0 */
{
    "item" : "1",
    "samples" : [ 
        {
            "key" : "test-key",
            "value" : "1"
        }, 
        {
            "key" : "test-key2",
            "value" : "2"
        }
    ]
}

/* 1 */
{
    "item" : "1",
    "samples" : [ 
        {
            "key" : "test-key",
            "value" : "3"
        }, 
        {
            "key" : "test-key2",
            "value" : "4"
        }
    ]
}

我想获得给定项目ID(在本例中为1)的key =“test-key”值的平均值。所以平均值应该是 $avg (1 + 3) = 2

谢谢

4

1 回答 1

12

您需要使用聚合框架。聚合最终看起来像这样:

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

于 2013-11-08T00:12:35.160 回答