Mongodb 获取运行日期的差异
我在mongo有一个多个日期的列表,我想找出每个日期之间的时差(例如,objectId 3和4、objectId 4和5、objectId 5和6之间的时间。我尝试了mongo的$subtract,但它以毫秒为单位给出了答案,转换后没有意义。此外,objectId 3不应该有dateDiff值,因为之前没有日期Mongodb 获取运行日期的差异,mongodb,mongodb-query,aggregation-framework,Mongodb,Mongodb Query,Aggregation Framework,我在mongo有一个多个日期的列表,我想找出每个日期之间的时差(例如,objectId 3和4、objectId 4和5、objectId 5和6之间的时间。我尝试了mongo的$subtract,但它以毫秒为单位给出了答案,转换后没有意义。此外,objectId 3不应该有dateDiff值,因为之前没有日期 > db.collection.find({item:"1"}) { "_id" : ObjectId("3"), "item" : "1", "date" : ISODate("
> db.collection.find({item:"1"})
{ "_id" : ObjectId("3"), "item" : "1", "date" : ISODate("2016-02-08T10:24:36Z") }
{ "_id" : ObjectId("4"), "item" : "1", "date" : ISODate("2016-02-13T10:24:36Z") }
{ "_id" : ObjectId("5"), "item" : "1", "date" : ISODate("2016-02-29T10:24:36Z") }
{ "_id" : ObjectId("6"), "item" : "1", "date" : ISODate("2016-03-13T10:24:36Z") }
> db.collection.aggregate([
{$match:{item:"1"}},
{$sort:{date:1}},
{$project:{dateDiff:{$subtract:[new Date(),"$date"]}}}
])
{ "_id" : ObjectId("3"), "dateDiff" : NumberLong("18412790289") }
{ "_id" : ObjectId("4"), "dateDiff" : NumberLong("17980790289") }
{ "_id" : ObjectId("5"), "dateDiff" : NumberLong("16598390289") }
{ "_id" : ObjectId("6"), "dateDiff" : NumberLong("15302390289") }
使用新的Date()
可以有效地将当前的日期与各自的日期字段中的日期进行比较。您要做的是将不同文档中的值相互比较,这可以通过创建包含所有日期的帮助器数组来实现
以下是一个有效的注释版本:
db.collection.aggregate([{
$match: {
item: "1" // not really needed but I guess it makes sense in your specific case so I leave it here
}
},
{
$sort: {
date: 1 // we need to sort in order to find the next bigger date
}
},
{
$group: {
"_id": "$item", // for every item...
"date": { $push: "$date" }, // ...we create two identical arrays (could be done using another $project stage, too)
"allDates": { $push: "$date" } // ...that each contain all dates that we find for our item
}
},
{
$unwind: "$date" // this will flatten one of the two created date arrays
},
{
$project: {
"date": 1,
"followingDate": {
$arrayElemAt: [
"$allDates", // here we use the second (remaining) date array to find the next date...
{ $add: [ { $indexOfArray: [ "$allDates", "$date" ] }, 1 ] } // ...which needs to sit in that sorted array at the position of the element we're looking at right now + 1
]
}
}
},
{
// now, we simply create a nice result document with various ways of looking at the date differences
$project: {
"date": 1,
"followingDate": 1,
"differenceInMilliseconds": {
$subtract: [ "$followingDate", "$date" ]
},
"differenceInSeconds": {
$divide: [ { $subtract: [ "$followingDate", "$date" ] }, 1000 ]
},
"differenceInMinutes": {
$divide: [ { $subtract: [ "$followingDate", "$date" ] }, 1000 * 60 ]
},
"differenceInHours": {
$divide: [ { $subtract: [ "$followingDate", "$date" ] }, 1000 * 60 * 60 ]
},
"differenceInDays": {
$divide: [ { $subtract: [ "$followingDate", "$date" ] }, 1000 * 60 * 60 *24 ]
}
}
}])
您必须查看您尝试的语句,并理解您要求“数据库服务器”所做的只是“从当前时间中提取日期值”,这就是
new date()
实际执行的操作。这解释了您从尝试中获得的输出,因为该值不是当前值“上次文件日期”
如果您所追求的只是前一项和当前项之间的日期差异,那么我真的不认为这是“数据库服务器”本身要执行的任务。您所要求的,更自然地通过简单地处理光标和保留对前一个“日期”的引用来解决“然后简单地从电流中减去:
var previousDate = false;
db.getCollection('collection').find().sort({ "item": 1, "date": 1 }).map( d => {
var diff = (previousDate) ? NumberLong(d.date - previousDate) : 0;
previousDate = d.date;
return Object.assign(d,{ diff })
})
返回:
{
"_id" : ObjectId("598b794c1b61b8b7a12329b4"),
"item" : "1",
"date" : ISODate("2016-02-08T10:24:36.000Z"),
"diff" : 0.0
},
{
"_id" : ObjectId("598b794c1b61b8b7a12329b5"),
"item" : "1",
"date" : ISODate("2016-02-13T10:24:36.000Z"),
"diff" : NumberLong(432000000)
},
{
"_id" : ObjectId("598b794c1b61b8b7a12329b6"),
"item" : "1",
"date" : ISODate("2016-02-29T10:24:36.000Z"),
"diff" : NumberLong(1382400000)
},
{
"_id" : ObjectId("598b794c1b61b8b7a12329b7"),
"item" : "1",
"date" : ISODate("2016-03-13T10:24:36.000Z"),
"diff" : NumberLong(1123200000)
}
这通常是最好的做法,因为自然事件是“一个接一个”的,这就是处理光标的实际操作。MongoDB本身没有真正的方法“引用前一个文档的值”,唯一的方法基本上是将值强制到“数组”中进行处理
因此,在大多数实际情况下,这种“作为数组”的处理实际上并不实用,而且通过将值存储在任意大小的数组中,实际上会超过16MB BSON限制
但是,如果您认为必须“在服务器上”执行此操作,则最有效的使用方法是,在将列表分别作为$$this
和$$value
处理时,应用该选项可以访问“当前”和“以前减少的”项目:
db.getCollection('collection').aggregate([
{ "$sort": { "item": 1, "date": 1 } },
{ "$group": {
"_id": "$item",
"diff": { "$push": "$date" }
}},
{ "$addFields": {
"diff": {
"$map": {
"input": {
"$reduce": {
"input": "$diff",
"initialValue": [],
"in": {
"$concatArrays": [
"$$value",
[{
"date": "$$this",
"diff": {
"$cond": {
"if": { "$eq": [{ "$size": "$$value" }, 0] },
"then": 0,
"else": {
"$subtract": ["$$this", { "$arrayElemAt": ["$$value.date", -1] }]
}
}
}
}]
]
}
}
},
"as": "d",
"in": "$$d.diff"
}
}
}},
{ "$unwind": "$diff" }
])
哪个输出
{ "_id" : "1", "diff" : 0.0 }
{ "_id" : "1", "diff" : NumberLong(432000000) }
{ "_id" : "1", "diff" : NumberLong(1382400000) }
{ "_id" : "1", "diff" : NumberLong(1123200000) }
请注意,因为与其他语言的对应部分一样,它通常是关于“减少”一个结果,所以我们通过使用“连接”“上一个”和“当前”输出,将返回的结果设置为一个数组
每个项目的基本过程(第一个项目的逻辑除外)是通过使用索引-1
(表示数组的“最后一个”项目)从通过前一阶段收集的“当前”项目处理到上一个结果
由于需要“上一个日期”,因此输出包括“date”
值,以确定每个日期之间的差异。实际的“diff”
然后通过包装操作提取值,包装操作仅返回该属性的值,并得出最终结果,然后处理数组,以便将项目放回文档形式
更少的管道阶段总是意味着更好的性能,因为每个阶段本质上都是一个“数据传递”,这需要时间。所以这里进行“收集”,下一个阶段发现“差异”。这基本上就是操作,实际上只需要这两个阶段
这有点“杂耍”,但比你可能做的要少很多,而且是真正解决“服务器上”问题的最有效的方法。但这实际上不是“服务器”问题,更适合于简单地“按顺序处理光标”,就像最初显示的那样