问题
Sample Documents :
{
"_id" : ObjectId("50ed90a55502684f440001ac"),
"time" : "2020-05-14T14:12:46.058-0400",
"ServerTimeStamp" : "2020-05-14T14:12:46.058-0400"
}
{
"_id" : ObjectId("50ed90a55502684f440001ad"),
"time" : "2020-05-14T15:12:46.058-0400",
"ServerTimeStamp" : "2020-05-14T14:12:46.058-0400"
}
{
"_id" : ObjectId("50ed90a55502684f440001ae"),
"time" : "2020-05-14T18:12:46.058-0400",
"ServerTimeStamp" :"2020-05-14T14:12:46.058-0400"
}
I would like to calculate the time difference of above rows ( in seconds) as below, here the time field is in string format instead of date.
Required Output :
{
"_id" : ObjectId("50ed90a55502684f440001ac"),
"time" : "2020-05-14T14:12:46.058-0400",
"time_difference" :null
}
{
"_id" : ObjectId("50ed90a55502684f440001ad"),
"time" : "2020-05-14T15:12:46.058-0400",
"time_difference" : 3600
}
{
"_id" : ObjectId("50ed90a55502684f440001ae"),
"time" : "2020-05-14T18:12:46.058-0400",
"time_difference" : 10800
}
I've tried the below query but getting error and I have already converted the time to date from string :
db.hello.aggregate(
{$sort: {$dateFromString:{time: 1}}},
{$group: {_id: 0, document: {$push: '$$ROOT'}}},
{$project: {documentAndPrevTime: {$zip: {inputs: ['$document', {$concatArrays: [[null], '$document.time']}]}}}},
{$unwind: {path: '$documentAndPrevTime'}},
{$replaceWith: {$mergeObjects: [{$arrayElemAt: ['$documentAndPrevTime', 0]}, {prevTime: {$arrayElemAt: ['$documentAndPrevTime', 1]}}]}},
{$set: {time_difference: {$trunc: [{$divide: [{$subtract: ['$time', '$prevTime']}, 1000]}]}}},
{$unset: 'prevTime'}
);
回答1:
You've written all the stages, But the thing here is there is no relation between one to another, Means changes done at prior setup is not being reflected in next steps. Couple of issues :
{$sort: {$dateFromString:{time: 1}}} // You can't use `$dateFromString` in sort stage. Also syntax of `$dateFromString` is incorrect.
Let's suppose if it worked(it won't but assume ) you're not actually converting time
& storing converted time to a variable for later use in $group
or further down stages. So you need to store it to a variable in the respective document using $addFields
or $project
. I've not gone further down but you can try below query :
Query :
db.collection.aggregate([
/** sort on `time` field */
{ $sort: { time: 1 } },
/** Convert string format of `time` field to milliseconds & store to `convertedTime` field for each doc */
{ $addFields: { convertedTime: { $toLong: { $dateFromString: { dateString: "$time" } } } } },
/** Group without condition to push all documents into `docs` array */
{
$group: { _id: "", docs: { $push: "$$ROOT" } }
},
/** re-creating `docs` array */
{
$project: {
_id: 0,
docs: {
$reduce: {
input: { $slice: [ "$docs", 1, { $size: "$docs" } ] }, /** Pick `docs` array without first element for iteration */
initialValue: { docObj: [ { $arrayElemAt: [ "$docs", 0 ] } ], previousTime: { $arrayElemAt: [ "$docs.convertedTime", 0 ] } },
in: {
docObj: { $concatArrays: [ "$$value.docObj", [
{ $mergeObjects: [ "$$this", { time_difference: { $divide: [ { $subtract: [ "$$this.convertedTime", "$$value.previousTime" ] }, 1000 ] } } ] }
]
]
},
previousTime: "$$this.convertedTime" // Store current doc's time to `previousTime` to utilize for next record
}
}
}
}
},
{
$unwind: { path: "$docs.docObj" }
},
/** Remove additionally added field */
{
$project: { "docs.docObj.convertedTime": 0 }
},
/** Replace root of the doc with `docs.docObj` */
{
$replaceRoot: { newRoot: "$docs.docObj" }
}
])
Test : mongoplayground
Ref : aggregation-pipeline
Note : This query would not add "time_difference" :null
for first document but just in case if it's needed try this in initialValue
: docObj: [ {$mergeObjects :[ { $arrayElemAt: [ "$docs", 0 ] }, { "time_difference" :null } ] ]
. Also I would suggest to limit this operation to certain documents in the collection using $match
as first stage, rather-than doing this query on all of the documents cause
{
$group: { _id: "", docs: { $push: "$$ROOT" } }
}
itself will be a huge thing when it's done on entire collection with huge dataset.
来源:https://stackoverflow.com/questions/61825682/need-to-calculate-the-datetime-difference-for-the-datetime-field-saved-in-string