node.jsmongoose

Pagination with aggregate search in mongoose


const page = 1;
const limit = 10;

const searchResultsArray = await mongoose.connection.db
                .collection('game_activity')
                .aggregate([
                    {
                        $match: {
                            game_id
                        }
                    },
                    {
                        $addFields: {
                            activities: {
                                $filter: {
                                    input: '$activity',
                                    as: 'activity',
                                    cond: {
                                        $and: condition
                                    }
                                }
                            }
                        }
                    },
                    {
                        $project: {
                            activities: 1,
                            activity_number: { <--- can't read property error
                                $size: '$activities'
                            }
                        }
                    },
                    { $limit: 50 },
                    {
                        $facet: {
                            paginatedResult: [ { $sort: searchFilter }, { $skip: (page - 1) * limit } ]
                        }
                    }
                ])
                .toArray();
            const searchResultsObject = searchResultsArray[0];
            if (
                searchResultsObject.paginatedResult[0].activity_number === undefined ||
                searchResultsObject.paginatedResult[0].activity_number == 0
            ) {
                const result = {
                    no_activities: true
                };
                res.json(result);
            } else {
                const result = {
                    search_result: {
                        total_activities: searchResultsObject.paginatedResult[0].activity_number,
                        current_page: page,
                        last_page: Math.ceil(searchResultsObject.paginatedResult[0].activity_number / 10),
                        searched_activities: searchResultsObject.paginatedResult[0].activities
                    },
                    no_activities: false
                };
                res.json(result);
            }

I have this .aggregate() search function and trying to apply the pagination result. This format worked on other search but on this aggregate() search, I have 2 problems.

console.log(searchResultsObject.paginatedResult[0]); 
-------- result --------
{
  search_result: {
    total_activities: 16,
    current_page: 1,
    last_page: 2,
    searched_activities: [
      [Object], [Object],
      [Object], [Object],
      [Object], [Object],
      [Object], [Object],
      [Object], [Object],
      [Object], [Object],
      [Object], [Object],
      [Object], [Object]
    ]
  },
  no_activities: false
}
  1. const limit = 10 does not applied here. It returns all of the activities.

  2. when I set the const page = 2;, I get TypeError: Cannot read property 'activity_number' of undefined this error and console.log(searchResultsObject.paginatedResult[0]); shows `undefined

I want to return 10 gaming activities per page, so in this case, if I set const page =2;, then it will return the rest 6 activities. I couldn't quite figure out how to fix this since this works on other .aggregate() search that I have.

------------ MongoDB Document ---------

{
    "_id": {
        "$oid": "601942d93aca6ee8cb300327"
    },
    "location_id": "ddff23323443",
    "activity": [{
        "activity_id": "VVINxmhRHsnMwvfT",
        "created_by": {
            "id": "aa332",
           
        },
        "activity_type": "league_of_legends",
        "activity_info": {
            "location_id": "ddff23323443",
            "activity_time": "2021-02-02 05:45:00.000Z",
            "game_code": "6"
        },
        "attendee": ["aa332"]
    }, {
        "activity_id": "EBZNKmsFKDgdeDz0",
        "created_by": {
            "id": "aa332",
           
        },
        "activity_type": "league_of_legends",
        "activity_info": {
            "id": "ddff23323443",
            "activity_time": "2021-02-02 05:45:00.000Z",
            "game_code": "6"
        },
        "attendee": ["aa332"]
    }, {
        "activity_id": "j8J1Jlk8MtWPi1HT",
        "created_by": {
            "id": "aa332",
           
        },
        "activity_type": "league_of_legends",
        "activity_info": {
            "location_id": "bvvsd33",
            "activity_time": "2021-02-02 05:45:00.000Z",
            "game_code": "6"
        },
        "attendee": ["aa332"]
    }]
}

----- expectation -----

const page = 1
 {
      search_result: {
        total_activities: 16,
        current_page: 1,
        last_page: 2,
        searched_activities: [
          [Object], [Object],
          [Object], [Object],
          [Object], [Object],
          [Object], [Object],
          [Object], [Object],
        ]
      },
      no_activities: false
    }

const page = 2
 {
      search_result: {
        total_activities: 16,
        current_page: 2,
        last_page: 2,
        searched_activities: [
          [Object], [Object],
          [Object], [Object],
          [Object], [Object],
        ]
      },
      no_activities: false
    }

Solution

  • Your expectation is not clear as per your try, i can explain the basic pagination process,

    const page = 1;
    const limit = 10;
    // If you are starting page number from 1 then you need to minus 1
    const skip = (page - 1) * limit;
    
    const searchResultsArray = await mongoose.connection.db.collection('game_activity').aggregate([
      { $match: { _id: game_id } },
      {
        $project: {
          activities: {
            $filter: {
              input: '$activity',
              as: 'activity',
              cond: { $and: condition }
            }
          }
        }
      },
      { $unwind: "$activities" },
      { $replaceRoot: { newRoot: "$activities" } },
      { $sort: searchFilter },
      {
        $facet: {
          searched_activities: [
            { $skip: skip },
            { $limit: limit }
          ],
          total_activities: [
            { $count: "count" }
          ]
        }
      }
    ]).toArray();
    
    // No Data Fond!
    if (!searchResultsArray[0].total.length) {
      res.json({ no_activities: true });
      return;
    }
    
    res.json({
      search_result: {
        total_activities: searchResultsArray[0].total_activities[0].count,
        current_page: page,
        last_page: Math.ceil(searchResultsArray[0].total_activities[0].count / limit),
        searched_activities: searchResultsArray[0].searched_activities
      },
      no_activities: false
    });
    

    Combine above code together in sequence and try.