MongoDB — Получить последнюю дату каждого отдельного имени

#mongodb #mongodb-query

Вопрос:

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

У меня есть некоторые данные в моей коллекции (студенты):

 { "_id" : ObjectId("61479d4bc146b1663a8f2b7d"), "city" : "SAO PAULO", "name" : "ANA", "status" : "ACTIVE", "date1" : ISODate("2020-09-01T08:14:30.000Z") }
{ "_id" : ObjectId("61479d88c146b1663a8f2b7e"), "city" : "SAO PAULO", "name" : "MARIA", "status" : "ACTIVE", "date1" : ISODate("2020-08-01T04:16:00.000Z") }
{ "_id" : ObjectId("61479dc2c146b1663a8f2b7f"), "city" : "RIO DE JANEIRO", "name" : "MARIA", "status" : "ACTIVE", "date1" : ISODate("2021-02-01T11:10:00.000Z") }
{ "_id" : ObjectId("61479df1c146b1663a8f2b80"), "city" : "SAO PAULO", "name" : "MARIA", "status" : "INACTIVE", "date1" : ISODate("2021-02-01T11:15:00.000Z") }
{ "_id" : ObjectId("61479e60c146b1663a8f2b81"), "city" : "BRASILIA", "name" : "JOHH", "status" : "ACTIVE", "date1" : ISODate("2021-06-01T01:18:00.000Z") }
 

Я создаю запрос для фильтрации статуса «АКТИВНЫЙ» и показываю только самые последние данные для каждого студента, показывая только «город», «имя», «дату», и я пытаюсь сделать это, используя $MAX или $LAST в ГРУППЕ:

 db.getCollection('students').aggregate([
   { $match: { status: "ACTIVE" } },
   { $group: { _id: { name : "$name"},
         date1 : { $max : "$date1" } ,
         city : { $max : "$city" } } }
])
 

Желаемый результат:

 { "city" : "SAO PAULO", "name" : "ANA", "date1" : ISODate("2020-09-01T08:14:30.000Z") }
{ "city" : "RIO DE JANEIRO", "name" : "MARIA", "date1" : ISODate("2021-02-01T11:10:00.000Z") }
{ "city" : "BRASILIA", "name" : "JOHH", "date1" : ISODate("2021-06-01T01:18:00.000Z") }
 

Но в результате получается вот что:

 { "city" : "SAO PAULO", "name" : "ANA", "date1" : ISODate("2020-09-01T08:14:30.000Z") }
{ "city" : "SAO PAULO", "name" : "MARIA", "date1" : ISODate("2021-02-01T11:10:00.000Z") }
{ "city" : "BRASILIA", "name" : "JOHH", "date1" : ISODate("2021-06-01T01:18:00.000Z") }
 

Он извлекает неправильные данные. Для АНЫ и ДЖОНА (только по одному документу на каждого) все в порядке. Но у МАРИИ есть три документа, и мне нужно извлечь все данные из ее документа с датой $max, и я извлекаю «город» : «САН-ПАУЛУ», а не «город» : «РИО-ДЕ-ЖАНЕЙРО», потому что оператор $MAX применяется и для этого поля. Это применяется ко всем полям, и оператор GROUP не позволяет удалить оператор MAX.

Я не знаю, как это исправить. Как получить весь документ, фильтруя по «последней дате каждого отдельного имени» ?

Ответ №1:

Вы можете использовать этот конвейер агрегации:

  • Сначала $match , как и у вас.
  • Затем $sort , чтобы получить желаемые значения в первой позиции. Это используется на следующем этапе.
  • При $group агрегировании вы получаете $first значение (по мере сортировки документа первое значение будет желаемым).
  • И последнее $project , чтобы получить желаемый результат.
 db.collection.aggregate([
  {
    "$match": {
      "status": "ACTIVE"
    }
  },
  {
    "$sort": {
      "date1": -1
    }
  },
  {
    "$group": {
      "_id": {
        "name": "$name"
      },
      "date1": {
        "$first": "$date1"
      },
      "city": {
        "$first": "$city"
      }
    }
  },
  {
    "$project": {
      "_id": 0,
      "name": "$_id.name",
      "city": 1,
      "date1": 1
    }
  }
])
 

Пример здесь