How mongodb filters arrays in documents

Keywords: Database MongoDB

mongodb documents contain arrays. You need to filter the qualified data in the arrays and return the result set. You can query group s or filters in two ways.

Data source:
{
    "_id" : ObjectId("5bbcc0c9a74db9804e78a157"),
    "uid" : "1000001",
    "name" : "zhangsan",
    "addrs" : [ 
        {
            "is_query" : "1",
            "city" : "Beijing"
        }, 
        {
            "is_query" : "0",
            "city" : "Shanghai"
        }, 
        {
            "is_query" : "1",
            "city" : "Shenzhen"
        }
    ]
}
{
    "_id" : ObjectId("5bbcc167a74db9804e78a172"),
    "uid" : "1000002",
    "name" : "lisi",
    "addrs" : [ 
        {
            "is_query" : "0",
            "city" : "Beijing"
        }, 
        {
            "is_query" : "0",
            "city" : "Shanghai"
        }, 
        {
            "is_query" : "1",
            "city" : "Shenzhen"
        }
    ]
}

Under the uid specified by the query, the addrs array only contains the result set whose is query is equal to 1 (0 is not included).

Query statement:

Method 1: use $unwind to break up the addrs array, get the result set, use $match to filter the qualified data, and finally use $group to aggregate to get the final result set.

db.getCollection('user').aggregate(
    [
        {   
            $unwind: "$addrs" 
        },
        { 
            $match : {
                "uid":"1000001", 
                "addrs.is_query": "1" 
            } 
        },
        { 
            $group : { 
                "_id" : "$uid", 
                "addrs": { $push: "$addrs" } 
            } 
        } 
    ]
)

Result:

{
    "_id" : "1000001",
    "addrs" : [ 
        {
            "is_query" : "1",
            "city" : "Beijing"
        }, 
        {
            "is_query" : "1",
            "city" : "Shenzhen"
        }
    ]
}

Method 2: use $match to filter the result set of the qualified root document, and then use $project to return the corresponding fields. At the same time, use $filter in the addrs array for internal filtering to return the final result set

db.getCollection('user').aggregate(
    [
        { 
            $match : { "uid": "1000001" } 
        },
        {
            $project: {
                "uid": 1,
                "name": 1,
                "addrs": {
                    $filter: {
                        input: "$addrs",
                        as: "item",
                        cond: { $eq : ["$$item.is_query","1"] }
                    }
                }
            }
        }
    ]
)

Result:

{
    "_id" : ObjectId("5bbcc0c9a74db9804e78a157"),
    "uid" : "1000001",
    "name" : "zhangsan",
    "addrs" : [ 
        {
            "is_query" : "1",
            "city" : "Beijing"
        }, 
        {
            "is_query" : "1",
            "city" : "Shenzhen"
        }
    ]
}

Compared with the way of grouping and aggregating $group to return result set, $filter is more elegant and direct under the current query requirements. Of course, if statistics operations are included, such as the number of is_query equal to 1, then $group is very suitable.

Posted by Andrei on Sun, 15 Dec 2019 08:38:06 -0800