【问题标题】:MongoDB - Get latest non-null field value from documents with timestampMongoDB - 从带有时间戳的文档中获取最新的非空字段值
【发布时间】:2018-09-05 10:58:34
【问题描述】:

在我查询的 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"
}

这是错误的,因为它只使用最新文档中的值,而不是混合所有文档。

我尝试了 Rishi 在另一个主题中提出的另一种方法。他建议不要为每个修订创建一个新文档,而是将修订子文档推送到一个数组中,并在父文档中维护最新修订。

类似这样的:

{
    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"
        }
    ]
}

但是,维护最新版本并不那么容易,因为更新不是按时间顺序接收的,然后我可以收到一个“新”文档,它具有较旧的“日期时间”字段值,然后我不能更新这些字段,除非他们是空的。然后,如果我愿意,我必须记录所有字段的最后更新时间戳!

【问题讨论】:

  • "如果我想这样做,我必须记录所有字段的最后更新时间戳!" - 所以?每个时间戳为 32/64 位。我还要补充一点,您最好混合使用而不是一个文档——一个集合用于合并文档,另一个集合用于所有文档。
  • @AgostonHorvath:这只是为了收集可读性的问题,而不是磁盘空间的问题。

标签: mongodb


【解决方案1】:

你可以试试这个:

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]} 
    }}
])

【讨论】:

  • 是否可以去掉非空过滤?我们调整了我们的集合以避免插入空字段,然后我尝试简化查询。我试图删除过滤并将 {$arrayElemAt: ["$field1notNull", 0]} 替换为 {$arrayElemAt: ["$field1", 0]} 但它不起作用......(返回 NULL 值字段)。
  • 对不起,这条评论是给@Juan的,我忘了指明。
  • 知道了!我只需要完全删除第一个投影,而不仅仅是一些字段......
  • 好吧,第一个投影只用于删除空字段,所以应该没问题。祝你有美好的一天!
【解决方案2】:

您可以尝试使用$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 阶段结束时。 希望对你有帮助

【讨论】:

    猜你喜欢
    • 2021-08-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-20
    • 2018-10-24
    • 2021-06-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多