torbenrudgaard
torbenrudgaard

Reputation: 2551

Building dynamic $match: for MongoDB aggregates

I am trying to build a dynamic $match: for my MongoDB aggregate

This is what I wish I could do:

var matchStr = "";
if (req.body.propertyID) {
    matchStr += "property: { $in: properties },"
}
if (req.body.status=="ACTIVE" || req.body.status=="NOPAY") {
    matchStr += "status : {$lte: 4},"
} else if (req.body.status) {
    matchStr += "status : {$lte: req.body.status},"
}
matchStr += "checkin : {$gte: (checkin)}, checkout: {$lte: (checkout)}"

And this is what I end up doing:

Its so messy, and now I have to add even more statements to the $match which will give me even bigger if structures.

There has to be a better way to do this!

I hope someone out there has an idea how to build these dynamically without the "if hell :)"

if (req.body.propertyID & req.body.status=="ALL") {
    var matchStr = { 
        $match: {
            $and: 
            [{ 
                property: { $in: properties },
                checkin : {$gte: (checkin)}, 
                checkout: {$lte: (checkout)}    
            }]
        }
    }
} else if (!req.body.propertyID & !req.body.status) {
    var matchStr = { 
        $match: {
            $and: 
            [{ 
                checkin : {$gte: (checkin)}, 
                checkout: {$lte: (checkout)}    
            }]
        }
    }
} else if ((req.body.status=="ACTIVE" || req.body.status=="NOPAY")) {
    if (req.body.propertyID) { 
        var matchStr = { 
            $match: {
                $and: 
                [{ 
                    property: { $in: properties },
                    status : {$lte: 4}, 
                    checkin : {$gte: (checkin)}, 
                    checkout: {$lte: (checkout)}    
                }]
            }
        }
    } else {
        var matchStr = { 
            $match: {
                $and: 
                [{ 
                    status : {$lte: 4}, 
                    checkin : {$gte: (checkin)}, 
                    checkout: {$lte: (checkout)}    
                }]
            }
        }
    }
} else {
    if (req.body.propertyID) { 
        var matchStr = { 
            $match: {
                $and: 
                [{ 
                    property: { $in: properties },
                    status : {$lte: req.body.status}, 
                    checkin : {$gte: (checkin)}, 
                    checkout: {$lte: (checkout)}    
                }]
            }
        }
    } else {
        var matchStr = { 
            $match: {
                $and: 
                [{ 
                    status : {$lte: req.body.status}, 
                    checkin : {$gte: (checkin)}, 
                    checkout: {$lte: (checkout)}    
                }]
            }
        }
    }
}

UPDATE! - Almost got it now, except it puts the "myMatch" inside the mongoDB statement.

Here is the new code:

var myMatch = {
    checkin: {$gte: checkin},
    checkout: {$lte: checkout}
};

if (req.body.hasOwnProperty('propertyID')) {
    var properties = req.body.propertyID.map(function (obj) {
        return obj._id;
    });
    console.log("properties: " + properties); 
    myMatch["property"] = { "$in": properties };
}
if (req.body.status=="ACTIVE" || req.body.status=="NOPAY") {
    myMatch["status"] = { "$lte": 4 };
} else if (req.body.hasOwnProperty('status')) {
    myMatch["status"] = { "$lte": +req.body.status };
}

And here is a console log of that structure:

myMatch: {
    "checkin": {
        "$gte": 1473571600
    },
    "checkout": {
        "$lte": 1596163600
    },
    "property": {
        "$in": [
            "PAP-720",
            "ATL-D406",
            "WAT-606"
        ]
    },
    "status": {
        "$lte": 3
    }
}

Here is where I inject the myMatch object:

    bookingTable.aggregate([
        {
            $match: {
                $and: 
                [{ 
                    myMatch
                }]
            }
        },

And here is the final mongodb statement:

Mongoose: 
booking.aggregate([ 
{ '$match': { '$and': [ { myMatch: { checkin: { '$gte': 1473571600 }, checkout: { '$lte': 1596163600 }, property: { '$in': [ 'PAP-720', 'ATL-D406', 'WAT-606' ] }, status: { '$lte': 3 } } } ] } },

Upvotes: 3

Views: 4713

Answers (1)

Neil Lunn
Neil Lunn

Reputation: 151112

First thing you really need to note is that you almost never need to write an explicit $and statements. It has a usage, but typically all expressions in a MongoDB query are already an "AND" operation.

The next thing is that the solution will look remarkably familiar:

var myMatch = {
    checkin: checkin,
    checkout: checkout
};

if (req.body.propertyID) {
    myMatch["property"] = { "$in": properties };
}
if (req.body.status=="ACTIVE" || req.body.status=="NOPAY") {
    myMatch["status"] = { "$lte": 4 };
} else if (req.body.hasOwnProperty('status')) {
    myMatch["status"] = { "$lte": +req.body.status };
}

The property names are pretty normal so you could alternately do:

    myMatch.property = { "$in": properties };

But this all comes down to basic manipulation of JavaScript objects. And it's pretty much the same in any dynamically typed language.

Then you can just do later:

{ "$match": myMatch }

And its done.

Upvotes: 3

Related Questions