Ограничить агрегацию в групповой агрегации

У меня была такая коллекция, но с гораздо большим количеством данных.

{
  _id: ObjectId("db759d014f70743495ef1000"),
  tracked_item_origin: "winword",
  tracked_item_type: "Software",
  machine_user: "mmm.mmm",
  organization_id: ObjectId("a91864df4f7074b33b020000"),
  group_id: ObjectId("20ea74df4f7074b33b520000"),
  tracked_item_id: ObjectId("1a050df94f70748419140000"),
  tracked_item_name: "Word",
  duration: 9540,
}

{
  _id: ObjectId("2b769d014f70743495fa1000"),
  tracked_item_origin: "http://www.facebook.com",
  tracked_item_type: "Site",
  machine_user: "gabriel.mello",
  organization_id: ObjectId("a91864df4f7074b33b020000"),
  group_id: ObjectId("3f6a64df4f7074b33b040000"),
  tracked_item_id: ObjectId("6f3466df4f7074b33b080000"),
  tracked_item_name: "Facebook",
  duration: 7920,
}

Я выполняю агрегацию, возвращая сгруппированные данные следующим образом:

{"_id"=>{"tracked_item_type"=>"Site", "tracked_item_name"=>"Twitter"}, "duration"=>288540},
{"_id"=>{"tracked_item_type"=>"Site", "tracked_item_name"=>"ANoticia"}, "duration"=>237300},
{"_id"=>{"tracked_item_type"=>"Site", "tracked_item_name"=>"Facebook"}, "duration"=>203460},
{"_id"=>{"tracked_item_type"=>"Software", "tracked_item_name"=>"Word"}, "duration"=>269760},
{"_id"=>{"tracked_item_type"=>"Software", "tracked_item_name"=>"Excel"}, "duration"=>204240}

Простой код агрегации:

AgentCollector.collection.aggregate(
  {'$match' => {group_id: '20ea74df4f7074b33b520000'}},
  {'$group' => {
    _id: {tracked_item_type: '$tracked_item_type', tracked_item_name: '$tracked_item_name'},
    duration: {'$sum' => '$duration'}
  }},
  {'$sort' => {
    '_id.tracked_item_type' => 1,
    duration: -1
  }}
)

Есть способ ограничить только 2 предмета по клавише tracked_item_type? Бывший. 2 сайта и 2 софта.


person Ronan    schedule 20.02.2014    source источник
comment
Вы хотите ограничить результат двумя сайтами и двумя программами (всего четыре документа)?   -  person heinob    schedule 20.02.2014
comment
Да @heinob, это оно.   -  person Ronan    schedule 25.02.2014


Ответы (1)


Поскольку ваш вопрос в настоящее время остается неясным, я действительно надеюсь, что вы имеете в виду, что хотите указать два ключа Site и 2 ключа Software, потому что это хороший и простой ответ, который вы можете просто добавить к своей фазе $match, как в:

{$match: {
    group_id: "20ea74df4f7074b33b520000",
    tracked_item_name: {$in: ['Twitter', 'Facebook', 'Word', 'Excel' ] }
}},

И мы все можем радоваться и радоваться ;)

Однако, если ваш вопрос носит более дьявольский характер, например, получение двух лучших записей Sites и Software из результата по продолжительности, то мы очень благодарны вам за создание этой мерзости.

Предупреждение:

Ваш пробег может варьироваться в зависимости от того, что вы на самом деле хотите сделать, или от того, собирается ли это взорваться из-за размера ваших результатов. Но это следует в качестве примера того, что вас ждет:

