33

My daily collection has documents like:

..
{ "date" : ISODate("2013-01-03T00:00:00Z"), "vid" : "ED", "san" : 7046.25, "izm" : 1243.96 }
{ "date" : ISODate("2013-01-03T00:00:00Z"), "vid" : "UA", "san" : 0, "izm" : 0 }
{ "date" : ISODate("2013-01-03T00:00:00Z"), "vid" : "PAL", "san" : 0, "izm" : 169.9 }
{ "date" : ISODate("2013-01-03T00:00:00Z"), "vid" : "PAL", "san" : 0, "izm" : 0 }
{ "date" : ISODate("2013-01-03T00:00:00Z"), "vid" : "CTA_TR", "san" : 0, "izm" : 0 }
{ "date" : ISODate("2013-01-04T00:00:00Z"), "vid" : "CAD", "san" : 0, "izm" : 169.9 }
{ "date" : ISODate("2013-01-04T00:00:00Z"), "vid" : "INT", "san" : 0, "izm" : 169.9 }
...

I left off _id field to spare the space here. My task is to "fetch all documents within last 15 days". As you can see I need somehow to:

  1. Get 15 unique dates. The newest one should be taken as the newest document in collection (what I mean that it isn't necessary the today's date, it's just the latest one in collection based on date field), and the oldest.. well, maybe it's not necessary to strictly define the oldest day in query, what I need is some kind of top15 starting from the newest day, if you know what I mean. Like 15 unique days.
  2. db.daily.find() all documents, that have date field in that range of 15 days.

In the result, I should see all documents within 15 days starting from the newest in collection.

Xavier Guihot
  • 54,987
  • 21
  • 291
  • 190
kaytrance
  • 2,657
  • 4
  • 30
  • 49

3 Answers3

88

I just tested the following query against your data sample and it worked perfectly:

db.datecol.find(
{
    "date": 
    {
        $gte: new Date((new Date().getTime() - (15 * 24 * 60 * 60 * 1000)))
    }
}
).sort({ "date": -1 })
AshHimself
  • 4,024
  • 1
  • 21
  • 26
Moacy Barros
  • 1,869
  • 13
  • 9
8

Starting in Mongo 5, it's a nice use case for the $dateSubtract operator:

// { date: ISODate("2021-12-05") }
// { date: ISODate("2021-12-02") }
// { date: ISODate("2021-12-02") }
// { date: ISODate("2021-11-28") } <= older than 5 days
db.collection.aggregate([
  { $match: {
    $expr: {
      $gt: [
        "$date",
        { $dateSubtract: { startDate: "$$NOW", unit: "day", amount: 5 } }
      ]
    }
  }}
])
// { date: ISODate("2021-12-05") }
// { date: ISODate("2021-12-02") }
// { date: ISODate("2021-12-02") }

With $dateSubtract, we create the oldest date after which we keep documents, by subtracting 5 (amount) "days" (unit) out of the current date $$NOW (startDate).

And you can obviously add a $sort stage to sort documents by date.

Xavier Guihot
  • 54,987
  • 21
  • 291
  • 190
  • 2
    Perfect answer. – arbob Mar 19 '22 at 12:28
  • Please note that using $expr with $dateSubstract will lead to table scan. $expr has limitation regarding indexes uses. Documentation states: "Indexes are not used for comparisons with more than one field path operand". But the query suggested by @Xavier will transform into a filter with an $and operator. Which I suspect prevent $expr from using any index. Tested this on our own database and I can confirm Moacy Barros answer is still the best choice, especially if you need indexes for query optimisation. – BenBenMushi Jul 26 '23 at 08:17
  • `the best choice` depends on the context in which a query is used. Not all contexts depend on a big collection or even require an index. But indeed, good warning for those specific contexts. – Xavier Guihot Jul 26 '23 at 08:39
-1

You need to run the distinct command to get all the unique dates. Below is the example. The "values" array has all the unique dates of the collection from which you need to retrieve the most recent 15 days on the client side

db.runCommand ( { distinct: 'datecol', key: 'date' } )
{
    "values" : [
       ISODate("2013-01-03T00:00:00Z"),
       ISODate("2013-01-04T00:00:00Z")
    ],
    "stats" : {
       "n" : 2,
       "nscanned" : 2,
       "nscannedObjects" : 2,
       "timems" : 0,
       "cursor" : "BasicCursor"
    },
    "ok" : 1
}

You then use the $in operator with the most recent 15 dates from step 1. Below is an example that finds all documents that belong to one of the mentioned two dates.

db.datecol.find({
  "date":{
     "$in":[
        new ISODate("2013-01-03T00:00:00Z"), 
        new ISODate("2013-01-04T00:00:00Z")
      ]
  }
})
Mr. Alien
  • 153,751
  • 34
  • 298
  • 278
Aravind Yarram
  • 78,777
  • 46
  • 231
  • 327