mongoose aggregation $group with nested object

Viewed 1146

I have the following resolver:

        const result = await UserPassage.aggregate([
      { $sort: { createdAt: -1 } },
      {
        $group: {
          _id: '$level',
          level: { $first: '$level' },
          passageId: { $first: '$passageId' },
          userId: { $first: '$userId' },
          type: { $first: '$type' },
          category: { $first: '$category' },
          score: { $first: '$score' },
          completedStage: { $first: '$completedStage' },
          userPassageStatsId: {
            _id: { $first: '$_id' },
            stats: {
              readingTime: { $first: '$readingTime' },
              qtdVocab: { $first: '$qtdVocab' },
              qtdTestDone: { $first: '$qtdTestDone' },
              totalQuiz: { $first: '$totalQuiz' },
              progress: { $first: '$progress' },
            },
          },
        },
      },
      { $sort: { level: 1 } },
    ]);

    await UserPassageStats.populate(result, { path: 'userPassageStatsId' });

The problem is that I need to populate 'userPassageStatsId' and return it but it's not working well returning the following error:

 MongoError: The field 'userPassageStatsId' must be an accumulator object

does anyone knows what I am doing wrong?

1 Answers

$group can only contain _id or accumulator objects like $first, $last, $sum etc. In your case your building nested object and that syntax is not allowed - accumulator has to be on a top level. You can try two approaches, either return flat structure from $group and then reshape using $project:

{
    $group: {
        _id: '$level',
        level: { $first: '$level' },
        passageId: { $first: '$passageId' },
        userId: { $first: '$userId' },
        type: { $first: '$type' },
        category: { $first: '$category' },
        score: { $first: '$score' },
        completedStage: { $first: '$completedStage' },
        userPassageStatsId_id: { $first: '$_id' },
        readingTime: { $first: '$readingTime' },
        qtdVocab: { $first: '$qtdVocab' },
        qtdTestDone: { $first: '$qtdTestDone' },
        totalQuiz: { $first: '$totalQuiz' },
        progress: { $first: '$progress' }
    }
},
{
    $project: {
        _id: 1,
        level: 1,
        ...,
        userPassageStatsId: {
            _id: "$userPassageStatsId_id",
            stats: {
                readingTime: "$readingTime",
                ...
            }
        }
    }
}

or use $$ROOT to capture first object for every group and reshape it using $project:

{
    $group: {
        _id: '$level',
        d: { $first: "$$ROOT" }
    }
},
{
    $project: {
        _id: 1,
        level: "$d.level",
        ...,
        userPassageStatsId: {
            _id: "$d._id",
            stats: {
                readingTime: "$d.readingTime",
                ...
            }
        }
    }
}
Related