I have this document structure in my collection:
{
"grades": [
{ "grade": "A", "score": 8 },
{"grade": "A", "score": 7 }
]
}
I need to filter for those documents when their score
is divided by 7 has 0 as a reminder. Original question is asked here (question 30). Their solution is simple:
db.restaurants.find(
{"grades.score" :
{$mod : [7,0]}
},
{"restaurant_id" : 1,"name":1,"grades":1}
);
However, in order to practice, I wanted to to my own solution, which doesn’t work out.
db.restaurants.aggregate([
{
"$match": {
"$expr": {"$in": [0, [ { $mod: [ "$grades.score", 7 ] } ] ]}
}
},
])
The error is:
PlanExecutor error during aggregation :: caused by :: $mod only supports numeric types, not array and int
Basically I want to check if 0 is in custom made array which will contain only single value after modulo operation, as explained in the docs for $in when used in aggregation.
Is there a way to make $mod
work as part of aggregation?
I see that similar question is asked here, but it is only referencing first score in grades array not all of them. So in my case I should see example document given on the beginning, since 7 % 7 = 0
2
Answers
Use the
$map
function to map each value of the array to the modulo result, and then check for 0 being in the resulting array.You can use one of these:
Mongo Playground
And if you want that as a single stage within the pipeline:
Mongo Playground
Here’s an alternative with
$reduce
which may skip some calculations when a0
modulo is found but would still iterate over the whole array anyway. In this case, since the modulo calculation is very efficient, it’s unlikely to actually give any real benefit unless you had very very long lists of grades.Mongo Playground with more example data
Also I skipped the
{ $min: [{ $mod: ["$$this", 7] }, "$$value"] }
since we’re only checking for zero vs non-zero remainder. And in case scores are negative, the modulo minimum would be negative: