MongoDB: 第二次匹配聚合失败后保留第一次匹配的结果

MongoDB: Keep result of first match after second match failed in aggregation

在 MongoDB 中,我必须从文档中仅推送与对象 属性 匹配的对象数组。使用聚合我可以实现这一目标。

但是我对匹配有疑问。

Users
    .aggregate()
    .match({_id: userId})
    .unwind('tags')
    .match({"tags.name": "verified"})
    .group({
            "_id": {"id": "$_id", "type": "$type"},
            "tags": {$push: "$tags"}
        })

当用户有 "verified" 标签时,这工作正常。但是当用户没有时,结果只是 [] (空数组)。

我知道这是已知的行为,但是我如何获得匹配的用户(因为第一个匹配将 return 如果有效用户)单独使用 tags 作为空数组。

样本集:

{
  _id: 123,
  type: "normal",
  tags: [{name: "verified", "tagId": 1}, {name: "good", "tagId": 2}]
}

{
  _id: 122,
  type: "normal",
  tags: [{name: "good", "tagId": 2}]
}

所以当我对 userId = 123 应用上述聚合时,结果是,

{
   result: [
     {
       "_id": {
              "id": 123,
              "type": "normal"
       }
       "tags": [{name: "verified", "tagId": 1}]
     }
   ],
   "ok": 1
}

同理如果我用userId = 122申请,结果是[]

但预期的结果是,

   {
       result: [
         {
           "_id": {
                  "id": 122,
                  "type": "normal"
           }
           "tags": []
         }
       ],
       "ok": 1
    }

您基本上需要测试匹配数是否为 0 并将结果数组换成一个空数组,否则为空数组。将不匹配的值替换为 null insead:

这是一种方法,但如果可能的话,另一种方法是只使用 $redact 来过滤数组:

Users.aggregate(
  [
    { "$match": { "_id": userId } },
    { "$redact": {
        "$cond": {
          "if": {
            "$eq": [ { "$ifNull": [ "$name", "verified" ] }, "verified" ]
          },
          "then": "$$DESCEND",
          "else": "$$PRUNE"
        }
    }}
  ],
  function(err,results) {

  }
);

在即将发布的 MongoDB 版本中(截至撰写本文时),还将有一个 $filter 聚合运算符。这很简单,并且也会留下空数组:

Users.aggregate(
  [
    { "$project": {
      "type": 1,
      "tags": {
        "$filter": {
          "input": "$tags",
          "as": "el",
          "cond": {
            "$eq": [ "$$el.name", "verified" ]
          }
        }
      }
    }}
  ],
  function(err,results) {

  }
);

但除此之外,您基本上计算匹配的数量并在 $cond 中交换数组,其中匹配计数为 0:

Users.aggregate(
 [
    { "$match": { "_id": userId } },
    { "$unwind": "$tags" },
    { "$group": {
      "_id": {
        "_id": "$_id",
        "type": "$type"
      },
      "tags": { 
        "$push": {
          "$cond": [
            { "$eq": [ "$tags.name", "verified" ] },
            "$tags",
            null
          ]
        }
      },
      "count": { 
        "$sum": {
          "$cond": [
            { "$eq": [ "$tags.name", "verified" ] },
            1,
            0
          ]
        }
      }
    }},
    { "$unwind": "$tags" },
    { "$match": { 
      "$or": [
        { "tags.name": "verified" },
        { "count": 0 }
      ]
    }},
    { "$group": {
      "_id": "$_id",
      "tags": { "$push": "$tags" },
      "count": { "$first": "$count" }
    }},
    { "$project": {
      "tags": {
        "$cond": [
          { "$eq": [ "$count", 0 ] },
          [],
          "$tags"
        ]
      }
    }}
  ],
  function(err,results) {

  }
);

如果可以的话,我会选择前者。然后在 MongoDB 的生产版本支持它后使用 $filter