计算mongo对象数组中对象属性的次数

时间:2015-01-07 18:18:27

标签: mongodb mongodb-query aggregation-framework

我有这样的文档结构

[{
 name: "Something",
 codes: [
         {type: 11}, 
         {type: 11}, 
         {type: 15}
        ]
 },
 {
 name: "Another",
 codes: [
         {type: 11}, 
         {type: 12}, 
         {type: 15},
         {type: 11}
        ]
 }]]

我需要计算集合中每个条目出现type = 11次的次数。 我很难过。

3 个答案:

答案 0 :(得分:5)

虽然可以应用仅$match过滤掉包含特定类型代码的文档,但不应将其应用于此特定问题语句。因为它会从输出中过滤掉没有特定类型代码的文档。

你需要:

  • Unwind每个文档都基于代码字段。
  • 如果代码字段属于所需类型,则project字段wantedType的值为1 或者值为0
  • Group_id字段后获取wantedType字段的总和,其中 为您提供特定文档中所需类型代码的数量。
  • 因此,即使文档没有所需的类型代码,它也会在输出中显示 0为计数。

代码:

var typeCountToCalculate = 11;

db.collection.aggregate([
{$unwind:"$codes"},
{$project:{"name":1,
          "wantedType":{$cond:[{$eq:["$codes.type",typeCountToCalculate ]},1,0]}}},
{$group:{"_id":"$_id",
         "name":{$first:"$name"},"count":{$sum:"$wantedType"}}}
])

O / P:

{
        "_id" : ObjectId("54ad79dae024832588b287f4"),
        "name" : "Another",
        "count" : 2
}
{
        "_id" : ObjectId("54ad79dae024832588b287f3"),
        "name" : "Something",
        "count" : 2
}

答案 1 :(得分:4)

MongoDB的聚合框架就是答案。关键操作是$unwind,用于将数组内容处理为“规范化”文档,$group管道阶段用于获取计数。

$match管道阶段也在进行优化。在查询开始时,为了过滤掉在$unwind阶段之后不可能匹配的文档,为了删除那些肯定与条件不匹配的元素(现在是文档):

db.collection.aggregate([
    // Match to filter documents
    { "$match": { "codes.type": 11 }},

    // Unwind to 'de-normalize'
    { "$unwind": "$codes" },

    // Match to filter again, but remove the array elements
    { "$match": { "codes.type": 11 }},

    // Count the occurrences of the the matches
    { "$group": {
        "_id": "$codes.type",
        "count": { "$sum": 1 }
    }}
])

当然,如果你取出所有“匹配”,那么你会在整个系列中获得每个“类型”的“计数”。

在现代版本中,您可以使用MongoDB 2.6及更高版本的$redact运算符稍微改变一下。如果由于这个管道阶段的递归性质而有点做作:

db.collection.aggregate([
    // Match to filter documents
    { "$match": { "codes.type": 11 }},

    // Filter out non matches
    { "$redact": {
        "$cond": {
            "if": { "$eq": [ 
                { "$ifNull": [ "$type", 11 ] },
                11
            ]},
            "then": "$$DESCEND",
            "else": "$$PRUNE"
        }
    }}

    // Unwind to 'de-normalize'
    { "$unwind": "$codes" },

    // Count the occurrences of the the matches
    { "$group": {
        "_id": "$codes.type",
        "count": { "$sum": 1 }
    }}
)

这是一种不同的过滤方式,如果您的服务器支持它,则完全有效。如果在其他示例中使用嵌套级别,请小心。

在执行任何其他操作之前,

始终过滤您想要在“first”上工作的匹配值。这将从聚合管道中处理不必要的工作。如果只有10,000个可能匹配,并且这些文档中只有2,000个元素也匹配,则不得使用处理100,000个文档。

答案 2 :(得分:3)

试试这个:

db.test.aggregate([
{$unwind : "$codes"},
{$match : { 'codes.type' : 11}} ,
{$group : { 
    _id : { 'name': '$name', 'type' : '$codes.type'}
    ,count: { $sum: 1 }
}},
]).result

输出将是:

{
    "0" : {
        "_id" : {
            "name" : "Another",
            "type" : 11
        },
        "count" : 2
    },
    "1" : {
        "_id" : {
            "name" : "Something",
            "type" : 11
        },
        "count" : 2
    }
}