Leonardo Drici
Leonardo Drici

Reputation: 789

Retrieve n random document with certain filters MongoDB

I need to retrieve from a collection of random docs based on a limit given. If some filters are provided they should be added to filter the results of the response. I'm able to build the match and size based on the fields provided but even tho I have 20 documents that meet the filter when I make the call I receive only 2 or 3 docs back and I can't seem to figure it out. If I set only the limit it does give me back N random docs based on the limit but if I add a filter it won't give me the wanted results.

This is what I do now

const limit = Number(req.query.limit || 1);

  const difficulty = req.query.difficulty;
  const category = req.query.category;
  const settings = [
    {
      $sample: {
        size: limit
      }
    }
  ];

  if (difficulty && category) {
    settings.push({
      $match: {
        difficulty: difficulty,
        category: category
      }
    });
  } else if (difficulty && category == null) {
    settings.push({
      $match: {
        difficulty
      }
    });
  }

  if (difficulty == null && category) {
    settings.push({
      $match: {
        category
      }
    });
  }
  console.log(settings);

  Question.aggregate(settings)
    .then(docs => {
      const response = {
        count: docs.length,
        difficulty: difficulty ? difficulty : "random",
        questions:
          docs.length > 0
            ? docs.map(question => {
                return {
                  _id: question._id,
                  question: question.question,
                  answers: question.answers,
                  difficulty: question.difficulty,
                  category: question.category,
                  request: {
                    type: "GET",
                    url:
                      req.protocol +
                      "://" +
                      req.get("host") +
                      "/questions/" +
                      question._id
                  }
                };
              })
            : {
                message: "No results found"
              }
      };

      res.status(200).json(response);
    })
    .catch(err => {
      res.status(500).json({
        error: err
      });
    });

Upvotes: 1

Views: 178

Answers (1)

Ashh
Ashh

Reputation: 46461

Order of the stages matters here. You are pushing the $match stage after the $sample stage which first put the $size to whole the documents and then applies the $match stage on the $sampled documents documents.

So finally you need to push the $sample stage after the $match stage. The order should be

const limit = Number(req.query.limit || 1);

  const difficulty = req.query.difficulty;
  const category = req.query.category;
  const settings = []

  if (difficulty && category) {
    settings.push({
      $match: {
        difficulty: difficulty,
        category: category
      }
    })
  } else if (difficulty && category == null) {
    settings.push({
      $match: {
        difficulty
      }
    })
  }

  if (difficulty == null && category) {
    settings.push({
      $match: {
        category
      }
    })
  }

  setting.push({
    $sample: {
      size: limit
    }
  })
  console.log(settings);

  Question.aggregate(settings)

Upvotes: 1

Related Questions