【问题标题】:MongoDB: Keep result of first match after second match failed in aggregationMongoDB:在聚合中第二次匹配失败后保留第一次匹配的结果
【发布时间】:2015-08-27 22:10:31
【问题描述】:

在 MongoDB 中,我只需要从文档中推送与对象属性匹配的对象数组。使用聚合我可以做到这一点。

但是我对匹配有问题。

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

当用户有“已验证”标签时,这可以正常工作。但是当用户没有那个结果时,只是[](空数组)。

我知道这是已知的行为,但我如何才能获得匹配的用户(因为如果有效用户将返回第一个匹配项),而 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
    }

【问题讨论】:

  • 你能发一份样本文件吗?
  • 目前的结果也会有所帮助
  • @ZeMoon 添加了样本收集和预期结果

标签: mongodb mongoose mongodb-query aggregation-framework


【解决方案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

【讨论】:

  • 第一个就像魅力一样。但第二个解决方案没有奏效。请删除或更正,以便我接受答案。还有问题,第一个解决方案工作正常,但是在查询中,您没有提到“标签”字段名称,它是如何匹配的,请解释一下。
  • @sincerekamal $redact 管道运算符(链接到答案中的文档)在此处使用的表单中遍历任何级别的文档,因此只关心里面的“名称”字段数组“下降”到其中时。因此,有趣的构造也不会删除顶级文档(没有“名称”字段)。我在第二个清单中看到的唯一问题(我希望你无论如何都不会使用)是我输入了“$userId”而不是“$_id”。这也是固定的。
猜你喜欢
  • 2017-11-30
  • 1970-01-01
  • 2013-09-30
  • 1970-01-01
  • 2018-11-24
  • 2018-06-27
  • 1970-01-01
  • 2016-10-20
  • 1970-01-01
相关资源
最近更新 更多