Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

mongodb: query for the time period between two date fields

If I have documents in the following schema saved in my mongoDB:

{
  createdDate: Date,
  lastUpdate: Date
}

is it possible to query for documents where the period of time between creation and the last update is e.g. greater than one day?

like image 533
zaphod1984 Avatar asked Mar 23 '16 11:03

zaphod1984


People also ask

How do you check if a date is between two dates in MongoDB?

Use operator gteandlt to find objects between two dates in MongoDB.

What is timestamp in MongoDB?

The MongoDB timestamp looks similar to Date data type, where it has the same 64-bit value. But it also has a few aspects where it differs from the Date. The MongoDB Timestamp is quite used for the internal purpose, and with every single instance of mongodb, the values generated for timestamp are unique.

How do I query a date in MongoDB?

We can use date () command to get a date as a string in a query without the new keyword or MongoDB shell.

How do I subtract two dates in MongoDB?

In MongoDB, you can use the $subtract aggregation pipeline operator to subtract numbers and/or dates. Specifically, $subtract can do the following three things: Subtract two numbers to return the difference. Subtract a number (in milliseconds) from a date and return the resulting date.


2 Answers

Starting in Mongo 5, it's a perfect use case for the new $dateDiff aggregation operator:

// { created: ISODate("2021-12-05T13:20"), lastUpdate: ISODate("2021-12-06T05:00") }
// { created: ISODate("2021-12-04T09:20"), lastUpdate: ISODate("2021-12-05T18:00") }
db.collection.aggregate([
  { $match: {
    $expr: {
      $gt: [
        { $dateDiff: { startDate: "$created", endDate: "$lastUpdate", unit: "hour" } },
        24
      ]
    }
  }}
])
// { created: ISODate("2021-12-04T09:20"), lastUpdate: ISODate("2021-12-05T18:00") }

This computes the number of hours of difference between the created and lastUpdate dates and checks if it's more than 24 hours.

like image 168
Xavier Guihot Avatar answered Oct 17 '22 05:10

Xavier Guihot


Best option is to use the $redact aggregation pipeline stage:

db.collection.aggregate([
    { "$redact": {
        "$cond": {
            "if": {
                "$gt": [
                    { "$subtract": [ "$lastUpdate", "$createdDate" ] },
                    1000 * 60 * 60 * 24
                ]
            },
            "then": "$$KEEP",
            "else": "$$PRUNE"
        }
    }}
])

So you are looking at the milliseconds value from the difference being greater than the milliseconds value for one day. The $subtract does the math for the difference, and when two dates are subtracted the difference in miliseconds is returned.

The $redact operator takes a logical expression as "if", and where that condition is true it takes the action in "then" which is to $$KEEP the document. Where it is false then the document is removed from results with $$PRUNE.

Note that since this is a logical condition and not a set value or a range of values, then an "index" is not used.

Since the operations in the aggregation pipeline are natively coded, this is the fastest execution of such a statement that you can get though.

The alternate is JavaScript evaluation with $where. This takes a JavaScript function expression that needs to similarly return a true or false value. In the shell you can shorthand like this:

db.collection.find(function() {
    return ( this.lastUpdate.valueOf() - this.createdDate.valueOf() )
       > ( 1000 * 60 * 60 * 24 );
})

Same thing, except that JavaScript evalution requires interpretation and will run much slower than the .aggregate() equivalent. By the same token, this type of expression cannot use an index to optimize performance.

For the best results, store the difference in the document. Then you can simply query directly on that property, and of course you can index it as well.

like image 30
Blakes Seven Avatar answered Oct 17 '22 04:10

Blakes Seven