MongoDB - Получить последнее ненулевое значение поля из документов с отметкой времени - PullRequest
0 голосов
/ 05 сентября 2018

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

Для иллюстрации рассмотрим этот пример. Мы получили 3 набора данных для посылки:

/* 1 */
{
    "parcelNum" : "CC123456789FR",
    "datetime" : ISODate("2018-09-05T10:48:38.584Z"),
    "field1" : "value1_1",
    "field2" : "value2_1"
}

/* 2 */
{
    "parcelNum" : "CC123456789FR",
    "datetime" : ISODate("2018-09-05T10:48:40.566Z"),
    "field1" : "value1_2",
    "field2" : null
}

/* 3 */
{
    "parcelNum" : "CC123456789FR",
    "datetime" : ISODate("2018-09-05T10:48:42.777Z"),
    "field1" : null,
    "field2" : "value2_2"
}

Как извлечь последнее ненулевое значение для всех полей с учетом отметки времени документа, которому они принадлежат?

Используя предыдущий пример, вот что я пытаюсь получить:

{
    "parcelNum" : "CC123456789FR",
    "field1" : "value1_2",
    "field2" : "value2_2"
}

Я пытался выполнить такой запрос, но не могу найти способ смешать значения полей из нескольких документов:

db.testDB.aggregate([
    {$sort: { datetime: -1 }},
    {$group: { _id: "$parcelNum", 
        field1: {$first: "$field1" },
        field2: {$first: "$field2" }
    }}
])

дает мне:

{
    "_id" : "CC123456789FR",
    "field1" : null,
    "field2" : "value2_2"
}

, что неверно, поскольку он использует только значения из самого последнего документа и не смешивает все документы.

Я попробовал другой подход, предложенный Риши в другой теме. Вместо создания нового документа для каждой ревизии он предложил поместить вложенные документы ревизии в массив и сохранить последнюю ревизию в родительском документе.

Примерно так:

{
    parcelNum: CC123456789FR,
    lastUpdated: ISODate("2018-09-05T10:48:42.777Z")
    field1: "value1_2",
    field2: "value2_2",
    revisions: [
        {
            datetime: ISODate("2018-09-05T10:48:38.584Z"),
            field1: "value1_1",
            field2: "value2_1"
        },
        {
            datetime: ISODate("2018-09-05T10:48:40.566Z"),
            field1: "value1_2",
            field2: null
        },
        {
            datetime: ISODate("2018-09-05T10:48:42.777Z"),
            field1: null,
            field2: "value2_2"
        }
    ]
}

Однако поддержка последней ревизии не так проста, потому что обновления не принимаются в хронологическом порядке, тогда я могу получить «новый» документ, который имеет более старое значение поля «datetime», и тогда я не должен обновлять поля, кроме случаев, если они нулевые. Затем мне нужно будет записать отметку времени последнего обновления для всех полей, если я хочу это сделать!

Ответы [ 2 ]

0 голосов
/ 05 сентября 2018

Вы можете попробовать это:

db.getCollection('test').aggregate([
    //Sort
    {$sort: { datetime: -1 }},
    //Add fields to an array
    {$group: {
        "_id": null,
        "field1": { $push: "$field1" },
        "field2": { $push: "$field2" },
    }},

    //Filter and do not include null values
    {$project: {
        "field1notNull" : {
              $filter: {
               input: "$field1",
               as: "f",
               cond: { $ne: [ "$$f", null ] }
            }
          },
        "field2notNull" : {
              $filter: {
               input: "$field2",
               as: "f",
               cond: { $ne: [ "$$f", null ] }
            }
          }
        }
    },
    //Get the first values of each
    {$project: {
        "_id": null,
        "field1": {$arrayElemAt: ["$field1notNull", 0]},
        "field2": {$arrayElemAt: ["$field2notNull", 0]} 
    }}
])
0 голосов
/ 05 сентября 2018

Вы можете попробовать с $ facet stage, чтобы угроза field1 и field2 раздельно:

db['01'].aggregate(
    [
        // Stage 1
        {
            $sort: {
            "datetime":-1
            }
        },

        // Stage 2
        {
            $facet: {parcelNum:[{$group:{_id:"$parcelNum"}}],
                field1: [ {
                        $match: {
                        field1:{$ne:null}
                        }
                    },      
                    {
                        $limit: 1
                    },
                    {
                        $project: {
                         _id:0,
                         field1:1
                         }
                    }, ],

                field2: [ {
                        $match: {
                        field2:{$ne:null}
                        }
                    },      
                    {
                        $limit: 1
                    },
                    {
                        $project: {
                         _id:0,
                         field2:1
                         }
                    }, ],
            }
        },

        // Stage 3
        {
            $project: {
                parcelNum:"$parcelNum._id"  ,
                field1:"$field1.field1",
                field2:"$field2.field2",
            }
        },

        // Stage 4
        {
            $project: {
                parcelNum:{$arrayElemAt:["$parcelNum"  ,0]},
                field1:{$arrayElemAt:["$field1"  ,0]},
                field2:{$arrayElemAt:["$field2"  ,0]},
            }
        },
    ],
);

Обратите внимание, что этапы 3 и 4 являются только «декоративными», необходимый результат присутствует в конце этапа 2. Надеюсь, это поможет

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...