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

Edg*_*nez 3 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}
        ]
 }]]
Run Code Online (Sandbox Code Playgroud)

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

Bat*_*eam 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"}}}
])
Run Code Online (Sandbox Code Playgroud)

O/P:

{
        "_id" : ObjectId("54ad79dae024832588b287f4"),
        "name" : "Another",
        "count" : 2
}
{
        "_id" : ObjectId("54ad79dae024832588b287f3"),
        "name" : "Something",
        "count" : 2
}
Run Code Online (Sandbox Code Playgroud)

  • 很好,你的解决方案的输出比我的更可读 (2认同)
  • @EdgarMartinez直到你意识到这是完全错误的,对于人们学习而言,信息量不大.这是一个糟糕的接受. (2认同)

Nei*_*unn 5

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

$match流水线阶段还进行了优化。在查询开始时是为了过滤掉可能不匹配的文档,在$unwind阶段之后,都是为了删除那些肯定不符合条件的元素(现在是document):

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 }
    }}
])
Run Code Online (Sandbox Code Playgroud)

自然地,如果您抽出所有“匹配项”,那么整个集合中每个“类型”都会获得“计数”。

在现代版本中,您可以使用$redactMongoDB 2.6及更高版本的运算符对此进行一些更改。如果由于此管道阶段的递归性质而有所不为:

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 }
    }}
)
Run Code Online (Sandbox Code Playgroud)

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

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