Результат агрегации расщепления по Монгодбу - PullRequest
0 голосов
/ 29 июня 2018

В настоящее время я пытаюсь разделить результат агрегации на два разных массива, используя только 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 к текущему результату агрегирования.

Чтобы дать вам больше контекста, вот моя объектная модель взаимодействия:

{ "_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 группы с равномерно распределенным количеством взаимодействий.

Редактировать 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 голосов
/ 30 июня 2018

Я не совсем понимаю, чего именно вы пытаетесь достичь, не видя пример ввода и вывода. Однако пытались ли вы использовать $ 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 $ делить d на 2), а затем $ reverseArray один из них, а затем $ zip оба массива снова, что должно привести к чему-то похожему, когда вы перетасовываете колоду игральных карт. После этого вам нужно будет снова объединить вложенный массив в один (используя $ redu и $ concatArrays или около того), а затем снова разрезать массив на две части, которые должны быть что вы ищете, если я не слишком устал, чтобы обдумать статистические части здесь.

...