MongoDB группировать по идентификатору, а затем по дате - PullRequest
0 голосов
/ 11 января 2019

У меня есть коллекция в моей базе данных MongoDB, в которой хранятся длительности для людей, которые находятся в группах, это выглядит примерно так:

[{
    "_id": "5c378eecd11e570240a9b0ac",
    "state": "DRAFT",
    "groupId": "5c378eebd11e570240a9ae49",
    "personId": "5c378eebd11e570240a9aee1",
    "date": "2019-01-07T00:00:00.000Z",
    "duration": 480,
    "__v": 0
},
{
    "_id": "5c378eecd11e570240a9b0bb",
    "state": "DRAFT",
    "groupId": "5c378eebd11e570240a9ae58",
    "personId": "5c378eebd11e570240a9aeac",
    "date": "2019-01-07T00:00:00.000Z",
    "duration": 480,
    "__v": 0
},
{
    "_id": "5c378eecd11e570240a9b0c5",
    "state": "DRAFT",
    "groupId": "5c378eebd11e570240a9ae3e",
    "personId": "5c378eebd11e570240a9aef6",
    "date": "2019-01-07T00:00:00.000Z",
    "duration": 480,
    "__v": 0
}]

Я хотел бы иметь возможность выполнить агрегатный запрос, который возвращает коллекцию personIds и duration, сгруппированных в день с соответствующим groupId, который будет выглядеть следующим образом:

[{
    "personId": "5c378eebd11e570240a9aee1",
    "time": [{
        "date": "2019-01-07T00:00:00.000Z",
        "entries": [{
            "groupId": "5c378eebd11e570240a9ae49",
            "duration": 480,
            "state": "DRAFT"
        }]
    }]
}, {
    "personId": "5c378eebd11e570240a9aeac",
    "time": [{
        "date": "2019-01-07T00:00:00.000Z",
        "entries": [{
            "groupId": "5c378eebd11e570240a9ae58",
            "duration": 480,
            "state": "DRAFT"
        }]
    }]
}, {
    "personId": "5c378eebd11e570240a9aef6",
    "time": [{
        "date": "2019-01-07T00:00:00.000Z",
        "entries": [{
            "groupId": "5c378eebd11e570240a9ae3e",
            "duration": 480,
            "state": "DRAFT"
        }]
    }]
}]

Пока что я написал следующую агрегацию (я использую Mongoose, отсюда и синтаксис):

Time.aggregate()
    .match({ date: { $gte: new Date(start), $lte: new Date(end) } })
    .group({
      _id: '$personId',
      time: { $push: { date: '$date', duration: '$duration', state: '$state' } },
    })
    .project({ _id: false, personId: '$_id', time: '$time' })

, который возвращает следующее:

[{
    "personId": "5c378eebd11e570240a9aed1",
    "time": [{
        "date": "2019-01-11T00:00:00.000Z",
        "duration": 480,
        "state": "DRAFT"
    }, {
        "date": "2019-01-11T00:00:00.000Z",
        "duration": 480,
        "state": "DRAFT"
    }
    // ...
}]

Надеюсь, вы видите, что duration сгруппированы по personId, но я не смог выяснить, как применить другую группировку к массиву time, так как date дублируются, если personId имеет более одного duration на указанную дату.

Можно ли сгруппировать по идентификатору, идентификатору, передать в массив, а затем сгруппировать значения в этом массиве в виде агрегации, или вместо этого моему приложению потребуется отобразить / уменьшить результаты?

Ответы [ 2 ]

0 голосов
/ 11 января 2019

Я бы предложил выполнить две $group операции подряд:

db.time.aggregate({
  // first, group all entries by personId and date
  $group: {
    _id: {
      personId: "$personId",
      date: "$date"
    },
    entries: {
      $push: {
        groupId: "$groupId",
        duration: "$duration",
        state: "$state"
      }
    }
  }
}, {
  // then, group previously aggregated entries by personId
  $group: {
    _id: "$_id.personId",
    time: {
      $push: {
        date: "$_id.date",
        entries: "$entries"
      }
    }
  }
}, {
  // finally, rename _id to personId
  $project: {
    _id: 0,
    personId: "$_id",
    time: "$time"
  }
})

В Mongoose это должно быть примерно так:

Time.aggregate()
  .match({
    date: {
      $gte: new Date(start),
      $lte: new Date(end)
    }
  })
  .group({
    _id: {
      personId: '$personId',
      date: '$date'
    },
    entries: {
      $push: {
        groupId: '$groupId',
        duration: '$duration',
        state: '$state'
      }
    }
  })
  .group({
    _id: '$_id.personId',
    time: {
      $push: {
        date: '$_id.date',
        entries: '$entries'
      }
    }
  })
  .project({
    _id: false,
    personId: '$_id',
    time: '$time'
  })
0 голосов
/ 11 января 2019
db.getCollection("dummyCollection").aggregate(
[
    { 
        "$group" : {
            "_id" : "$personId", 
            "time" : {
                "$push" : {
                    "date" : "$date", 
                    "duration" : "$duration", 
                    "state" : "$state"
                }
            }
        }
    }, 
    { 
        "$project" : {
            "_id" : false, 
            "personId" : "$_id", 
            "time" : "$time"
        }
    }, 
    { 
        "$unwind" : "$time"
    }, 
    { 
        "$group" : {
            "_id" : "$time.date", 
            "time" : {
                "$addToSet" : "$time"
            }
        }
    }
]

);

Используйте $ addToSet, который возвращает массив всех уникальных значений, полученных в результате применения выражения к каждому документу в группе документов, которые совместно используют одну и ту же группу по ключу.

...