How to query documents in MongoDB that fall within a specified date range using Mongoose and Node.
Tags: node, javascript, mongodb, mongoose
Published: 2019-11-07 23:47:25

---
title: How to query documents in MongoDB that fall within a specified date range using Mongoose and Node.
published: true
description: A tutorial to show how to make database queries using dates as upper and lower limits.
tags: node, javascript, mongodb, mongoose.
cover_image: https://thepracticaldev.s3.amazonaws.com/i/md7dx1mtz1ijznt1eilf.jpg
---

***Please Note: This tutorial assumes you have a working knowledge of javascript, the node run-time environment, mongoose queries and mongoDB***



##The Challenge:

So you work for a sales company called ***Super Doors*** as a developer and your boss, a Senior dev has been tasked with finding all customers that were unfortunate enough to purchase some bulletproof doors from the 24th - 30th of the month. Turns out the doors weren't so super after all and management needs a list of all the purchases made within those days for proper damage control. Lucky for you, your boss can't be bothered with such tasks and he assigns it to you. Next he goes, 'you know what? Why not just make it a feature on the platform for users to be able to query the database for customers within any range of dates?'

###On the frontend:
 You're meant to build a date range component that sends the **startDate** and the **endDate** to the backend. I won't go through building this as our concern is how to handle using dates on the backend to query the database. The point here is to define what values we're expecting as request parameters for the query.

###Approach:
It's always a good idea to actually write out in words how you intend to approach the problem. You can be as specific or vague about it as you want, as long as it keeps you on track. Here's mine:

1. check that date is not empty
2. check that date is in proper format for query
3. Query database using Mongoose
4. Handle response.


## On the backend.
We would need  a **Transaction model** to hold information about each transaction made and a reference to an arbitrary User model. We would create a folder called 'models' and within it create a js file called 'transaction.js'.


##Transaction Model:
```javascript
const mongoose = require('mongoose');


const transactionSchema = mongoose.Schema({
     _id:{ type: mongoose.Schema.Types.ObjectId, ref: 'User'},
     name: String,
     email: {
              type: String,
              unique: true,
              match:`Regex match to ensure that string follows email format`  
             },
    address: String,
    phone: String,
    door:{ 
          name:String, 
          price: String
         },
   payment_status: {
            type: String,
            enum: ['Not Paid', 'Partial Payment', 'Paid'],
            default: 'Not Paid'
                   },
  date_paid: Date,
  
  date_delivered: Date          
});

module.exports = mongoose.model('Transaction', transactionSchema); 

```

##Controller Function:

Our controller function is where all the magic happens. Just like we did for the transaction.js file above, we would create a folder called 'controllers' and within it create a file called 'transaction.js'. 

```javascript
const Transaction = require('../models/transaction');


exports.getTransactionByDate = async(req, res) => {

try {
   //get dates from req.query by es6 object destructuring

    let { startDate, endDate } = req.query;
   
  //1. check that date is not empty
   if(startDate === '' || endDate === '') {
   return res.status(400).json({
       status:'failure',
       message: 'Please ensure you pick two dates'
        })
       }
       
  //2. check that date is in the right format
 //expected result: YYY-MMM-DDD
   console.log({ startDate, endDate});


//In some cases you'll get a date-time format where you have to separate the date
//from the time.


//3. Query database using Mongoose
//Mind the curly braces
const transactions = Transaction.find({ 
  date_paid: {
        $gte: new Date(new Date(startDate).setHours(00, 00, 00))
        $lt: new Date(new Date(endDate).setHours(23, 59, 59))
         }
  }).sort({ date_paid: 'asc'})  
}

//4. Handle responses
if(!transactions) {
return res.status(404).json({
 status:'failure',
 message:'Could not retrieve transactions'
})
}


res.status(200).json({
status:'success',
data: transactions
   })

} catch(error) {
  return res.status(500).json({
     status:'failure',
     error: error.message
        })
 }

}
```

Woah, okay, slow down...what just happened?

* We got the required parameters from our request query object:

```javascript
    let { startDate, endDate } = req.query;
```

* We checked that neither parameters were empty and handled the response should that be the case.

```javascript
    if(startDate === '' || endDate === '') {
   return res.status(400).json({
       status:'failure',
       message: 'Please ensure you pick two dates'
        })
       }
       
  //2. check that date is in the right format
 //expected result: YYY-MMM-DDD
   console.log({ startDate, endDate});
```
***It is easier to get the date in Year-Month-Date format since that is the default way javascript handles dates***

* We queried the database using the mongoose **find** and **sort** method. We also made use of mathematical operations, namely 'greater than or equal to'--$gte and 'less than'--$lt. The sort method was just a little extra I added to sort our list of transactions in ascending order, it's not inherently necessary.

```javascript
   const transactions = Transaction.find({ 
  date_paid: {
        $gte: new Date(new Date(startDate).setHours(00, 00, 00))
        $lt: new Date(new Date(endDate).setHours(23, 59, 59))
         }
  }).sort({ date_paid: 'asc'})  
}
```
Now you may be confused by what this **new Date(new Date(startDate).setHours(00, 00, 00))** and this **new Date(new Date(endDate).setHours(23, 59, 59))** does...Don't worry, got you covered.

When you call  **new Date()** and pass it a date in string format, it returns the date you passed to it in date-time format. Then when we call new Date('date').setHours(); we get the chance to specify the time of day we want to start searching from in hour-minute-second format. In the case above, we want to start searching from the very beginning of our start date, so we have **new Date('date').setHours(00,00,00);**. Here's a short snippet from my PC to explain it further:

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/po0rnpgggx1cdbojilhl.png)

In the same manner, we want to get transactions up to but not beyond the last minute of our endDate, so we type **new Date('date').setHours(23,59,59);**. 

But that's not all is it? We still have to wrap our **new Date().setHours()** in another new Date(). Why? Because as you can see from the snippet above, **new Date().setHours()** gives us what we want in seconds!(or is it milliseconds? I'm not sure). The point is, that's not the format we want, it's not legible enough.

So we wrap up **new Date().setHours()** to become **new Date(new Date().setHours())** and we get our date in date-time format to the specific time we want!

* We handle responses. Both for success and failure.
 In three statements:
-If we don't get our transactions, notify the client. 
-If we get our transactions however, return the data to the client side. 
-All other exceptions, catch!

```javascript
  if(!transactions) {
return res.status(404).json({
 status:'failure',
 message:'Could not retrieve transactions'
})
}


res.status(200).json({
status:'success',
data: transactions
   })

} catch(error) {
  return res.status(500).json({
     status:'failure',
     error: error.message
        })
 }
```

**If you want to search for records within a single day, it's pretty much the same concept, only this time you have the same startDate and endDate, the difference would be in the time part of the query**


##Our Router:

Here we listen for get requests on the specified path of our choice and tell our getTransactionByDate function to handle the event for us.

```javascript
  const express = require('express');
  const { getTransactionByDate } = require('../controllers/transaction');

const API = process.env.BASE_URL;
const router = express.Router();

router.get(`${API}/date_range`, getTransactionByDate);

module.exports = router;

```




**I hope this has been helpful. If it has, like and comment! Also share to anyone you think may be having a hard time with querying NoSQL databases within a  specific date range.**