获取 “记录水平” 的计数每个 “名”

问题描述:

,比如我有以下集合:获取 “记录水平” 的计数每个 “名”

db.names.find({})

{ "_id" : ObjectId("5768d9b4bc6f464899594570"), "name" : "t1", "loglevel" : "ERROR" } 

{ "_id" : ObjectId("5768d9d5bc6f464899594571"), "name" : "t1", "loglevel" : "ERROR" } 

{ "_id" : ObjectId("5768d9dcbc6f464899594572"), "name" : "t1", "loglevel" : "WARNING" } 

{ "_id" : ObjectId("5768d9dfbc6f464899594573"), "name" : "t1", "loglevel" : "WARNING" } 

{ "_id" : ObjectId("5768d9e0bc6f464899594574"), "name" : "t1", "loglevel" : "WARNING" } 

{ "_id" : ObjectId("5768d9e7bc6f464899594575"), "name" : "t1", "loglevel" : "INFO" } 

{ "_id" : ObjectId("5768d9f3bc6f464899594576"), "name" : "t2", "loglevel" : "INFO" } 

{ "_id" : ObjectId("5768d9f9bc6f464899594577"), "name" : "t2", "loglevel" : "ERROR" } 

{ "_id" : ObjectId("5768da19bc6f464899594578"), "name" : "t2", "loglevel" : "ERROR" } 

{ "_id" : ObjectId("5768da1abc6f464899594579"), "name" : "t2", "loglevel" : "ERROR" } 

{ "_id" : ObjectId("5768da1bbc6f46489959457a"), "name" : "t2", "loglevel" : "ERROR" } 

{ "_id" : ObjectId("5768e247bc6f46489959457b"), "name" : "t3", "loglevel" : "INFO" } 

我都试过,但我认为它应该是更好的解决方案(也可以省略else

db.names.aggregate(
    {$group: { 
     _id: "$name", 
     error: {$sum: {$cond: {if: {$eq: ['$loglevel', 'ERROR']}, then: {$sum: 1}, else: {$sum: 0}}}}, 
     warning: {$sum: {$cond: {if: {$eq: ['$loglevel', 'WARNING']}, then: {$sum: 1}, else: {$sum: 0}}}}, 
     info: {$sum: {$cond: {if: {$eq: ['$loglevel', 'INFO']}, then: {$sum: 1}, else: {$sum: 0}}}} 
    }} 
) 

if/else条件块取下{ "$sum": 1 }{ "$sum": 0 }表达式(分别为每个条件块)与值1和0代替它们。

最后的管道应该是这样的,使用其他$cond语法,省略了if/else块:

db.names.aggregate([ 
    { 
     "$group": { 
      "_id": "$name", 
      "error": { 
       "$sum": { 
        "$cond": [ { "$eq": [ "$loglevel", "ERROR" ] }, 1, 0] 
       } 
      }, 
      "warning":{ 
       "$sum": { 
        "$cond": [ { "$eq": [ "$loglevel", "WARNING" ] }, 1, 0 ] 
       } 
      }, 
      "info": { 
       "$sum": { 
        "$cond": [ { "$eq": [ "$loglevel", "INFO" ] }, 1, 0 ] 
       } 
      } 
     } 
    } 
]) 

或动态创建的管道,由于可能的状态数组:

var statuses = ["ERROR", "WARNING", "INFO"], 
    groupOperator = { "$group": { "_id": "$name" } }; 

statuses.forEach(function (status){ 
    groupOperator["$group"][status.toLowerCase()] = { 
     "$sum": { 
      "$cond": [ { "$eq": [ "$loglevel", status ] }, 1, 0] 
     } 
    } 
}); 

db.names.aggregate([groupOperator]); 

输出

/* 1 */ 
{ 
    "_id" : "t1", 
    "error" : 2, 
    "warning" : 3, 
    "info" : 1 
} 

/* 2 */ 
{ 
    "_id" : "t2", 
    "error" : 4, 
    "warning" : 0, 
    "info" : 1 
} 

/* 3 */ 
{ 
    "_id" : "t3", 
    "error" : 0, 
    "warning" : 0, 
    "info" : 1 
} 
+0

关于if/else块的好处,但仍然在寻找DRY解决方案(不要复制loglevel的块) – madzohan

+0

@madzohan更新我的答案以包含DRY方法。 – chridam

+0

不错!你也可以告诉我哪种方式会更好(性能等) - 保持像mongodb中的分组逻辑或将其移动到编程语言,如Python? – madzohan

如果我理解正确,你想得到logle对每个名字的小品。 不应该有这帮助吗?

db.names.aggregate([{$group:{_id:{name:"$name",loglevel:"$loglevel"},sum:{$sum:1}}}]) 
+0

是的我早就试过这个了,但是我需要用'$ name'分组方式,所有的loglevel都注释为 – madzohan

+0

。刚刚看到@ Chridam的回答。猜猜这就是你想要的。 – KaSh