Last Task on Exercise Tracker

Tell us what’s happening:
I am trying to solve the last task of Exercise Tracker. I am trying to use aggregate to execute queries in my subdocument called exercises. I got stuck though, because I can’t find many examples on the internet to understand how to use aggregate with mongoose.

My first question is whether this is doable with this approach. If so, I need some help including the username (a field where I do not apply any filter) in the aggregation result, and also limiting the number of the subdocuments.

Your code so far

code

Your browser information:

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

Challenge: Exercise Tracker

Link to the challenge:

If you log the foundUser aggregation with

    console.log(`foundUser: ${JSON.stringify(foundUser)}`);

you get

foundUser: [{"_id":"62466343f6e0d90d58be2d24","exercises":[]}]

and exercises is empty either because they are not posting correctly elsewhere or you’ve filtered all of them with the aggregation. Commenting the projection stuff gets you

foundUser: [{"_id":"624666c69ec0a211430375f1","username":"fcc_test_16487809986","exercises":[{"_id":"624666c79ec0a211430375f4","description":"test","duration":60,"date":"1990-01-01T00:00:00.000Z"},{"_id":"624666c79ec0a211430375f7","description":"test","duration":60,"date":"1990-01-03T00:00:00.000Z"}],"__v":2}]

which looks more like what you want. Presumably you can transform the document with projection or group stages to rearrange the data to look like the return object format.

Then to filter, I would assume you would need a conditional based on the filtering parameters and then use a $match stage (either chained or separate aggregations) to filter on from and to and maybe a limit stage to limit the documents. See the docs and this for further info. I may be wrong on the stage names; here is the list.

So, it should be doable but you’re going to have to play with the stages to find the correct ones and correct order. Of course, this is all really easy to do by grabbing the user document and exercise documents and processing them in the server. I’m sure it’s much faster to let mongoDB handle it (especially for large data sets and real scale projects), but speed doesn’t really matter here so this is much more of the extra credit solution to the project.

1 Like

Thank you for your answer!

The exercise array is empty because of the condition I have in the aggregate filter cond: { $eq: ['$$exercise.description', 'Sitting'] }, so it does not match any of the exercises generated from the tests.

I have also made a post on StackOverflow, since it seems to be an advanced topic, but my goal here is learning as much as I can. If I don’t get any help, I will just write a couple of if statements in order to pass the tests of the last task.

I should say this is my first reply and I can tell you it is doable because I just submitted mine a few minutes ago.

Though I did it quite a bit differently, here are some hints:

  1. Get the user by id,
  2. Run an aggregate() on your exercise model
    a. with match( filters) // Filters here would be exercise that belong to the user, and that meet the date (to/from) criteria if any
    b. with project () and pass an object with all the necessary fields as keys
    the date field gave me tough time because I was using free mongo service
    b. if limit is passed, use the limit() method as well

Okay, after getting some help on Stack Overflow I managed to partially understand how the aggregate framework works in mongodb, and pass the last test of this challenge. I will post my Schema and the aggregation I used to filter the results from the database.

Schema:

const Schema = mongoose.Schema;

const userSchema = new Schema({
  username: String,
  exercises: [{
    description: String,
    duration: Number,
    date: Date
  }]
});
const User = mongoose.model('User', userSchema);

aggregation:

let [foundUser] = await User.aggregate([
      {
        $match: { _id: new mongoose.Types.ObjectId(userId) }
      },{
        $project: {
          username: 1,
          exercises: {
            $slice: [
              {
                $filter: {
                  input: '$exercises',
                  as: 'exercise',
                  cond: {
                    $and: [
                      {
                        "$gte": [
                          "$$exercise.date", from
                        ]
                      },{
                        "$lt": [
                          "$$exercise.date", to
                        ]
                      }
                    ]
                  }
                }
              }, limit
            ]
          }
        }
      },{
        "$addFields": {
          "count": {
            $size: "$exercises"
          }
        }
      }
    ]);