Mongodb拆分聚合结果

时间:2018-06-29 14:06:38

标签: mongodb mongodb-query aggregation-framework

我目前正在尝试仅使用mongodb将聚合结果分成两个不同的数组。

我的主要目标是创建两个具有相同分布的用户子集,涉及他们进行的互动次数。为此,我目前正在发出此请求:

db.getCollection('Interaction').aggregate([
 { $group : { _id : "$userId", count: { $sum: 1 }}},
 { $sort : { count : -1 }},
 { $group : { _id :{$mod : [_rand() * 2, 2]}, ids : { $push: "$_id"}}}   
}

我的主要问题实际上是,_rand()函数在聚合执行期间仅被调用一次,因此我的所有结果都放在一个数组中。

此外,随机分布也不是很好。有没有一种方法可以使用每个结果的索引?

编辑1:

在@dnickless回答之后,我仍然在groupBy部分中遇到发行问题。理想情况下,我想做这样的事情

db.getCollection('Interaction').aggregate([
        { $group : { _id : "$userId", count: { $sum: 1 }}},
        { $sort : { count : -1 }},
        { $bucket: {
                groupBy: { $mod: [ { $indexOfArray : ??? }, 2 ] },
                boundaries: [ 0, 1 ],
                default: 2,
                output: {
                  "users": { $push: "$_id"}
                }
            }
        }
    ],
    { allowDiskUse: true })

那可以将偶数索引和奇数索引分成两个单独的数组。但是我想在当前的汇总结果上应用$indexOfArray

在这里为您提供更多上下文是我的Interaction对象模型:

{ "_id" : ObjectId("5af01..."), "name" : "WATCH", "date" : ISODate("2018-05-07T09:32:53.219Z") }

没有铲斗部分,我得到以下结果:

{ "_id" : "5b1e7f...", "count" : 43.0 } 
{ "_id" : "5b1e75...", "count" : 41.0 } 
{ "_id" : "5b1e7a...", "count" : 40.0 }
...

我希望我的回答看起来像这样:

{
  { "_id" : 0, "users" : [ "5b1e7f...", "5b1e7a...", ... ] }, // even index results
  { "_id" : 1, "users" : [ "5b1e75...", ... ] }  // odd index results
}

我的最终目标是将用户分成两组,并将互动次数平均分配。

编辑2:

最后找到了解决我的问题的方法:

db.getCollection('Interaction').aggregate([
        { $group : { _id : "$userId", count: { $sum: 1 }}},
        { $sort : { count : -1 }},
        { $group : { _id : "whatever" , user : { $push : { _id : "$_id" , count : "$count"}}}},
        { $unwind : { path : "$user" , "includeArrayIndex" : "rank"}},
        { $bucket: {
                groupBy: { $mod: [ "$rank"  , 2 ] },
                boundaries: [ 0, 1 ],
                default: 2,
                output: {
                  "users": { $push: "$user._id"}
                }
            }
        }
    ],
    { allowDiskUse: true })

可能根本不是最优化的解决方案,但仍然可以完成工作:) 如果您有任何改进建议,我仍然对此感兴趣。

1 个答案:

答案 0 :(得分:0)

我不完全了解您在此处想要实现的目标而又没有看到一些示例输入和输出。但是,您是否尝试过使用$bucketAuto?像这样:

db.getCollection('Interaction').aggregate([
 { $group : { _id : "$userId", count: { $sum: 1 }}},
 { $bucketAuto : {
     groupBy : "$count",
     buckets : 2, // number of buckets goes here
     output : {
       ids : { $push : "$id" }
     }
   }
 }])

如果您想对分配方面做得更复杂,则可以尝试这样的方法,将所有偶数数都放入一个底池中,而将所有奇数数都放入一个底池中。

$bucket: {
    groupBy: { $mod: [ "$count", 2 ] },
    boundaries: [ 0, 1 ],
    default: 2,
    output: {
      "docs": { $push: "$$ROOT" }
    }
}

根据您的userId字段的类型,您可能会想出一个更“随机”的分布。

最后,我不确定

到底是什么意思
  

“有没有一种方法可以使用每个结果的索引?”

也许是诸如$size$arrayElemAt和/或$indexOfArray ...之类的东西?

或者,您可以尝试将已排序的数组$slice分成两个大小相等的部分(使用$size $divide乘以2),然后$reverseArray其中之一,然后然后$zip再次将两个阵列都排列起来,这将导致诸如洗牌游戏。之后,您需要将嵌套数组再次扁平化为单个数组(使用$reduce$concatArrays左右),然后再次将数组分为两部分,这应该是您要查找的内容我现在不太累,可以仔细考虑这里的统计部分。