Выберите последний документ после группировки его по полю в MongoDB - PullRequest
2 голосов
/ 18 октября 2019

У меня есть вопрос, который я ожидал бы быть довольно простым, но я не могу понять это. Я хочу сделать следующее:

  • Найти все документы в коллекции и:
    • отсортировать документы по определенному полю даты
    • применить distinct водно из его других полей, , но возвращает весь документ

Лучше всего показано в примере.

Это вводный ввод:

[
  {
    "commandName" : "migration_a",
    "executionDate" : ISODate("1998-11-04T18:46:14.000Z")
  },
  {
    "commandName" : "migration_a",
    "executionDate" : ISODate("1970-05-09T20:16:37.000Z")
  },
  {
    "commandName" : "migration_a",
    "executionDate" : ISODate("2005-11-08T11:58:52.000Z")
  },
  {
    "commandName" : "migration_b",
    "executionDate" : ISODate("2016-06-02T19:48:34.000Z")
  }
]

Ожидаемый вывод:

[
  {
    "commandName" : "migration_a",
    "executionDate" : ISODate("2005-11-08T11:58:52.000Z")
  },
  {
    "commandName" : "migration_b",
    "executionDate" : ISODate("2016-06-02T19:48:34.000Z")
  }
]

Или, другими словами:

  • Сгруппируйте входные данные по полю commandName
  • Внутри каждой группы Сортируйте документы
  • Верните самый новый документ из каждой группы

Мои попытки написать этот запрос потерпели неудачу:

  • Функция distinct() будет возвращать только значение поля, по которому я различаюсь,не весь документ. Это делает его непригодным для моего случая.

  • Пробовал писать запрос aggregate, но столкнулся с проблемой, как отсортировать и выбрать один документ внутри каждой группы? Стадия агрегации sort будет сортировать groups между собой, а это не то, чего я хочу.

Я не слишком хорошо разбираюсь в Монго, и именно здесь я бьюстена. Любые идеи о том, как продолжить?


Для справки: это запрос агрегации в процессе выполнения, который я пытаюсь расширить:

db.getCollection('some_collection').aggregate([
{ $group: { '_id': '$commandName', 'docs': {$addToSet: '$$ROOT'} } }, 
{ $sort: {'_id.docs.???': 1}}
])

Пост-разрешенное редактирование

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

db.getCollection('some_collection').aggregate([
{ $sort: {'executionDate': 1}},
{ $group: { '_id': '$commandName', 'result': { $last: '$$ROOT'} } },
{ $replaceRoot: {newRoot: '$result'} }
])

Результат запроса без Этап $replaceRoot будет выглядеть следующим образом:

[
  {
    "_id": "migration_a",
    "result": {
      "commandName" : "migration_a",
      "executionDate" : ISODate("2005-11-08T11:58:52.000Z")
    }
  },
  {
    "_id": "migration_b",
    "result": {
      "commandName" : "migration_b",
      "executionDate" : ISODate("2016-06-02T19:48:34.000Z")
    }
  }
]

Внешние _id и _result - это просто "групповые обертки" вокруг фактического документа, который я хочу, который вложен в result ключПеремещение вложенного документа в корень результата выполняется с использованием этапа $replaceRoot. Результат запроса при использовании этого этапа:

[
  {
    "commandName" : "migration_a",
    "executionDate" : ISODate("2005-11-08T11:58:52.000Z")
  },
  {
    "commandName" : "migration_b",
    "executionDate" : ISODate("2016-06-02T19:48:34.000Z")
  }
]

Ответы [ 3 ]

1 голос
/ 18 октября 2019

Я считаю, что это приведет к тому, что вы ищете:

db.collection.aggregate([
  {
    $group: {
      "_id": "$commandName",
      "executionDate": {
        "$last": "$executionDate"
      }
    }
  }
])

Вы можете проверить это здесь

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

{
 $sort: {
  "executionDate": 1
 }
}

Вы можете проверить эту версию здесь.

1 голос
/ 18 октября 2019

Вариант использования, представленный в вопросе, почти описан в $last агрегации документации оператора.

Какойрезюмирует:

этап $group должен следовать за этапом $sort, чтобы входные документы располагались в определенном порядке. Поскольку $last просто выбирает последний документ из группы.

Запрос: Ссылка

db.collection.aggregate([
  {
    $sort: {
      executionDate: 1
    }
  },
  {
    $group: {
      _id: "$commandName",
      executionDate: {
        $last: "$executionDate"
      }
    }
  }
]);
1 голос
/ 18 октября 2019

Попробуйте это:

db.getCollection('some_collection').aggregate([
 { $sort: {'executionDate': -1}},
 { $group: { '_id': '$commandName', 'doc': {$first: '$$ROOT'} } }
])
...