【问题标题】:mongodb: sort nested array using dynamic parmeter of field namemongodb:使用字段名称的动态参数对嵌套数组进行排序
【发布时间】:2020-08-20 04:59:16
【问题描述】:

假设在聚合管道中,其中一个步骤会产生以下结果:

{
    customer: "WN",
    sort_category: "category_a",
    locations: [ 
        {
            city: "Elkana", 
            category_a: 11904.0, 
            category_b: 74.0,
            category_c: 657.0,
        }, 
        {
            city: "Haifa",
            category_a: 20.0,
            category_b: 841.0,
            category_c: 0,
        }, 
        {
            city" : "Jerusalem",
            category_a: 451.0,
            category_b: 45.0,
            category_c: 712.0,
        }
    ]
}
{
...
}

下一步是对集合中每个文档的嵌套对象列表进行排序。

嵌套对象列表应按包含字段名称的动态参数排序。

例如 - locations 的列表应该按 category_a 的值排序。

category_asort_category 字段中给出的参数。

【问题讨论】:

  • 上述情况的主要挑战是集合中的每个文档都可以按不同的字段排序,具体取决于文档顶部出现的参数。
  • 我认为 $sort 运算符或任何其他运算符都不可能,可能需要在 v4.4 中使用 $function 创建自己的运算符,您使用哪个平台来执行此查询?前任。节点?你用的是什么版本的mongodb?
  • mongodb 4.2.3版

标签: mongodb sorting aggregate


【解决方案1】:

这里是不使用$function的解决方案:

db.tests.aggregate([
    {$unwind:"$locations"}, 
    {$project: {
        _id: 1,
        customer: 1,
        sort_category: 1,
        locations: 1,
        locationsKV:{$objectToArray:"$locations"}
        }
    }, 
    {$unwind:"$locationsKV"}, 
    {$project:{
        _id: 1,
        customer: 1,
        sort_category: 1,
        locations: 1,
        locationsKV: 1,
        category: {
            $cond:[{$eq: ["$sort_category","$locationsKV.k"]}, 
            "$locationsKV.v", 0]},
        agg: {
            $cond: [{$eq: ["$sort_category","$locationsKV.k"]}, true, false]
            }
        }
    }, 
    {$match: {agg: true}}, 
    {$sort: {category: 1}}, 
    {$group: {
        _id: "$_id",
        customer: {$first: "$customer"},
        sort_category: {$first: "$sort_category"},       
        locations: {$push: "$locations"}
        }
    }, 
    {$project:{ _id: 0}}
])

【讨论】:

    【解决方案2】:

    您可以尝试自定义方式,

    • $addFields 将使用 $map$reducelocations 数组中的每个对象中添加一个名为 category 的密钥 sort_category 的复制字段
    • $unwind解构locations数组
    • $sort by category 字段,我们添加到 locations 数组中
    • $project 删除 category 字段
    • $group by _id 并重构 locations 数组
    • $project 删除 _id 字段
    db.collection.aggregate([
      {
        $addFields: {
          locations: {
            $map: {
              input: "$locations",
              as: "l",
              in: {
                $mergeObjects: [
                  "$$l",
                  {
                    category: {
                      $reduce: {
                        input: { $objectToArray: "$$l" },
                        initialValue: null,
                        in: {
                          $cond: [{ $eq: ["$$this.k", "$sort_category"] }, "$$this.v", "$$value"]
                        }
                      }
                    }
                  }
                ]
              }
            }
          }
        }
      },
      { $unwind: "$locations" },
      { $sort: { "locations.category": 1 } },
      { $project: { "locations.category": 0 } },
      {
        $group: {
          _id: "$_id",
          customer: { $first: "$customer" },
          sort_category: { $first: "$sort_category" },
          locations: { $push: "$locations" }
        }
      },
      { $project: { _id: 0 } }
    ])
    

    Playground


    如果您计划将您的 MongoDB 升级到 v4.4 或者这对其他人有帮助,$function 是自定义操作和用户定义操作的选项。

    有3个属性:

    • body 函数定义。可以指定函数定义为BSON类型Code或者String,使用function(){定义我们自己的函数,我们传递了locations数组和sort_category动态字段,函数逻辑降序排序
    • args 传递给函数体的参数
    • lang 正文中使用的语言。您必须指定 lang: "js"
    db.collection.aggregate([
      {
        $addFields: {
          locations: {
            $function: {
    
              body: function(locations, sort_category){
                return locations.sort(function(a, b){
                  // DESCENDING ORDER
                  return b[sort_category] - a[sort_category]
                  // ASCENDING ORDER
                  // return a[sort_category] - b[sort_category]
                })
              },
    
              args: ["$locations", "$sort_category"],
    
              lang: "js"
    
            }
          }
        }
      }
    ])
    

    有关限制和注意事项的更多指南Refer

    【讨论】:

    • 有没有办法在不使用$function的情况下做到这一点?
    • 我从未见过其他运营商的任何例子,我不确定,也在寻找你,如果可能的话会更新你。
    • 我找到了一个不使用$function的解决方案(见下面的答案)。
    • @ElhananSchwarts 我添加了另一种方法,您可以防止第二次复制位置数组和第二次展开管道。表示这是您查询的优化版本。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-02-06
    • 1970-01-01
    • 2020-04-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多