我确实对Moped查询表格有一个普遍的偏好,因为它可以在较低级别上工作,并且可以利用MongoDB查询操作符的完整功能集。对某些人来说,这可能看起来不是很“高尚”,但有一些优势。特别是当解决方案涉及所以为了找到匹配的先决条件的学生已经采取,你会建立这样的语句课程,该课程采用.aggregate()
:
Course.collection.aggregate([
// Filters the documents, not an exact match but a start
{ "$match" => {
"prerequisites" => { "$in" => [ "a", "b" ] },
}},
// Unwind the array
{ "$unwind" => "$prerequisites" },
// Tag only the matching entries
{ "$project" => {
"prerequisites" => 1,
"matching" => { "$or" => [
{ "$eq" => [ "$prerequisites", "a" ] },
{ "$eq" => [ "$prerequisites", "b" ] },
]}
}},
// Group back to the course _id
{ "$group" => {
"_id" => "$_id",
"prerequisites" => { "$push" => "$prerequisites" },
"matching" => { "$min" => "$matching" }
}},
// Match only the true values (all prerequisites met)
{ "$match" => { "matching" => true } },
// Project only the wanted fields
{ "$project" => { "prerequisites" => 1 } }
])
所以“courses_taken”的每个元素都被添加到$in
运算符中,因此只有包含某些内容的课程才会匹配。但是,这当然不能完全过滤学生必须满足所有先决条件课程的条件,这里的要点是将文档数量减少到可能匹配的数量。
数组解开后,每个值都可以进行比较。这就是$project
通过从数组元素构建语句来测试是否找到该值。所以在$or
条件下,任何不匹配的东西都会返回false
作为该值。
在后面的$group
阶段,由于文档被重新放回原来的形式,该“匹配”测试的值将被存储在文档中。这意味着,如果prerequisites数组中的任何元素被视为匹配,则整个文档的值将被视为false
。
下一个$match
用于过滤任何课程,因此会包含课程先决条件,而该课程先决条件与用于输入的学生所采用的课程不匹配。所以现在只留下可以采取的课程,最后$project
只是删除“匹配”字段(通过省略),以便文档现在处于原始形式。
如果你确实有MongoDB版本2。6(刚刚发布了写作的)或向上,则是聚集新的运营商,使更简单的声明:
Course.collection.aggregate([
{ "$match" => {
"prerequisites" => { "$in" => [ "a", "b" ] }
}},
{ "$project" => {
"prerequisites" => 1,
"diff" => { "$size" => {"$setDifference" => [
"$prerequisites",
[ "a", "b" ]
]}}
}},
{ "$match" => { "diff" => 0 } },
{ "$project" => { "prerequisites" => 1 } }
])
因此,这使得使用新的运营商对于$setDifference
它可以直接比较阵列发现不在集合中的元素,以及使用$size
将返回测试数组的长度。由于任何包含不在课程中的学员要素的课程将返回这些元素作为* $setDifference
的结果,那么可以从整体结果中排除除0
之外的“大小”的任何结果。
除了简单得多,有一些速度优势外,还可以通过将学生的课程数组直接传递到管道查询的构建中来避免生成的复杂性,而且不必乱用构建第一个例子中使用的“相等”测试语句。
但是,这给了你相当强大的方式来做这种匹配,而不诉诸循环结果的代码。它还指出,聚合框架的使用不仅仅是对结果进行分组,而是一个非常强大的查询工具。
这实际上并未显示您的数据或模型的布局。没有人知道如何构建您的查询。请在你的问题中包含这些细节。 –
好的,我已添加更多详细信息 –