与nodejs驱动程序的聚合 - 按字段是否为空进行分组

时间:2017-06-15 02:10:36

标签: node.js mongodb aggregation-framework

文档简单如下:

[
  {'id': '1', 'type': 'a', 'startedAt': '2017-06-11'},
  {'id': '2', 'type': 'b', 'startedAt': ''},
  {'id': '3', 'type': 'b', 'startedAt': '2017-06-11'}
]

预期的汇总结果:

[
  {'type': 'a', 'started': true, 'count': 1},
  {'type': 'b', 'started': true, 'count': 1},
  {'type': 'b', 'started': false, 'count': 1}
]

如何使用mongodb nodejs驱动程序获得上述结果?

我尝试过如下,但它没有工作('已开始'始终为空):

db.collection('docs').group(
      {'type': '$type', 'started': {
        $cond: [{$eq: ['$startedAt': '']}, false, true ]
      }}, 
      {}, 
      {'total': 0},
      'function(curr, result) {result.total++}'
    )

1 个答案:

答案 0 :(得分:2)

你在这里使用.aggregate()而不是.group(),这完全是另一个功能:

db.collection('docs').aggregate([
  { "$group": {
    "_id": {
      "type": "$type",
      "started": {
        "$gt": [ "$startedAt", "" ]
      }
    },
    "count": { "$sum": 1 }
  }}
],function(err, results) {
   console.log(results);
})

$gt运算符在满足条件时返回true。在这种情况下,字符串中的任何内容都大于"大于"一个空字符串。

如果该字段实际上是"根本不存在"然后我们可以适应$ifNull。如果属性实际上不存在,则给出默认值,否则计算为null

db.collection('docs').aggregate([
  { "$group": {
    "_id": {
      "type": "$type",
      "started": {
        "$gt": [ { "$ifNull": [ "$startedAt", ""] }, "" ]
      }
    },
    "count": { "$sum": 1 }
  }}
],function(err, results) {
   console.log(results);
})

这会产生:

{ "_id" : { "type" : "b", "started" : true }, "count" : 1 }
{ "_id" : { "type" : "b", "started" : false }, "count" : 1 }
{ "_id" : { "type" : "a", "started" : true }, "count" : 1 }

您可以选择$project之后将结果中的字段更改为_id,但实际上您不应该这样做,因为这意味着您可以轻松访问无论如何都要有价值。

结果只是.map()

console.log(
  results.map(function(r) { 
    return { type: r._id.type, started: r._id.started, count: r.count }
 })
);

$project

db.collection('docs').aggregate([
  { "$group": {
    "_id": {
      "type": "$type",
      "started": {
        "$gt": [ { "$ifNull": [ "$startedAt", ""] }, "" ]
      }
    },
    "tcount": { "$sum": 1 }
  }},
  { "$project": {
    "_id": 0,
    "type": "$_id.type",
    "started": "$_id.started",
    "count": "$tcount"
  }}
],function(err, results) {
   console.log(results);
})

产生您想要的格式

{ "type" : "b", "started" : true, "count" : 1 }
{ "type" : "b", "started" : false, "count" : 1 }
{ "type" : "a", "started" : true, "count" : 1 }

供参考,.group()的正确用法是:

  db.collection('docs').group(
    function(doc) {
      return {
        "type": doc.type,
        "started": (
          (doc.hasOwnProperty('startedAt') ? doc.startedAt : "") > ""
        )
      }
    },
    [],
    { "count": 0 },
    function(curr,result) {
      result.count += 1
    },
    function(err,results) {
      console.log(results);
    }
  );

返回:

[
    { "type" : "a", "started" : true, "count" : 1  },
    { "type" : "b", "started" : false, "count" : 1 },
    { "type" : "b", "started" : true,  "count" : 1 }
]

但你真的不应该使用,因为.group()依赖于JavaScript评估,其运行速度比.aggregate()

的速度慢得多