使用 Node.JS 过滤 MongoDB 中的文档以实现多重约束

问题描述 投票:0回答:1

我在

questionBank
集合中有 1000 个问题(每个问题都是一个文档)。

每个问题都有自己的文档 ID、主题键

(s1, s2, s3, s4, ... ,s10)
和难度键
(easy, medium, hard)

我的目标是准确获取其中 15 个问题,以满足以下约束:

  • 主题:我们最多需要
    s1 to s5
    主题提出 2 个问题。我们最多需要来自
    s6 to s10
    主题的 3 个问题。
  • 难度:我们希望最多 40% 的简单问题,最多 75% 的中等问题,最多 40% 的困难问题。 (现在,我们随机生成每个难度的问题数量)。
  • 每个用户都有
    visitedQuestions
    集合,其中他们已经尝试过的所有问题的列表都由文档 ID 存储,因此我们也希望避免使用之前尝试过的问题。
result = await QuestionBank.aggregate([
            {
              $facet: {
                easyQuestions: [
                  {
                    $match: {
                      difficulty: "Easy",
                      ...criteriaSet,
                    },
                  },
                  { $sample: { size: difficultyRanges.easy } },
                ],
                mediumQuestions: [
                  {
                    $match: {
                      difficulty: "Medium",
                      ...criteriaSet,
                    },
                  },
                  { $sample: { size: difficultyRanges.medium } },
                ],
                hardQuestions: [
                  {
                    $match: {
                      difficulty: "Hard",
                      ...criteriaSet,
                    },
                  },
                  { $sample: { size: difficultyRanges.hard } },
                ],
              },
            },
            {
              $project: {
                questions: {
                  $concatArrays: [
                    "$easyQuestions",
                    "$mediumQuestions",
                    "$hardQuestions",
                  ],
                },
              },
            },
            {
              $unwind: "$questions",
            },
            {
              $group: {
                _id: "$questions.subTopic",
                questions: { $push: "$questions" },
                count: { $sum: 1 },
              },
            },
            {
              $match: {
                $or: SAToicLimits,
              },
            },
            {
              $limit: 15,
            },
          ]);
          let resres: any = [];
          for (let i = 0; i < result.length; i++) {
            resres = [...resres, ...result[i].questions];
          }
          result = resres;
          if (result.length === 15) {
            break;
          }

注意:

SAToicLimits
有科目名称和各自的限制。

最有效的方法是什么?

node.js mongodb mongoose
1个回答
0
投票

第一个挑战是获得一组随机问题。为此,您可以按

$toHashedIndexKey
值排序。使用
$dateToString: { date: "$$NOW" }
保证每次调用的顺序都不同(除非在一毫秒内执行多次)。您还可以使用静态值(例如
a
b
c
)来获得确定性的随机顺序。

一旦你有了随机的问题顺序,你就可以应用你的约束,例如

{$eq: ["$difficulty", "Medium"]}
{$lt: [{$divide: ["$difficultyNo", "$difficultyCount"]}, 0.75]}

这是实现这一目标的第一次尝试:

const visitedQuestions = db.visitedQuestions.find({user: "Shaha"}).toArray().map( x => x._id )

db.collection.aggregate([
  { $match: { _id: { $nin: visitedQuestions } } },
  {
    $set: {
      orderBy: {
        $toHashedIndexKey: {
          $concat: [
            { $toString: "$_id" },
            { $dateToString: { date: "$$NOW" } } // or a static value for random orders which are deterministic
          ]
        }
      }
    }
  },
  {
    $setWindowFields: {
      partitionBy: "$difficulty",
      sortBy: { orderBy: 1 },
      output: {
        difficultyNo: { $documentNumber: {} },
        difficultyCount: { $count: {} }
      }
    }
  },
  {
    $match: {
      $expr: {
        $or: [
          { $and: [
              { $eq: [ "$difficulty", "Easy" ] },
              { $lt: [ { $divide: [ "$difficultyNo", "$difficultyCount" ] }, 0.4 ] }
          ]},
          { $and: [
              { $eq: [ "$difficulty", "Medium" ] },
              { $lt: [ { $divide: [ "$difficultyNo", "$difficultyCount" ] }, 0.75 ] }
          ]},
          { $and: [
              { $eq: [ "$difficulty", "Hard" ] },
              { $lt: [ { $divide: [ "$difficultyNo", "$difficultyCount" ] }, 0.4 ] }
          ]}
        ]
      }
    }
  },
  { $sort: { orderBy: 1 } },
  { $limit: 15 }
])

这不是最终的解决方案,但它指明了前进的方向。添加

  {
    $setWindowFields: {
      partitionBy: "$subject",
      sortBy: { orderBy: 1 },
      output: {
        subjectNo: { $documentNumber: {} }
      }
    }
  }

定义与主题相关的约束。

© www.soinside.com 2019 - 2024. All rights reserved.