mongo根据条件聚合以过滤文档以进行版本控制

时间:2020-11-10 18:19:46

标签: javascript node.js mongodb aggregation-framework

我正在进行版本控制,我们有基于UUIDsjobUuids的文档,而jobUuids是与当前工作的用户关联的文档。我对这些集合有一些汇总查询,我需要根据作业UUID更新这些查询,

通过聚合查询获取的结果应为

  • 如果当前用户jobUuid文档不存在,则将返回带有jobUuid: "default"的主文档(该文档不包含任何jobUuid)
  • 如果作业uuid存在,则仅返回文档。

我有一个$match用于根据特定条件获取这些文档,我需要根据上述条件从这些文档中过滤掉文档,并在下面显示一个示例,

数据如下:

[
  {
    "uuid": "5cdb5a10-4f9b-4886-98c1-31d9889dd943",
    "name": "adam",
    "jobUuid": "default",
  },
  {
    "uuid": "5cdb5a10-4f9b-4886-98c1-31d9889dd943",
    "jobUuid": "d275781f-ed7f-4ce4-8f7e-a82e0e9c8f12",
    "name": "adam"
  },
  {
    "uuid": "b745baff-312b-4d53-9438-ae28358539dc",
    "name": "eve",
    "jobUuid": "default",
  },
  {
    "uuid": "b745baff-312b-4d53-9438-ae28358539dc",
    "jobUuid": "d275781f-ed7f-4ce4-8f7e-a82e0e9c8f12",
    "name": "eve"
  },
  {
    "uuid": "26cba689-7eb6-4a9e-a04e-24ede0309e50",
    "name": "john",
    "jobUuid": "default",
  }
]

"jobUuid": "d275781f-ed7f-4ce4-8f7e-a82e0e9c8f12"的结果应为:

[
  {
    "uuid": "5cdb5a10-4f9b-4886-98c1-31d9889dd943",
    "jobUuid": "d275781f-ed7f-4ce4-8f7e-a82e0e9c8f12",
    "name": "adam"
  },
  {
    "uuid": "b745baff-312b-4d53-9438-ae28358539dc",
    "jobUuid": "d275781f-ed7f-4ce4-8f7e-a82e0e9c8f12",
    "name": "eve"
  },
  {
    "uuid": "26cba689-7eb6-4a9e-a04e-24ede0309e50",
    "name": "john",
    "jobUuid": "default",
  }
]

基于上述条件,是否可以在聚合查询中过滤文档以提取特定作业uuid的文档?

编辑1:我得到了以下解决方案,效果很好,我想要一个更好的解决方案,消除所有这些嵌套阶段。

编辑2:使用实际的UUID更新了数据,我仅将name作为另一个字段,我们确实有n个字段,这些字段与此处无关,但最后需要(针对那些想在所有字段上使用投影的人都提及了这一点。)

3 个答案:

答案 0 :(得分:3)

根据评论进行更新:

,但是UUID是字母数字字符串,如上所示,它是否具有 对这些排序的影响,并且由于我们没有使用条件 得到结果,我担心这会引起问题。

您可以使用其他字段来匹配排序顺序,使其与in表达式中的值相同。确保您提供默认值作为最后一个值。

[
  {"$match":{"jobUuid":{"$in":["d275781f-ed7f-4ce4-8f7e-a82e0e9c8f12","default"]}}},
  {"$addFields":{ "order":{"$indexOfArray":[["d275781f-ed7f-4ce4-8f7e-a82e0e9c8f12","default"], "$jobUuid"]}}},
  {"$sort":{"uuid":1, "order":1}},
  {
    "$group": {
      "_id": "$uuid",
      "doc":{"$first":"$$ROOT"}
    }
  },
  {"$project":{"doc.order":0}},
  {"$replaceRoot":{"newRoot":"$doc"}}
]

此处的示例-https://mongoplayground.net/p/wXiE9i18qxf

原始

您可以使用以下查询。如果存在用于uuid的查询,则查询将选择非默认文档,否则将选择默认文档作为唯一文档。

