在特殊情况下对MongoDB中的文档进行分组

时间:2017-12-07 07:21:03

标签: python mongodb pymongo

我的收藏包含

{name:'p1', age: 20}
{name: 'p2', age: 21}
{name: 'p3', age: 23}
{name: 'p4', ag:41 }

我想对这样的人进行分组,即对于该组中的任何人,该组中存在另一个人,使得他们的年龄之间的差异最多为2。 此处生成的组将包含

预期结果

[{name:'p1' ...}, {name:'p2' ...}, {name: 'p3'}]

年龄p2 -p1 = 1 and p3-p2 = 2

p1,p2,p3组成一个组

1 个答案:

答案 0 :(得分:1)

声明

在阅读其余答案之前,请先阅读https://docs.mongodb.com/manual/core/aggregation-pipeline-limits/ 预期问题中的结果文档将包含属于特定年龄组的所有文档的数组。 该数组的大小不能超过16MB ,因此下面的代码仅适用于非常小的小文档集合。

代码:

db.collection.aggregate([
    { $sort: { age: 1 } },
    { $group: {
            _id: null,
            ages: { $push: "$age" }
    } },
    { $addFields: {
        ranges: { $reduce: { 
            input: { $range: [ 1, { $size: "$ages" }, 1 ] }, 
            initialValue: [ [ { $arrayElemAt: [ "$ages", 0 ] } ] ], 
            in: { $cond: { 
                if:  { $gt: [
                    { $subtract: [ { $arrayElemAt: [ "$ages", "$$this" ] }, { $arrayElemAt: [ "$ages", { $subtract: [ "$$this", 1 ] } ] } ] },
                    2
                    ] }, 
                then: { $concatArrays: [ "$$value",  [ [ { $arrayElemAt: [ "$ages", "$$this" ] } ] ] ] }, 
                else: { $concatArrays: [ 
                    { $slice: [ "$$value" , { $subtract: [ { $size: "$$value" }, 1 ] } ] },
                    [ { $concatArrays: [ 
                        { $arrayElemAt: [ { $slice: [ "$$value" , -1 ] }, 0 ] }  ,  
                        [ { $arrayElemAt: [ "$ages", "$$this" ] } ]
                    ]  } ]
                ] }
            } }
        } } 
    } },
    { $unwind: "$ranges" }, 
    { $lookup: {
       from: "collection",
       localField: "ranges",
       foreignField: "age",
       as: "group"
     } },
     { $project: { _id: 0, group: 1 } }
])

可能需要一些解释的部分是如何计算年龄组。

为此,我们将所有年龄段的$group用于一个数组,然后$addFields"范围" - 年龄组中年龄最大的人与年龄最小的人之间存在差距的二维年龄组数组,大于2年。

使用$reduce所有年龄段的索引数组$range来计算数组,但首先是初始值。

reduce表达式是$cond,用于计算所有年龄段数组的当前和前一个($subtract)元素之间的差异。

如果大于2,则使用$concatArrays添加新的年龄组。否则,使用$slice将年龄添加到最旧的组,以推送到范围数组中的最后一个组,并$setUnion以消除重复。

当计算年龄组时,我们$lookup按年龄{{3}}将相同的集合分组到"组"阵列。