为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

胡子哥哥 2023-07-20 16:11:59
我正在研究版本控制,我们有基于UUIDs和 的文档jobUuids,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 回答

?
侃侃无极

TA贡献2051条经验 获得超10个赞

根据评论更新:


但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


查看完整回答
反对 回复 2023-07-20
?
四季花海

TA贡献1811条经验 获得超5个赞

这是我要做的:

  1. 匹配阶段使用 $in 而不是 $or (为了可读性)

  2. 小组赛阶段使用 $uuid 上的 _id,就像您所做的那样,但不要将所有数据推送到数组中,而是更具选择性。_id 已经存储了 $uuid,因此没有理由再次捕获它。每个 $uuid 的名称必须始终相同,因此仅采用第一个实例。根据匹配,jobUuid 只有两种可能性,但这将假设它将是“默认”或其他内容,并且可能会出现多次非“默认”jobUuid。使用“$addToSet”而不是推送到数组,以防用户多次出现相同的 jobUuid,此外,在添加到集合之前,使用条件仅添加非“默认”jobUuid,使用 $$REMOVE以避免在 jobUuid 为“默认”时插入 null。

  3. 最后,“$project”来清理一切。如果 jobUuids 数组的元素 0 不存在(为 null),则该用户除了 jobUuid 为“默认”之外没有其他可能性,因此使用“$ifNull”进行测试并根据需要设置“默认”。这里可能有超过 1 个 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"

            ]

        }

    }

}]


查看完整回答
反对 回复 2023-07-20
?
慕尼黑的夜晚无繁华

TA贡献1864条经验 获得超6个赞

我能够通过以下聚合查询解决这个问题,

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

  • 然后,使用小组赛阶段根据 uuid 对结果进行分组,并且我们也会对结果进行计数。

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

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

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

查询如下:

[

    {

      $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

              ]

            }

          }

        }

      }

    }

  ]


查看完整回答
反对 回复 2023-07-20
  • 3 回答
  • 0 关注
  • 142 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信