【问题标题】:Convert mongo query to aggregate query?将mongo查询转换为聚合查询?
【发布时间】:2021-06-19 16:41:45
【问题描述】:

我是 MongoDB 的新手。我有一个用例,应该将 mongo 查询 转换为 聚合查询

我有以下两个收藏:

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

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

用户传递的查询是:

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

我可以通过以下方式创建动态查询:

        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();

上述代码仅适用于分别比较项目和子项目的相等性,但用户可以发送不同类型的查询,例如:

{
  "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 } 
}

是否有任何简单的方法可以将这个普通的 MongoDB 查询转换为聚合查询,或者有没有其他方法可以实现这个......??

我正在尝试修改上述动态查询以适用于用户传递的任何查询,但处理所有查询变得困难。

是否有更好的方法来处理这种情况,例如更改用户传递的查询或在服务器端处理它的方式,或者我应该如何更改我的代码以支持用户传递的所有类型的查询.. ??

任何帮助将不胜感激

【问题讨论】:

    标签: node.js mongodb mongodb-query aggregation-framework aggregation


    【解决方案1】:

    如果这真的是你的意见

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

    那么聚合管道就是:

    let pipeline = [
       {$match: input.query},
       {$sort: input.sort},
       {$project: input.projection}
    ]
    
    db.collection.aggregate(pipeline)
    

    【讨论】:

    • items 和 subItems 是两个独立的集合。并且输入查询每次都不相同,它可以是: { "query": { $or: [{"name": "raj"}, {subItems: {$gt: { $size: 3}} }],“subItems.city”:“hyd”},“排序”:{“名称”:1 },“投影”:{“_id”:0,“分支”: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 } }
    • 好吧,我错过了$lookup。但是,它应该适用于任何 query/sort/projection - 只要语法正确。
    • 但是普通查询(由用户传递)和聚合查询是不同的,所以我们需要将普通查询转换为聚合查询,例如 {"loc": "hyd"} 到 {$ eq: ["$loc", "hyd"]}。它变得越来越困难。关于这个问题的任何建议..?
    • Stage {$match: {loc: "hyd"}} 完美运行。没有理由将其转换为{ $match: { $expr: { $eq: ["$loc", "hyd"] } } }
    • 谢谢,它醒了
    猜你喜欢
    • 1970-01-01
    • 2017-08-29
    • 2021-09-03
    • 2022-01-17
    • 1970-01-01
    • 1970-01-01
    • 2023-03-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多