MongoDB检查文档中第一个缺少或具有丢失值的日期

  • 本文关键字:日期 文档 第一个 MongoDB mongodb
  • 更新时间 :
  • 英文 :


我有两个字段的文档:datekey,其中date表示从2019-01-01开始的今年的"YYYY-MM-DD"格式的天数。
可能某个日期没有文档或者该日期的文档在key字段中没有值(=null)
是否可以创建一个查询来查找今年中不存在文档或其key字段没有值的第一个日期
第一个例子:

[
{date: "2019-01-01", key: "value"},
{date: "2019-01-02", key: "value"},
{date: "2019-01-04", key: "value"}
]

应返回"2019-01-03",因为文档中缺少此日期
第二个例子:

[
{date: "2019-01-01", key: "value"},
{date: "2019-01-02", key: "value"},
{date: "2019-01-03", key: null},
{date: "2019-01-04", key: "value"}
]

也应返回"2019-01-03",因为此日期没有key字段的值。

当对date字段使用某种排序或最小聚合时,第一个示例中的日期将不正确。我只找到了一种在几天内循环的方法,并检查是否有用于此date的文档,并且该文档的key字段具有非空值。然而,这个解决方案是每天的一个查询。

以下查询可以获得预期的输出:

db.collection.aggregate([
{
$sort:{
"date":-1
}
},
{
$group:{
"_id":null,
"dates":{
$push:"$$ROOT"
}
}
},
{
$project:{
"info":{
$reduce:{
"input":"$dates",
"initialValue":{
"previousDate":"9999-01-01",
"missing":"",
"with_null_key":""
},
"in":{
"missing":{
$cond:[
{
$gt:[
{
$subtract:[
{
$toDate:"$$value.previousDate"
},
{
$toDate:"$$this.date"
}
]
},
86400000
]
},
{
$toString:{
$toDate:{
$add:[
{
$toLong:{
$toDate:"$$this.date"
}
},
86400000
]
}
}
},
"$$value.missing"
]
},
"with_null_key":{
$cond:[
{
$eq:["$$this.key",null]
},
"$$this.date",
"$$value.with_null_key"
]
},
"previousDate":"$$this.date"
}
}
}
}
},
{
$project:{
"_id":0,
"first_missing":"$info.missing",
"first_with_null_key":"$info.with_null_key"
}
}
]).pretty()

数据集:

{
"_id" : ObjectId("5d7bb4d6226855e3ea97001b"),
"date" : "2019-01-01",
"key" : "value"
}
{
"_id" : ObjectId("5d7bb4d6226855e3ea97001c"),
"date" : "2019-01-02",
"key" : "value"
}
{
"_id" : ObjectId("5d7bb4d6226855e3ea97001d"),
"date" : "2019-01-04",
"key" : "value"
}
{
"_id" : ObjectId("5d7bc2a7226855e3ea97001e"),
"date" : "2019-01-05",
"key" : null
}

输出:

{
"first_missing" : "2019-01-03T00:00:00.000Z",
"first_with_null_key" : "2019-01-05"
}

查询分析:

  • 按日期降序搜索文档
  • 所有文档都被推送到一个名为dates的数组中
  • $reduce在dates阵列上执行
    • 如果上一个日期和当前日期的差值大于86400000(相当于1天的毫秒),则缺少的日期为当前日期+1天
    • 如果缺少日期键,则日期保存在字段with_null_key

您可以使用$range、$dateFromParts和$dateToString运算符生成一年的所有日期,然后$lookup the collection for the missing documents:

db.collection.aggregate(
[
{ $limit: 1 },
{ $project: {
_id: 0,
day: { $map: {
input: { $filter: { 
input: { $map: {
input: { $range: [ 1, 367, 1 ] },
as: 'day',
in: { $dateFromParts : { 'year': 2019, 'month': 1, 'day': '$$day','hour': 0, 'minute': 0, 'second': 0,'millisecond': 0 } }
} }, 
as: 'day', 
cond: { $lt: ['$$day', { $dateFromParts : { 'year': 2020, 'month': 1, 'day': 1,'hour': 0, 'minute': 0, 'second': 0,'millisecond': 0 } }] }
} },
as: 'day',
in: { $dateToString: { date: '$$day', format: '%Y-%m-%d' } }
} }
} },
{ $unwind: '$day' },
{ $lookup: {
from: 'collection',
let: { d: '$day' },
pipeline: [ 
{ $match: { 
$expr: { $and: [ 
{ $eq: [ '$date', '$$d' ] },
{ $not: [ { $eq: [ '$key', null ] } ] } 
] } 
} },
{ $limit: 1 }
],
as: 'docs'
} },
{ $match: { docs: { $size: 0 } } },
{ $limit: 1 },
{ $project: { day: 1 } } 
]
)

日期生成包含在一个额外的$过滤器中,用于闰年,但我建议在应用程序级别计算一年中的天数(367/366,这是唯一的边界)。然后,您可以移除过滤器,并将两个$map组合为一个。

最新更新