[
  {"$match":{"jobUuid":{"$in":[1,"default"]}}},
  {"$sort":{"uuid":1, "jobUuid":1}},
  {
    "$group": {
      "_id": "$uuid",
      "doc":{"$first":"$$ROOT"}
    }
  },
  {"$replaceRoot":{"newRoot":"$doc"}}
]

此处的示例-https://mongoplayground.net/p/KrL-1s8WCpw

答案 1 :(得分:1)

这就是我要做的:

  1. 使用$ in而不是$ or的匹配阶段(出于可读性)
  2. 和您一样,在$ uuid上的
  3. group阶段使用_id,但是不要将所有数据都放入数组,而是要更具选择性。 _id已经存储$ uuid,因此没有理由再次捕获它。每个$ uuid的名称必须始终相同,因此只能使用第一个实例。根据匹配情况,jobUuid只有两种可能,但这将假定它是“默认”或其他值,并且可能有不止一次的非“默认” jobUuid。如果用户多次出现相同的jobUuid,则使用“ $ addToSet”而不是推送到数组,同样,在添加到集合之前,请使用$ REMOVEVE有条件地仅添加非“默认” jobUuids避免在jobUuid为“默认”时插入null。
  4. 最后,使用“ $ project”进行清理。如果jobUuids数组的元素0不存在(为null),则该用户除了jobUuid为“默认”之外没有其他可能性,因此请使用“ $ ifNull”进行测试并适当地设置“ default”。此处可能有多个jobUuid,具体取决于您的数据库/应用程序中是否允许该值,由您自己决定如何处理(采用最高,最低等)。

经过以下测试:https://mongoplayground.net/p/e76cVJf0F3o

[{
    "$match": {
        "jobUuid": {
            "$in": [
                "1",
                "default"
            ]
        }
    }
},
{
    "$group": {
        "_id": "$uuid",
        "name": {
            "$first": "$name"
        },
        "jobUuids": {
            "$addToSet": {
                "$cond": {
                    "if": {
                        "$ne": [
                            "$jobUuid",
                            "default"
                        ]
                    },
                    "then": "$jobUuid",
                    "else": "$$REMOVE"
                }
            }
        }
    }
},
{
    "$project": {
        "_id": 0,
        "uuid": "$_id",
        "name": 1,
        "jobUuid": {
            "$ifNull": [{
                    "$arrayElemAt": [
                        "$jobUuids",
                        0
                    ]
                },
                "default"
            ]
        }
    }
}]

答案 2 :(得分:0)

我能够通过以下汇总查询解决此问题,

  • 我们首先要提取仅与用户提供的jobUuid或“匹配”部分中的"default"匹配的结果。

  • 然后将结果基于uuid进行分组,并使用分组阶段,并且我们也在对结果进行计数。

  • 首先使用replaceRoot中的条件,我们正在检查分组文档的长度,

  • 如果分组文档的长度大于或等于2,则为 过滤与提供的jobUuid相匹配的文档。

  • 如果它小于或等于1,那么我们正在检查它是否与default jobUuid匹配并返回它。

查询如下:

[
    {
      $match: {
        $or: [{ jobUuid:1 },{ jobUuid: 'default'}]
      }
    },
    {
      $group: {
        _id: '$uuid',
        count: {
          $sum: 1
        },
        docs: {
          $push: '$$ROOT'
        }
      }
    },
    {
      $replaceRoot: {
        newRoot: {
          $cond: {
            if: {
              $gte: [
                '$count',
                2
              ]
            },
            then: {
              $arrayElemAt: [
                {
                  $filter: {
                    input: '$docs',
                    as: 'item',
                    cond: {
                      $ne: [
                        '$$item.jobUuid',
                        'default'
                      ]
                    }
                  }
                },
                0
              ]
            },
            else: {
              $arrayElemAt: [
                {
                  $filter: {
                    input: '$docs',
                    as: 'item',
                    cond: {
                      $eq: [
                        '$$item.jobUuid',
                        'default'
                      ]
                    }
                  }
                },
                0
              ]
            }
          }
        }
      }
    }
  ]