Need to calculate the datetime difference for the datetime field saved in string format in MongoDB

梦想的初衷 提交于 2020-05-30 08:03:56

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!