MongoDB展开多个空数组

时间:2015-07-29 23:45:26

标签: mongodb mongodb-query aggregation-framework

数据库中的示例数据如下所示:

{
'data':
[
    'Log':
    {
        'IP':['8.8.8.8','8.8.4.4'],
        'URL':['www.google.com']
        'Hash' ['d2a12319bf1221ce7681928cc']
    },
    'Log':
    {
        'IP':['1.2.3.4'],
        'URL':['www.cnn.com']
        'Hash' []
    },

]
}

我正在尝试从上面的日志列表中聚合唯一的IP,URL和Hash列表。我当前的查询看起来像这样:

db.loglist.aggregate([{'$match':{'data.Log':{'$exists':true}}},
                {'$unwind':'$data'},
                {'$unwind':'$data.Log.URL'},
                {'$unwind':'$data.Log.Hash'},       
                {'$unwind':'$data.Log.IP'},
                {'$group':{'_id':'$ioc',
                            'FHList':{'$addToSet':'$data.Log.Hash'},
                            'URLList':{'$addToSet':'$data.Log.URL'},
                            'IPList':{'$addToSet':'$data.Log.IP'}}
                }]) 

如果对于每个日志,三个数组中的每个数组中至少有一个元素,则效果很好。但是,当任何一个日志中出现空数组时。 Mongo为整个查询返回空。我发现它是来自几个类似帖子的$ unwind的默认行为。但是,使用$ unwind的标准方法是什么,如果说我们没有结果" Hash",我们仍然可以保留" IP"和" URL"。

提前感谢您的回答。

1 个答案:

答案 0 :(得分:1)

$cond运算符是这里的主要帮助器,通过测试来查看数组是否为空,并将其替换为另一个值以便稍后过滤:

db.loglist.aggregate([
  {"$match":{"data.Log":{"$exists":true}}},
  {"$unwind":"$data"},
  { "$project": {
     "ioc": 1,
     "data": {
         "Log": {
            "IP": { "$cond": [
              { "$ne": [ "$IP", [] ] },
              "$IP",
              [false]
            ]},
            "URL": { "$cond": [
              { "$ne": [ "$URL", [] ] },
              "$URL",
              [false]
            ]},
            "Hash": { "$cond": [
              { "$ne": [ "$Hash", [] ] },
              "$Hash",
              [false]
            ]}
         }
     }
  }}
  {"$unwind":"$data.Log.URL"},
  {"$unwind":"$data.Log.Hash"},       
  {"$unwind":"$data.Log.IP"},
  {"$group":{
      "_id":"$ioc",
      "FHList":{"$addToSet":"$data.Log.Hash"},
      "URLList":{"$addToSet":"$data.Log.URL"},
      "IPList":{"$addToSet":"$data.Log.IP"}
  }},
  { "$project": {
      "FHList":{ "$setDifference": ["$FHList", [false]] },
      "URLList":{ "$setDifference": ["$URList", [false]] },
      "IPList":{ "$setDifference": ["$IPList", [false]] }
  }}
])  

一旦它构建了它,就会过滤掉不需要的值。

如果您的MongoDB版本低于2.6并且您没有$setDifference,那么您可以在再次展开后进行过滤,假设此处没有结果数组是空的:

db.loglist.aggregate([
  {"$match":{"data.Log":{"$exists":true}}},
  {"$unwind":"$data"},
  { "$project": {
     "ioc": 1,
     "data": {
         "Log": {
            "IP": { "$cond": [
              { "$ne": [ "$IP", [] ] },
              "$IP",
              [false]
            ]},
            "URL": { "$cond": [
              { "$ne": [ "$URL", [] ] },
              "$URL",
              [false]
            ]},
            "Hash": { "$cond": [
              { "$ne": [ "$Hash", [] ] },
              "$Hash",
              [false]
            ]}
         }
     }
  }}
  {"$unwind":"$data.Log.URL"},
  {"$unwind":"$data.Log.Hash"},       
  {"$unwind":"$data.Log.IP"},
  {"$group":{
      "_id":"$ioc",
      "FHList":{"$addToSet":"$data.Log.Hash"},
      "URLList":{"$addToSet":"$data.Log.URL"},
      "IPList":{"$addToSet":"$data.Log.IP"}
  }},
  { "$unwind": "$FHList" },
  { "$match": { "FHList": { "$ne": false } }},
  { "$unwind": "$URLList" },
  { "$match": { "URLList": { "$ne": false } }},
  { "$unwind": "$IPList" },
  { "$match": { "IPList": { "$ne": false } }},
  { "$group": {
      "_id": "$_id",
      "FHList":{ "$addToSet":"$FHList" },
      "URLList":{ "$addToSet":"$URLList" },
      "IPList":{ "$addToSet":"$IPList" }
  }}
])  

如果您的分组数组是空的,那么在第二种形式中它仍然很棘手但仍然可能。