聚合计数匹配嵌套数组的条件元素

时间:2017-06-12 13:09:18

标签: mongodb mongodb-query aggregation-framework

我是MongoDB聚合的新手。

我的数据库具有以下结构:

{
  "_id": "AAABBBCCCDDDD",
  "report":{
    "google": [{
        "report_id": "XXX",
        "detail": [{
            "available": true,
            "status_code": 200,
            "return_code": "ok",
        }, {
            "available": true,
            "status_code": 300,
            "return_code": "ok",
        }, {
            "available": true,
            "status_code": 400,
            "return_code": "ok",
        }]
    }, {
        "report_id": "YYY",
        "detail": [{
            "available": false,
            "status_code": 200,
            "return_code": "ok",
        }, {
            "available": true,
            "status_code": 200,
            "return_code": "ng",
        }, {
            "available": true,
            "status_code": 200,
            "return_code": "ok",
        }]
    }]
  }
}

我想要压扁这样的文件:

{
  "AAABBBCCCDDDD": [{
    "report_id": "XXX",
    "detail": [{
      "available": true,
      "status_code": 200,
      "return_code": "ok",
    }, {
      "available": true,
      "status_code": 300,
      "return_code": "ok",
    }, {
      "available": true,
      "status_code": 400,
      "return_code": "ok",
    }]
  }, {
    "report_id": "YYY",
    "detail": [{
      "available": false,
      "status_code": 200,
      "return_code": "ok",
    }, {
      "available": true,
      "status_code": 200,
      "return_code": "ng",
    }, {
      "available": true,
      "status_code": 200,
      "return_code": "ok",
    }]
  }]
}

然后计算匹配的available is truereturn_code is "ok"的数量,返回这样的结构:

{
  "AAABBBCCCDDDD": [{
    "report_id": "XXX",
    "available_count": 3,
  },
  {
    "report_id": "YYY",
    "available_count": 1,
  }]
}

有没有这样做?

1 个答案:

答案 0 :(得分:1)

使用现代MongoDB 3.4,您可以使用$replaceRoot$arrayToObject执行此操作:

db.collection.aggregate([
  { "$replaceRoot": {
    "newRoot": {
      "$arrayToObject": {
        "$concatArrays": [
          [
            { 
              "k": "$_id", 
              "v": {
                "$map": {
                  "input": "$report.google",
                  "as": "el",
                  "in": {
                    "report_id": "$$el.report_id",
                    "available_count": { 
                      "$size": {
                        "$filter": {
                          "input": "$$el.detail",
                          "as": "d",
                          "cond": {
                           "$and": [
                             "$$d.available",
                             { "$eq": [ "$$d.return_code", "ok" ] }
                            ]
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          ]
        ]
      }
    }
  }}
])

但是你可以在任何带有少量客户端代码的版本中执行此操作:

db.collection.find().forEach(doc => {
  doc[doc._id] = doc.report.google.map(el => {
    el.available_count = el.detail.filter(d => d.available && d.return_code === "ok").length;
    delete el.detail;
    return el;
  });
  delete doc._id;
  delete doc.report;
  printjson(doc);
})

两者产生相同的东西:

{
        "AAABBBCCCDDDD" : [
                {
                        "report_id" : "XXX",
                        "available_count" : 3
                },
                {
                        "report_id" : "YYY",
                        "available_count" : 1
                }
        ]
}

所以你根本不需要聚合,因为它只是真正重塑文档。

问题的原始数据

{
  "_id": "AAABBBCCCDDDD",
  "report":{
    "google": [{
        "report_id": "XXX",
        "detail": [{
            "available": true,
            "status_code": 200,
            "return_code": "ok",
        }, {
            "available": true,
            "status_code": 300,
            "return_code": "ok",
        }, {
            "available": true,
            "status_code": 400,
            "return_code": "ok",
        }]
    }, {
        "report_id": "YYY",
        "detail": [{
            "available": false,
            "status_code": 200,
            "return_code": "ok",
        }, {
            "available": true,
            "status_code": 200,
            "return_code": "ng",
        }, {
            "available": true,
            "status_code": 200,
            "return_code": "ok",
        }]
    }]
  }
}