我的收藏中有这个文档结构:
{
"grades": [
{ "grade": "A", "score": 8 },
{"grade": "A", "score": 7 }
]
}
我需要过滤那些文档,当它们的
score
除以 7 时有 0 作为提醒。原始问题在here提出(问题 30)。他们的解决方案很简单:
db.restaurants.find(
{"grades.score" :
{$mod : [7,0]}
},
{"restaurant_id" : 1,"name":1,"grades":1}
);
但是,为了练习,我想采用自己的解决方案,但行不通。
db.restaurants.aggregate([
{
"$match": {
"$expr": {"$in": [0, [ { $mod: [ "$grades.score", 7 ] } ] ]}
}
},
])
错误是:
PlanExecutor error during aggregation :: caused by :: $mod only supports numeric types, not array and int
基本上我想检查 0 是否在自定义数组中,该数组在模运算后仅包含单个值,如用于聚合时 $in 的文档中所述。
有没有办法让
$mod
作为聚合的一部分工作?
我看到类似的问题被问到here,但它仅引用成绩数组中的第一个分数,而不是全部。因此,就我而言,我应该看到开头给出的示例文档,因为
7 % 7 = 0
$map
函数将数组的每个值映射到模结果,然后检查结果数组中是否有 0。
您可以使用其中之一:
db.restaurants.aggregate([
{
$set: {
mods: {
"$map": {
"input": "$grades.score",
"as": "score",
"in": {
$mod: ["$$score", 7]
}
}
}
}
},
{
"$match": {
"$expr": {
"$in": [0, "$mods"]
}
}
}
])
如果您希望将其作为管道中的单个阶段:
db.restaurants.aggregate([
{
"$match": {
"$expr": {
"$in": [
0,
{
"$map": {
"input": "$grades.score",
"as": "score",
"in": {
$mod: ["$$score", 7]
}
}
}
]
}
}
}
])
$reduce
的替代方案,当找到 0
模时,它可能会跳过一些计算,但仍然会迭代整个数组。在这种情况下,由于模计算非常有效,因此它不太可能真正带来任何真正的好处,除非您有非常非常很长的成绩列表。
用于比较的初始值是 $maxKey,它始终大于 任何其他值。只要您的列表不为空就可以。
db.restaurants.aggregate([
{
$set: {
min_modulo: {
$reduce: {
input: "$grades.score",
initialValue: { "$maxKey": 1 },
in: {
$cond: {
if: { $eq: [ "$$value", 0] },
// in theory, once we have a 0
// don't calculate mods or the minimums any more
then: "$$value",
else: { $min: [{ $mod: ["$$this", 7] }, "$$value"] }
}
}
}
}
}
},
{ $match: { min_modulo: 0 } }
])