MongoDB 展开多个空数组

MongoDB unwind multiple empty arrays

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

{
'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 和哈希的列表。我当前的查询看起来像这样:

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 returns 整个查询为空。我从一些类似的帖子中发现这是 $unwind 的默认行为。但是使用 $unwind 的标准方法是什么,如果说我们没有 "Hash" 的结果,我们仍然可以保留 "IP" 和 "URL" 的结果。

提前感谢您的回答。

$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" }
  }}
])  

如果你的分组数组是空的,那么第二种形式就很棘手,但仍然有可能。