如何使用$ unwind解决空数组?

tes*_*ode 6 mongodb mongodb-query aggregation-framework

我有一张桌子并保存如下:

{ "_id" : ObjectId("5716617f4af77ca97a9614bd"), "count" : 1, "author" : "Tony", "music" : [ { "_id" : ObjectId("571661cd4af77ca97a9614c1"), "count" : 2, "author" : "Tony" } ] }
{ "_id" : ObjectId("5716617f4af77ca97a9614be"), "count" : 2, "author" : "Joe", "music" : [ { "_id" : ObjectId("571661cd4af77ca97a9614c0"), "count" : 1, "author" : "Joe" } ] }
{ "_id" : ObjectId("5716617f4af77ca97a9614bf"), "count" : 3, "author" : "Mary", "music" : [ ] }
Run Code Online (Sandbox Code Playgroud)

我希望找到"$ count">"$ music.count"的记录数.但当我做{$ unwind:"$ music"}时,我得到以下信息:

{ "_id" : ObjectId("5716617f4af77ca97a9614bd"), "count" : 1, "author" : "Tony", "music" : { "_id" : ObjectId("571661cd4af77ca97a9614c1"), "count" : 2, "author" : "Tony" } }
{ "_id" : ObjectId("5716617f4af77ca97a9614be"), "count" : 2, "author" : "Joe", "music" : { "_id" : ObjectId("571661cd4af77ca97a9614c0"), "count" : 1, "author" : "Joe" } }
Run Code Online (Sandbox Code Playgroud)

第三条记录消失了.我怎样才能得到如下结果:

{ "_id" : ObjectId("5716617f4af77ca97a9614bd"), "count" : 1, "author" : "Tony", "music" : { "_id" : ObjectId("571661cd4af77ca97a9614c1"), "count" : 2, "author" : "Tony" } }
{ "_id" : ObjectId("5716617f4af77ca97a9614be"), "count" : 2, "author" : "Joe", "music" : { "_id" : ObjectId("571661cd4af77ca97a9614c0"), "count" : 1, "author" : "Joe" } }
{ "_id" : ObjectId("5716617f4af77ca97a9614bf"), "count" : 3, "author" : "Mary", "music" : {"count": 0} }
Run Code Online (Sandbox Code Playgroud)

初始记录是通过$ loopup得到的,总代码如下:

db.bookAuthors.aggregate([{
$lookup:{from:"musicAuthors", localField:"author", foreignField:"author",as:"music"}},
{$unwind:"$music"},
{$project:{_id:"$author",count:1,music:1}},
{$match:{$gt:["$count","$music.count"]}},
{$group:{_id:null,count:{$sum:1}}}
])
Run Code Online (Sandbox Code Playgroud)

如何查找"$ count">"$ music.count"的记录数?在这个例子中,结果应该是2.但是现在由于解开问题,我得到1.谢谢.

Nei*_*unn 19

在MongoDB 3.2中(如果有的话,您可以使用$lookup),$unwind操作员可以preserveNullAndEmptyArrays选择.这会将行为更改为"不"从数组实际上为"空"的结果中删除文档:

db.bookAuthors.aggregate([
  { "$lookup":{
    "from": "musicAuthors", 
    "localField": "author", 
    "foreignField": "author",
    "as": "music"
  }},
  { "$unwind": { "path": "$music", "preserveNullAndEmptyArrays": true },
  { "$project": {
      "count": 1,
      "author": 1,
      "music": {
        "$ifNull": [ "$music", { "$literal": { "count": 0 } }] },
      }
  }}
])
Run Code Online (Sandbox Code Playgroud)

并且$ifNull在这种情况下替换了缺失值.

但实际上,既然你在这里的关联是完全1:1放弃$unwind了,只需要替换空数组:

db.bookAuthors.aggregate([
  { "$lookup":{
    "from": "musicAuthors", 
    "localField": "author", 
    "foreignField": "author",
    "as": "music"
  }},
  { "$project": {
    "count": 1,
    "author": 1,
    "music": {
      "$ifNull": [
        { "$arrayElemAt": [ "$music", 0 ] },
        { "$literal": { "count": 0 } }
      ]
    }
  }}
])
Run Code Online (Sandbox Code Playgroud)

如果$arrayElemAt0索引处找不到任何内容(因此为"空"),那么$ifNull将像以前一样返回备用值.当然它确实找到了什么,然后返回该值.

但同样,您的具体问题仍然有一个更好的解决方案,这也是不需要的$unwind.因为您可以使用数组"内联"计算"计数"条件:

db.bookAuthors.aggregate([
  { "$lookup":{
    "from": "musicAuthors", 
    "localField": "author", 
    "foreignField": "author",
    "as": "music"
  }},
  { "$group": {
    "_id": null,
    "count": {
      "$sum": {
        "$cond": {
          "if": {
            "$gt": [
              "$count",
              { "$sum": {
                "$map": {
                  "input": "$music",
                  "as": "el",
                  "in": "$$el.count"
                }
              }}
            ]
          },
          "then": 1,
          "else": 0
        }
      }
    }
  }}
])
Run Code Online (Sandbox Code Playgroud)

这里的$sum运算符用于它的两个用例,因为它是传统的"累加器"和数组中"求和"值的新角色.的$map操作者查看每个阵列元素,并返回值,以$sum产生总计."空"数组将返回为0.

然后$cond进行比较以确定数组中返回的总数是否小于"count"文档中的属性.其中truea 1为累加器返回,以及false它到达的位置0.

最终结果当然是2因为"第一"和"第三"文件实际上与累加器内的条件匹配.所以这确实是最有效的方法,即使它在这个过程中看起来有点"长篇大论".