How to dynamically build mongodb query

2019-05-25 04:54发布

问题:

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.

回答1:

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
    }
}])


回答2:

A variation: Check fields for existence and if so, change to a special value e.g. _ and add that to the $inlist:

db.foo.aggregate([
{$project: {"studentGradeLevels":{$ifNull: ["$studentGradeLevels","_"]},
            "contentArea":{$ifNull: ["$contentArea","_"]}
}}
,{$match: {
        "studentGradeLevels": { $in: ["_", "09", "10", "11", "12"] },
        "contentArea": { $in: [ "_", "English 1" ]  }
}}
              ]);