Группировка и суммирование встроенного массива по месяцам

У меня такая схема:

_id 
dates : 
    date :
       year
       month
       day
other unrelated fields

Я уже сгруппировал каждый _id таким образом, чтобы даты содержали несколько объектов даты (которые содержат год, месяц, день). Теперь я хочу сгруппировать каждый объект даты по году и месяцу, чтобы получить количество дат, соответствующих году и месяцу. Например, если у меня есть следующий документ:

_id : 124567789554
dates : 
    date : 
        year : 2018
        month : 9
        day : 1
    date : 
        year : 2018
        month : 9
        day : 2
    date : 
        year : 2018
        month : 9
        day : 3
    date : 
        year : 2018
        month : 10
        day : 1

Результат, который я хочу:

_id : 124567789554
dates : 
    date : 
        year : 2018
        month : 9
        count : 3
    date : 
        year : 2018
        month : 10
        count : 1

Как я могу это сделать?

Обновлено: для некоторого дополнительного контекста мне сначала нужно сгруппировать по personId. Изначально схема выглядит так:

_Id (automatically generated by mongoDB)
personId 
date

Есть несколько строк с одним и тем же personId, соответствующим дате. Мне нужно сначала сгруппировать так, чтобы _Id = personId, и объединить даты вместе. Как я могу делать и то, и другое одновременно? Мой текущий запрос:

{
  _id: "$personId",
  dates: {
    $addToSet: "$date"
  },
  other unrelated fields
}
Использование JavaScript и MongoDB
Использование JavaScript и MongoDB
Сегодня я собираюсь вкратце рассказать о прототипах в JavaScript, а также представить и объяснить вам работу с базой данных MongoDB.
1
0
970
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Если вы сохраните свое поле в формате BSON Дата, это легко сделать с помощью групповая агрегация.

Очень похожий пример кода из Mongo Document:

db.sales.aggregate(
   [
      {
        $group : {
           _id : { month: { $month: "$date" }, day: { $dayOfMonth: "$date" }, year: { $year: "$date" } },
           totalPrice: { $sum: { $multiply: [ "$price", "$quantity" ] } },
           averageQuantity: { $avg: "$quantity" },
           count: { $sum: 1 }
        }
      }
   ]
)

Результат:

{ "_id" : { "month" : 3, "day" : 15, "year" : 2014 }, "totalPrice" : 50, "averageQuantity" : 10, "count" : 1 }
{ "_id" : { "month" : 4, "day" : 4, "year" : 2014 }, "totalPrice" : 200, "averageQuantity" : 15, "count" : 2 }
{ "_id" : { "month" : 3, "day" : 1, "year" : 2014 }, "totalPrice" : 40, "averageQuantity" : 1.5, "count" : 2 }

Я думаю, что мой немного отличается, потому что сначала мне нужно сгруппировать по personId. Начальная схема выглядит так: _id, personId, date, ... Поскольку одному человеку соответствует несколько дат, существует несколько строк personId с соответствующей датой.

John Kim 09.09.2018 23:33
Ответ принят как подходящий

Используйте $group для группировки по идентификатору человека, месяцу и году и подсчитайте количество совпадений, за которым следует $group, чтобы собрать все даты с годом и месяцем и подсчитать для каждого идентификатора человека.

db.colname.aggregate([
  {"$group":{
    "_id":{"personId":"$personId","year":"$date.year","month":"$date.month"},
    "count":{"$sum":1}
  }},
  {"$group":{
    "_id":"$_id.personId",
    "dates":{"$push":{"year":"$_id.year","month":"$_id.month","count":"$count"}}
  }}
])

Это решение работает, спасибо! Как я могу получить доступ к объекту внутри массива дат? Я пытаюсь сопоставить счетчик $, чтобы создать новое логическое поле, которое возвращает истину, если любое из счетчиков за любой месяц больше 5.

John Kim 10.09.2018 01:08

Yw. Добавьте "countgt5":{"$cond":[{"$gt":["$count",5]},true,false]} после поля count в $ push.

s7vr 10.09.2018 01:19

Это добавляет поле истина / ложь к КАЖДОМУ счетчику года / месяца, но я хочу, чтобы это поле возвращало истину / ложь, если ЛЮБОЙ год / месяц имеют счет больше 5 (так что только одно из этого поля для каждого документа, вместо одного поля для каждого года / месяца)

John Kim 10.09.2018 01:21

ах. Пропустил эту часть. Добавьте следующий этап после $ group stage. {"$addFields":{"countgt5":{"$gt":[{"$size":{"$filter":{"inpu‌​t":"$dates","cond":{‌​"$gt":["$$this.count‌​",5]}}}},0]}}}

s7vr 10.09.2018 01:26

Ты бог. Большое спасибо! Я совершенно новичок в запросах NoSQL и определенно многому здесь научился.

John Kim 10.09.2018 01:30

Другие вопросы по теме