db.collection.aggregate([

    // Match items first to reduce the set
    {$match: {group_id: "20ea74df4f7074b33b520000" }},

    // Group on the types and "sum" of duration
    {$group: {
        _id: {
            tracked_item_type: "$tracked_item_type",
            tracked_item_name: "$tracked_item_name"
         },
         duration: {$sum: "$duration"}
    }},

    // Sort by type and duration descending
    {$sort: { "_id.tracked_item_type": 1, duration: -1 }},

    /* The fun part */

    // Re-shape results to "sites" and "software" arrays 
    {$group: { 
        _id: null,
        sites: {$push:
            {$cond: [
                {$eq: ["$_id.tracked_item_type", "Site" ]},
                { _id: "$_id", duration: "$duration" },
                null
            ]}
        },
        software: {$push:
            {$cond: [
                {$eq: ["$_id.tracked_item_type", "Software" ]},
                { _id: "$_id", duration: "$duration" },
                null
            ]}
        }
    }},


    // Remove the null values for "software"
    {$unwind: "$software"},
    {$match: { software: {$ne: null} }},
    {$group: { 
        _id: "$_id",
        software: {$push: "$software"}, 
        sites: {$first: "$sites"} 
    }},

    // Remove the null values for "sites"
    {$unwind: "$sites"},
    {$match: { sites: {$ne: null} }},
    {$group: { 
        _id: "$_id",
        software: {$first: "$software"},
        sites: {$push: "$sites"} 
    }},


    // Project out software and limit to the *top* 2 results
    {$unwind: "$software"},
    {$project: { 
        _id: 0,
        _id: { _id: "$software._id", duration: "$software.duration" },
        sites: "$sites"
    }},
    {$limit : 2},


    // Project sites, grouping multiple software per key, requires a sort
    // then limit the *top* 2 results
    {$unwind: "$sites"},
    {$group: {
        _id: { _id: "$sites._id", duration: "$sites.duration" },
        software: {$push: "$_id" }
    }},
    {$sort: { "_id.duration": -1 }},
    {$limit: 2}

])  

Теперь это приводит к *не совсем чистому набору результатов, который был бы идеальным, но это то, с чем можно работать программно, и это лучше, чем фильтрация предыдущих результатов в цикле. (Мои данные из тестирования)

{
    "result" : [
        {
            "_id" : {
                "_id" : {
                    "tracked_item_type" : "Site",
                    "tracked_item_name" : "Digital Blasphemy"
                 },
                 "duration" : 8000
            },
            "software" : [
                {
                    "_id" : {
                        "tracked_item_type" : "Software",
                        "tracked_item_name" : "Word"
                    },
                    "duration" : 9540
                },

                {
                    "_id" : {
                        "tracked_item_type" : "Software",
                        "tracked_item_name" : "Notepad"
                    },
                    "duration" : 4000
                }
            ]
        },
        {
            "_id" : {
                "_id" : {
                    "tracked_item_type" : "Site",
                    "tracked_item_name" : "Facebook"
                 },
                 "duration" : 7920
            },
            "software" : [
                {
                    "_id" : {
                        "tracked_item_type" : "Software",
                         "tracked_item_name" : "Word"
                    },
                    "duration" : 9540
                },
                {
                    "_id" : {
                        "tracked_item_type" : "Software",
                        "tracked_item_name" : "Notepad"
                    },
                    "duration" : 4000
                }
            ]
        }
    ],
    "ok" : 1
}

Итак, вы видите, что вы получаете 2 верхних элемента Sites в массиве, в каждый из которых встроены 2 верхних элемента Software. Сама агрегация не может прояснить это, потому что нам нужно будет повторно объединить элементы, которые мы разделили, чтобы сделать это, и пока нет оператора, который мы могли бы использовать для этого. действие.

Но это было весело. Это не полностью способ, но большая часть пути, и превращение этого в ответ из 4 документов было бы относительно тривиальным кодом. Но голова болит уже.

person Neil Lunn    schedule 21.02.2014
comment
Большое спасибо, Нил. Забыл сказать, что заказ и ограничение по продолжительности были моей бедой. Мне очень нравится ваш ответ, и я постараюсь его улучшить. Если получу, отпишусь здесь ;) - person Ronan; 25.02.2014
comment
@RonanRodrigoNunes Что ж, если вы действительно получили улучшение, то, возможно, место для публикации — здесь И я только что нашел способ получить окончательный результат с помощью этого ответа - person Neil Lunn; 27.02.2014