j-p
j-p

Reputation: 3818

Conditional filtering against a nested object in MongoDB

I am having a problem searching for a key of a nested object.

I have search criteria object that may or may not have certain fields I'd like to search on.

The way I'm solving this is to use conditional statements to append to a "match criteria" object that gets passed to the aggregate $match operator. it works well until I need to match to something inside a nested object.

Here is a sample document structure

{
        name: string,
        dates: {
            actived: Date,
            suspended: Date
        },
        address : [{
            street: string,
            city: string,
            state: string,
            zip: string
        }]

    };

My criteria object is populated thru a UI and passed a JSON that looks similar to this:

{
        "name": "",
        "state": ""
    }

And although I can explicitly use "dates.suspended" without issue - when I try to append address.state to my search match criteria - I get an error.

module.exports.search = function( criteria, callback ) 

        let matchCriteria = {
            "name": criteria.name,
            "dates.suspended": null
        };


        if ( criteria.state !== '' ) {
           // *** PROBLEM HAPPENS HERE *** //
            matchCriteria.address.state = criteria.state;
        }

        User.aggregate([
            { "$match": matchCriteria },
            { "$addFields": {...} },
            { "$project": {...} }
        ], callback );

    }

I get the error:

TypeError: Cannot set property 'state' of undefined

I understand that I'm specifying 'address.state' when 'address' doesn't exist yet - but I am unclear what my syntax would be surely it woulnd't be matchCriteria['address.state'] or "matchCriteria.address.state"

Is there a better way to do conditional filtering?

Upvotes: 0

Views: 671

Answers (3)

IftekharDani
IftekharDani

Reputation: 3729

For search in Nested Object, You have to use unwind

A query that help you :

//For testing declare criteria as const

 let  criteria = {name : 'name', 'state' : 'state'};
 let addressMatch = {};
 let matchCriteria = {
            "name": criteria.name,
            "dates.suspended": null
        };


        if ( criteria.state) {

            addressMatch = { 'address.state' :  criteria.state };
        }

db.getCollection('user').aggregate([{
    $match :matchCriteria,
    },{$unwind:'$address'},
    {$match :  addressMatch}
    ])

Upvotes: 2

scthi
scthi

Reputation: 2481

This should fix it:

matchCriteria['address.state'] = criteria.state;

Upvotes: 0

Sagar Chaudhary
Sagar Chaudhary

Reputation: 1393

Firstly check for address, and then access the property as shown:

if(matchCriteria['address']) {
 matchCriteria['address']['state'] = criteria['state'];
}
else {
//otherwise
}

Upvotes: 0

Related Questions