Mongodb - группировать документы и получать документы из каждой группы с максимальным значением поля - PullRequest
0 голосов
/ 23 октября 2019

У меня есть коллекция Mongo, где документы никогда не обновляются, скорее читается самый последний документ, и вставляется новый документ с некоторыми полями, обновленными в нем. Эти общие документы имеют идентификатор document_identifier, который отличает их от других.

Я хочу выполнить запрос, который выполняет следующее: получить все документы, у которых customer_id - X, сгруппировать их по document_identifier,и из каждой группы возьмите документ с максимальной отметкой времени updated_at. Он должен вернуть документы целиком (все их свойства).

Пример набора данных:

{
    document_identifier: "abc",
    updated_at: 1000,
    customer_id: "123",
    ...  
},

{
    document_identifier: "def",
    updated_at: 1001,
    customer_id: "123",
    ...
},

{
    document_identifier: "abc",
    updated_at: 1002,
    customer_id: "123",
    ...
},

{
    document_identifier: "def",
    updated_at: 10003,
    customer_id: "123",
    ...
},

{
    document_identifier: "xyz",
    updated_at: 1004,
    customer_id: "999",
    ...
},

{
    document_identifier: "abc",
    updated_at: 1005,
    customer_id: "123",
    ...
},

{
    document_identifier: "def",
    updated_at: 1006,
    customer_id: "123",
    ...
},

В приведенном выше примере, если я хотел запросить по customer_id из "123«результат будет такой:

{
    document_identifier: "abc",
    updated_at: 1005,
    customer_id: "123",
    ...
},

{
    document_identifier: "def",
    updated_at: 1006,
    customer_id: "123",
    ...
},

Я был направлен в сторону агрегатной структуры Mongo, но, похоже, не могу ее получить.

Любая помощь очень ценится.

РЕДАКТИРОВАТЬ: Это то, что у меня есть сейчас, и, кажется, работает, но я не уверен, что это наиболее оптимальный:

db.my_colleciton.aggregate([
    {
       $match: {customer_id: <value to query on>}
    },

    {
        $sort: {updated_at: -1}
    },

    {
        $group: {
            _id: "$document_identifier",
            my_doc: {$first: "$$ROOT"}
        }
    },

    {
        "$replaceRoot": {newRoot: "$my_doc"}
    }
])

Ответы [ 2 ]

0 голосов
/ 23 октября 2019

Первая сортировка по убыванию по updated_at, затем $group по document_identifier и выбор первого документа для этой конкретной группы с помощью $first

И сохранить поля с помощью одной и той же идеи $first.

Запрос: Демо-ссылка

db.collection.aggregate([
    { $sort: { updated_at: -1 } },
    {
      $group: {
        _id: "$document_identifier",
        document_identifier: { $first: "$document_identifier" },
        updated_at: { $first: "$updated_at" },
        customer_id: { $first: "$customer_id" }
      }
    }
  ]).pretty();

Результаты:

{
    "_id" : "abc",
    "document_identifier" : "abc",
    "updated_at" : 1005,
    "customer_id" : "123"
},
{
    "_id" : "xyz",
    "document_identifier" : "xyz",
    "updated_at" : 1004,
    "customer_id" : "999"
},
{
    "_id" : "def",
    "document_identifier" : "def",
    "updated_at" : 10003,
    "customer_id" : "123"
}
0 голосов
/ 23 октября 2019

Так что, если я правильно понимаю, я думаю, что этот запрос может помочь ...

db.records.aggregate(
[
    { $group: {
        _id: {customer_id: "$customer_id", document_identifier: "$document_identifier"},
        max_updated_at: { $max:  "$updated_at" }
    }}
])

идея состоит в том, чтобы сгруппировать по двум полям, customer_id и document_identifier. Для этого комбо выведите максимум updated_at, который, как ожидается, будет скользящим целым числом.

Для набора данных, который вы предоставили, мои результаты показывают ...

{ "_id" : { "customer_id" : "123", "document_identifier" : "def" }, "max_updated_at" : 10003 }
{ "_id" : { "customer_id" : "999", "document_identifier" : "xyz" }, "max_updated_at" : 1004 }
{ "_id" : { "customer_id" : "123", "document_identifier" : "abc" }, "max_updated_at" : 1005 }

Формат вывода отличаетсяиз вашего примера. Это нормально, или вам требуется, чтобы формат выходных данных соответствовал вашим примерам?

Редактировать: Таким образом, ОП запрашивает формат выходных данных, соответствующий ожидаемому формату, описанному в вопросе. Без лишних слов ...

db.records.aggregate(
[
    { $group: {
        _id: {customer_id: "$customer_id", document_identifier: "$document_identifier"},
        max_updated_at: { $max:  "$updated_at" }
    }},
    { $project: {
        _id: 0,
        document_identifier: "$_id.document_identifier",
        updated_at: "$max_updated_at",
        customer_id: "$_id.customer_id"

    }}
]
)

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

{ "document_identifier" : "def", "updated_at" : 10003, "customer_id" : "123" }
{ "document_identifier" : "xyz", "updated_at" : 1004, "customer_id" : "999" }
{ "document_identifier" : "abc", "updated_at" : 1005, "customer_id" : "123" }

Редактировать номер 2:

ОК, так чтоOP имеет гораздо больше полей, чем представлено в вопросе, и хотел бы видеть все поля для соответствующих документов. Вот запрос на данный момент ...

db.records.aggregate(
[
    { $match: { customer_id: "123" }},
    { $group: {
        _id: {customer_id: "$customer_id", document_identifier: "$document_identifier"},
        max_updated_at: { $max:  "$updated_at" }
    }},
    { $lookup: {
        from: "records",
        let: {
          customer_id: "$_id.customer_id",
          document_identifier: "$_id.document_identifier",
          max_updated_at: "$max_updated_at"
        },
        pipeline: [
          {
            $match: {
              $expr: {
                $and: [
                  { $eq: [ "$customer_id", "$$customer_id"] },
                  { $eq: [ "$document_identifier", "$$document_identifier"] },
                  { $eq: [ "$updated_at", "$$max_updated_at"] }
                ]
              }
            }
          }
        ],
        as: "result"
    }},
    { $unwind: "$result" } ,
    { $replaceRoot: { newRoot: "$result" } }
]
)

Теперь это сначала совпадает с идентификатором клиента. Затем он выполняет самостоятельное соединение, используя $ lookup, а затем $ replaceRoot, чтобы показать только исходные документы. При этом сохраняется исходный формат документа независимо от количества полей.

Вывод:

{ "_id" : ObjectId("5db07a5d3cf0c979dd020f85"), "document_identifier" : "def", "updated_at" : 10003, "customer_id" : "123" }
{ "_id" : ObjectId("5db07a5d3cf0c979dd020f87"), "document_identifier" : "abc", "updated_at" : 1005, "customer_id" : "123" }
...