I have a match expression in a mongodb aggregation. There are 3 fields that are included in the match but they don't all always contain data. I only want to include the fields in the match if the field isn't empty.
This is what the match looks like if all fields have data but for example, if the array used for studentGradeLevels
is empty, then I don't want to include it or I want the query to still return data ignoring the empty parameter.
$match: {
"school._id": "7011",
"studentGradeLevels": { $in: ["09", "10", "11", "12"] },
"contentArea": {
$in: [
"English 1"
]
}
}
Is there a way to either dynamically build the match so that I only include the fields I want based on if they are empty or not or do something in the query so that parameters that are empty are ignored.
You can use $in
when the array is not empty and $nin
when the array is empty, this way the match field will not be taken into account ($nin : []
):
function buildMatch(arr) {
var matcher = {};
if (arr.length == 0)
matcher["$nin"] = arr;
else
matcher["$in"] = arr;
return matcher;
}
var grades = ["09", "10", "11", "12"];
var areas = [ "English 2" ];
var gradeMatch = buildMatch(grades);
var areaMatch = buildMatch(areas);
db.students.aggregate([{
$match: {
"school._id": "7011",
"studentGradeLevels": gradeMatch,
"contentArea": areaMatch
}
}])
A variation: Check fields for existence and if so, change to a special value e.g. _
and add that to the $in
list:
db.foo.aggregate([
{$project: {"studentGradeLevels":{$ifNull: ["$studentGradeLevels","_"]},
"contentArea":{$ifNull: ["$contentArea","_"]}
}}
,{$match: {
"studentGradeLevels": { $in: ["_", "09", "10", "11", "12"] },
"contentArea": { $in: [ "_", "English 1" ] }
}}
]);