2

Hello every body here any one can help me with query below

  • I want to get quiz list with random amount
  • the amount of rendom will base on each lesson

The problem is

  • mongodb not allow to pass argument to element size of $sample

Any one can give me the solution

lessonModel.aggregate([
        { $match : {'quiz.status':1 } },
        {
            $lookup : {
                from : 'quiz',
                let : { 'lesson_id' : '$_id','limit' : '$quiz.amount' },
                pipeline : [
                    { 
                        $match: { 
                            $expr: { 
                                $eq: [ "$lesson_id", "$$lesson_id" ]
                            } 
                        } 
                    },
                    { 
                        $project: {
                            title:1,
                            check_list:1,
                            duration:1
                        }               
                    },
                    { $sample: { size: '$$limit' } }
                ],
                as: 'quiz'
            }
        }, 
        {$unwind: '$quiz'},
        { $replaceRoot: { newRoot: "$quiz" } }
    ]).exec();

The error said size argument to $sample must be a number

Here is my sample data Sample data

Dara Vichit
  • 590
  • 1
  • 7
  • 15

1 Answers1

1

UPDATE

I think your main problem is to randomly pick amount number of quizs under each lesson. Since $sample is not helpful use $function (New in version MongoDB 4.4).

Solution

Inside $function operator write some logic to

  1. Shuffle the questions (You can change it to your requirement).
  2. Slice it to return the number(amount) of questions required.
db.lessons.aggregate([
    { $match: { "quiz.status": 1 } },
    {
        $lookup: {
            from: "quiz",
            let: { "lesson_id": "$_id" },
            pipeline: [
                {
                    $match: {
                        $expr: { $eq: ["$lesson_id", "$$lesson_id"] }
                    }
                },
                {
                    $project: {
                        title: 1,
                        check_list: 1,
                        duration: 1
                    }
                }
            ],
            as: "questions"
        }
    },
    {
        $project: {
            quiz: {
                $function: {
                    body: function(questions, amount) {
                        if (amount == 0) return [];

                        for (let i = questions.length - 1; i > 0; i--) {
                            const j = Math.floor(Math.random() * (i + 1));
                            [questions[i], questions[j]] = [questions[j], questions[i]];
                        }

                        return questions.slice(0, amount);
                    },
                    args: ["$questions", { $ifNull: ["$quiz.amount", 0] }],
                    lang: "js"
                }
            }
        }
    },
    { $unwind: "$quiz" },
    { $replaceRoot: { newRoot: "$quiz" } }
]);

$sample does not support variables. A number must be specified explicitly like:

{ 
    $sample: { size: 1 }
}

Also replace your let as shown below because last lesson document has no amount filed in the quiz object

let: {
    'lesson_id': '$_id',
    'limit': { $ifNull: ['$quiz.amount', 0] } // or any other number.
},

Wrong:

{ 
    $sample: { size: "$$limit" } // Wont work!
}
Dheemanth Bhat
  • 4,269
  • 2
  • 21
  • 40