4

У меня есть несколько документов с этой схемой, каждый документ для каждого продукта в день:MongoDB Совокупная сумма Каждый ключ на поддокумента

{ 
    _id:{}, 
    app_id:'DHJFK67JDSJjdasj909', 
    date:'2014-08-07', 
    event_count:32423, 
    event_count_per_type: { 
     0:322, 
     10:4234, 
     20:653, 
     30:7562 
    } 
} 

Я хотел бы получить сумму каждого event_type для конкретного DATE_RANGE ,
Это результат, который я ищу, где каждый тип события был суммирован по всем документам. Ключи для event_count_per_type могут быть любыми, поэтому мне нужно что-то, что может проходить через каждый из них, а не быть имплицитным с их именами.

{ 
    app_id:'DHJFK67JDSJjdasj909', 
    event_count:324236456, 
    event_count_per_type: { 
     0:34234222, 
     10:242354, 
     20:456476, 
     30:56756 
    } 
} 

Я пытался несколько запросов до сих пор, это лучшее, что я получил до сих пор, но значения документа к югу не суммируются:

db.events.aggregate(
{ 
    $match: {app_id:'DHJFK67JDSJjdasj909'} 
}, 
{ 
    $group: { 
     _id: { 
      app_id:'$app_id', 
     }, 
     event_count: {$sum:'$event_count'}, 
     event_count_per_type: {$sum:'$event_count_per_type'} 
    } 
}, 
{ 
    $project: { 
     _id:0, 
     app_id:'$_id.app_id', 
     event_count:1, 
     event_count_per_type:1 
    } 
} 
) 

Выход я вижу это значение 0 для ключа event_count_per_type, а не объекта. Я мог бы изменить схему, чтобы ключи находились на верхнем уровне документа, но это все равно означает, что мне нужно иметь запись в выражении группы для каждого ключа, поскольку, поскольку я не знаю, какие имена клавиш я не могу делать.

Любая помощь будет оценена, я готов изменить свою схему, если это необходимо, а также попробовать MapReduce (хотя из документации кажется, что производительность плохо.)

+0

Если вы не знаете имен ключей в поддокументах, то вы не можете сделать это с помощью агрегации (хотя вы могли бы с уменьшением карты). Вы уверены, что имя ключа может быть * ничего * вообще? или это может быть только число в определенном диапазоне? Это может быть «foo»? Или это может быть только число от 0 до 59 или какое-то такое? –

+0

@ АсяКамский это будет между 0 и 100 – Irfan

ответ

7

Как указано, обработка документов как это не представляется возможным с рамками агрегации, если вы на самом деле собирается поставить все ключи, такие как:

db.events.aggregate([ 
    { "$group": { 
     "_id": "$app_id", 
     "event_count": { "$sum": "$event_count" }, 
     "0": { "$sum": "$event_count_per_type.0" }, 
     "10": { "$sum": "$event_count_per_type.10" } 
     "20": { "$sum": "$event_count_per_type.20" } 
     "30": { "$sum": "$event_count_per_type.30" } 
    }} 
]) 

Но вы, конечно, должны явно указать каждый ключ, который вы хотите работать. Это справедливо как для структуры агрегации, так и для общих операций запроса в MongoDB, поскольку для доступа к элементам, отмеченным в этой форме «поддокумента», вам нужно указать «точный путь» к элементу, чтобы что-то с ним делать.

В структуре агрегации и общих запросах нет понятия «обход», что означает, что они не могут обрабатывать «каждый ключ» документа. Для этого требуется построение языка для выполнения, которое не предусмотрено в этих интерфейсах.

Вообще говоря, использование «ключевого имени» в качестве точки данных, где это имя фактически представляет собой «значение», представляет собой немного «анти-шаблон». Лучший способ для моделирования это было бы использовать массив и представлять свой «тип» в качестве значения само по себе:

{ 
    "app_id": "DHJFK67JDSJjdasj909", 
    "date: ISODate("2014-08-07T00:00:00.000Z"), 
    "event_count": 32423, 
    "events": [ 
     { "type": 0, "value": 322 }, 
     { "type": 10, "value": 4234 }, 
     { "type": 20, "value": 653 }, 
     { "type": 30, "value": 7562 } 
    ] 
} 

отметить также, что «дата» в настоящее время соответствующий объект даты, а не строка, которая также является хорошей практикой. Такого рода данные, хотя легко обрабатывать с рамками агрегации:

db.events.aggregate([ 
    { "$unwind": "$events" }, 
    { "$group": { 
     "_id": { 
      "app_id": "$app_id", 
      "type": "$events.type" 
     }, 
     "event_count": { "$sum": "$event_count" }, 
     "value": { "$sum": "$value" } 
    }}, 
    { "$group": { 
     "_id": "$_id.app_id", 
     "event_count": { "$sum": "$event_count" }, 
     "events": { "$push": { "type": "$_id.type", "value": "$value" } } 
    }} 
]) 

Это показывает две стадии группировки в том, что первый получает итоговые данные за «тип» без указания каждого «ключ», так как вы больше не должны, то возвращается как отдельный документ на «app_id» с результатами в массиве, поскольку они были первоначально сохранены. Эта форма данных, как правило, гораздо более гибкая для просмотра определенных «типов» или даже «значений» в определенном диапазоне.

Если вы не можете изменить структуру, тогда ваш единственный вариант - mapReduce.Это позволяет «код» обход ключей, но так как это требует интерпретации JavaScript и исполнения это не так быстро, как структура агрегации:

db.events.mapReduce(
    function() { 
     emit(
      this.app_id, 
      { 
       "event_count": this.event_count, 
       "event_count_per_type": this.event_count_per_type 
      } 
     ); 
    }, 
    function(key,values) { 

     var reduced = { "event_count": 0, "event_count_per_type": {} }; 

     values.forEach(function(value) { 
      for (var k in value.event_count_per_type) { 
       if (!redcuced.event_count_per_type.hasOwnProperty(k)) 
        reduced.event_count_per_type[k] = 0; 
       reduced.event_count_per_type += value.event_count_per_type; 
      } 
      reduced.event_count += value.event_count; 
     }) 
    }, 
    { 
     "out": { "inline": 1 } 
    } 
) 

Это существенно траверс и объединить «ключи» и сумму значения для каждого найденного.

Так вы варианты либо:

  1. Изменение структуры и работы со стандартными запросами и агрегации.
  2. Оставайтесь со структурой и требует обработки JavaScript и mapReduce.

Это зависит от ваших реальных потребностей, но в большинстве случаев реструктуризация дает преимущества.

+0

Спасибо, это блестяще! Я все еще нахожусь на ранних этапах этого приложения, поэтому я продолжу менять схему. У меня на самом деле есть еще один массив, который я хочу объединить в одно и то же время с именем unqiue_event_count_per_type, но второй $ unwind заставляет $ sum удваивать счет записей, когда я включаю его в первую $ group _id. Должен ли я сделать это как второй запрос и объединить два результата в коде? – Irfan

+0

@Irfan Это звучит как еще один вопрос и лучше выражается путем публикации нового вопроса со всеми подробностями. Не забудьте принять ответы, которые помогут вам, так как многие из нас видят, есть ли ответы в вашей истории. Слияние двух массивов возможно, но весь вопрос заслуживает другого полного ответа. –

+0

Спасибо, я создал новый вопрос по адресу http://stackoverflow.com/questions/25201157/mongo-sum-compounded-when-doing-unwind-and-then-group-on-multiple-fields – Irfan

Смежные вопросы