使用Mongoid,如何查询不包含特定属性的对象?
具体来说,我正在寻找所有的课程对象,其中Course.prerequisite_courses不包括Student.courses_taken以外的任何课程。
例如:
Class Student
include Mongoid::Document
field :courses_taken, type: Array # an array of course IDs
end
Class Course
include Mongoid::Document
field :prerequisites, type: Array # an array of course IDs
end
student_1.courses_taken = [a, b]
course_1.prerequisites = [a]
course_2.prerequisites = [a, b]
course_3.prerequisites = [a, c]这样student_1就可以被course_1和course_2录取,而不是course_3
这两个物体是不相关的
请注意,在本例中,可能有数百个course.prerequisites和student.courses_taken,我打算让这只是我的查询中的几个链式方法之一。
是否有一种优雅(或至少相对便宜)的方法来处理蒙古查询?
发布于 2014-04-10 02:28:02
我确实有一个普遍的偏好,因为是在一个较低的水平上工作,并允许您充分利用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阶段,当文档被放回原来的形式时,“匹配”测试的$min值将存储在文档中。这意味着如果先决条件数组的任何元素被视为false匹配,那么整个文档的值将被视为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返回测试数组的长度。因为任何包含未在为学生所选课程数组中的prerequsite元素的课程将返回这些元素作为*$setDifference的结果,所以除0之外的任何结果都可以被排除在整个结果之外。
除了简单得多和具有一些速度优势之外,您还可以将课程数组从学生直接传递到管道查询的构建中,从而避免了生成中的复杂性,并且不必在第一个示例中使用的构造“相等”测试语句时乱搞。
但是这给了您相当强大的方法来完成这种匹配,而不需要在代码中循环结果。它还指出,聚合框架的使用不仅用于分组结果,而且是一个非常强大的查询工具。
https://stackoverflow.com/questions/22976221
复制相似问题