Exercise Trancker - Trying to query by date an array inside a document

Tell us what’s happening:

Hello everybody! I’m trying to pass the final test on my last project for this certification.
I’m using only one schema and pure MongoDB without mongoose. I know
I could do it with 2 schemas and mongoose, but I really want to learn how to
make queries inside an array that’s inside a document.
But I just cannot make it on my own.

Your code so far

//Here I'm trying to get only the objects inside the array inside user by limiting the dates:
let user = await usernames
  .find({ _id: objectID(userId) }, { log: { date: { $gte: from } } })
  .toArray();


Your browser information:

User Agent is: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36.

Challenge: Exercise Tracker

Link to the challenge:

Please Help :frowning:

1 Like

Hey David,

I’ve edited your post for readability. When you enter a code block into a post, please precede it with a separate line of three backticks and follow it with a separate line of three backticks to make it easier to read.

You can also use the “preformatted text” tool in the editor ( </> ) to add backticks around text.

See this post to find the backtick on your keyboard. Backticks (`) are not single quotes (’).

Regarding your problem,

did you look up on the gte operator?

db.inventory.find( { qty: { $gte: 20 } } )

This query would select all documents in inventory where the qty field value is greater than or equal to 20 .

So what do you want your find method to do?

Thanks for formatting my text and replying!.

Yes, I did look up the operators and mongodb aggregation framework.
The thing is that I want to limit the objects inside an array inside the document I’m searching the user by id and then inside this user’s document there is an array of exercise logs that I want to limit by the date they have. I’m receiving the
user and all my problem is the limiting of the array inside of the document

it looks something like this:

{
"_id": "5f2ae5d0a6ac9a119bb11181",
"username": "fcc_test_15966468633",
"log": [
{
"_id": "5f2ae5d1a6ac9a119bb11182",
"description": "test",
"duration": "60",
"date": "1990-01-01T00:00:00.000Z"
}
],
"count": 1
}

Im getting a ‘‘date’’ string from the url which I use to create a date object
to compare it with the log’s date object by using mongodb $gte but it doesnt seem to work. Please help :frowning:

So what’s the format of from in your gte and the format that is in the database?

I decided to opt for a traditional JavaScript approach for this:

If there is a ‘from’ or ‘to’ query, create some dates as the default from and to. I set the default from to be 0 (1st jan 1970) and to to be the current time. If either the from or to query exists, replace these dates with those. Then call the filter() method on the array returning only the values between these dates. We can call the getTime() method on the dates to convert these into a numerical timestamp to compare.


/*Date Filter */
if(request.query.from || request.query.to){
  let fromDate = new Date(0)
  let toDate = new Date()
  
  if(request.query.from){
    fromDate = new Date(request.query.from)
  }
  
  if(request.query.to){
    toDate = new Date(request.query.to)
  }
  
  result.log = result.log.filter((exerciseItem) =>{
    let exerciseItemDate = new Date(exerciseItem.date)
    
    return exerciseItemDate.getTime() >= fromDate.getTime()
      && exerciseItemDate.getTime() <= toDate.getTime()
  })
  
}

responseObject = responseObject.toJSON()
responseObject['count'] = result.log.length
response.json(result)

This may not necessarily be the best approach since we are fetching all the data from the database unnecessarily, and moves the computation from MongoDB to our express app.

Hey! thanks for answering!
Yeah the JS way is really straightforward, but I wanted to learn about mongoDB
and experiment with it as much as I could.

I ended up using aggregation framework like this:

  let usernames = db.collection('usernames');
  //Passing
  return async function(req, res){
  let { userId, from, to, limit } = req.query;
    userId = objectID(userId)
  try {
    
    //If there is no from in req.query, return everything since 1970
    if(from){
      from = new Date(from);
    }
    else{
      from = new Date(0);
    }
    
    //if there is no to, return everything until today
    if(to){
      to = new Date(to);
    }else{
      to = new Date();
    }
    
    //if there is no limit then return a whole page
    if(!limit){
      limit = 100;
    }
    
    //Query to getting the logs array
    let user = await usernames.aggregate([
      {
        //getting the right user by Id
        $match:{
          _id:userId
        }
      },
      //Filtering the log array inside the user's document
       { $project:{
          log:{
            $filter:{
              //Selecting the the array inside the user's document
              input:"$log",
              //selecting a name for every object inside the log's array
              as:"log",
              cond:{
                //Limiting the log by the date
                $and:[{$gte: ["$$log.date",from]},{$lte:["$$log.date",to]}]
              }
            }
          },
         //selecting which parameters to return with the user
         username:1
        },
      },
      {
        //Limiting results
        $limit:limit
      }
    ]).toArray();
    if (!user[0]) {
    return res.status(404).send();
    }
    
    //Counting documents 
    user[0].count = user[0].log.length;
    res.send(user[0]);

If you could check out my code and let me know what you think I would appreciate it!

Looks Good to me! It’s nice that you’ve managed to do all the computation using database logic, very efficient.

Only suggestion would be to maybe adapt this to be the length of the User’s log rather than 100?