简体   繁体   中英

Convert mongo query to aggregate query?

I am a newbie to MongoDB. I have a use case where the mongo query should be converted to aggregate query .

I have the following two collections:

items: {_id: "something", "name": "raj", "branch": "IT", subItems: ["subItemId","subItemId"]}

subItems: {_id: "something", "city": "hyd", list: ["adsf","adsf"]}

Query passed by the user is:

{
    "query": { "name": "raj", "subItems.loc": "hyd" },
    "sort": { "name": 1 },
    "projection": { "_id": 0, "branch": 1, "subItems.loc": 1 }
}

I am able to create a dynamic query in a following way:

        let itemConditions: any[] = [];
        let subItemConditions: any[] = [];
        let itemProjection: {[k:string]: any} = {};
        let subItemProjection: {[k:string]: any} = {};
        subItemConditions.push({$in: ["$_id", "$$subItems"]}); // Default condition to search files in the subItems collection
        let isAnysubItemProj = false;
        Object.keys(reqQuery).forEach(prop => {
            let value = reqQuery[prop];
            if(prop.includes('subItems.')) {
                key = prop.split(".")[1];
                if(key === '_id') value = new ObjectId(value);
                subItemConditions.push({$eq: [`$${prop}`, value]});
                return;
            }                
            itemConditions.push({$eq: [`$${prop}`, value]});
        });
        if(config.projection)
            Object.keys(config.projection).forEach(prop => {
                if(prop.includes('subItems.')) {
                    isAnysubItemProj = true;
                    const key = prop.split(".")[1];
                    subItemProjection[key] = config.projection[prop];
                    return;
                }
                itemProjection[prop] = config.projection[prop];
            });
        if(isAnysubItemProj) itemProjection['subItems'] = 1;
        let subItemPipeline: any[] = [];
        subItemPipeline.push(
            { $match: {
                $expr: {
                    $and: subItemConditions
                }
            }
        });
        if(Object.keys(subItemProjection).length)
            subItemPipeline.push({$project: subItemProjection});
        let query: any[] = [
            { 
                $match: {
                    $expr : {
                        $and: itemConditions
                    }
                }
            },
            {
                $addFields: {
                    subItems: {
                        $map: {
                            input: "$subItems",
                            as: "id",
                            in: { $toObjectId: "$$id" }
                        }
                    }
                }
            },
            {
                $lookup: {
                    from: "subItems",
                    let: {subItems: "$subItems"},
                    pipeline: subItemPipeline,
                    as: "subItems"
                }
            }
        ];
        if(config.sort && Object.keys(config.sort).length) query.push({$sort: config.sort});
        if(Object.keys(itemProjection).length) query.push({$project: itemProjection});
        const items = await collection.aggregate(query).toArray();

The above code will work only for the comparison of equality for items and subItems separately, but the user can send different types of queries like:

{
  "query": { $or: [{"name": "raj"}, {subItems: {$gt: { $size: 3}}}], "subItems.city": "hyd" },     
  "sort": { "name": 1 },     
  "projection": { "_id": 0, "branch": 1, "subItems.loc": 1 } 
}

{
  "query": { $or: [{"name": "raj"}, {"subItems.city": {"$in" : ["hyd", "ncr"]}}], "subItems.list": {"$size": 2} },     
  "sort": { "name": 1 },     
  "projection": { "_id": 0, "branch": 1, "subItems.loc": 1 } 
}

Is there any easy way to convert this normal MongoDB query into an aggregate query or is there any other approach to implement this...??

I am trying to modify the above dynamic query to work for any queries passed by the user but it is becoming difficult to handle all queries.

Is there any better approach to handle this situation like changing the query passed by the user or the way of handling it on the server-side or how I should change my code to support all types of queries passed by the user..??

Any help will be appreciated

If this is really your input

input = {
    "query": { "name": "raj", "subItems.loc": "hyd" },
    "sort": { "name": 1 },
    "projection": { "_id": 0, "branch": 1, "subItems.loc": 1 }
}

Then the aggregation pipeline would be simply:

let pipeline = [
   {$match: input.query},
   {$sort: input.sort},
   {$project: input.projection}
]

db.collection.aggregate(pipeline)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM