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

Count the number of times object an property exists in mongo array of objects

我有这样的文档结构

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

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

虽然可以应用$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
}

试试这个:

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
    }
}

MongoDB的聚合框架就是这里的答案。关键操作是 $unwind for processing the array contents into "normalized" documents and the $group 用于获取计数的流水线阶段。

$match pipeline stages. Both at the beginning of the query in order to filter out documents that cannot possibly match and after the $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 }
    }}
])

当然,如果您取出所有 "matching",那么您会在整个集合中得到每个 "type" 的 "count"。

在现代版本中,从 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 }
    }}
)

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

Always 在执行任何其他操作之前过滤您要在 "first" 上处理的匹配值。这消除了在聚合管道中处理的不必要的工作。如果只有 10,000 个可能匹配并且这些文档中只有 2,000 个元素也匹配,则处理 100,000 个文档没有用。