Преобразовать запрос mongo в агрегированный запрос?

#node.js #mongodb #mongodb-query #aggregation-framework #aggregation

Вопрос:

Я новичок в 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 amp;amp; 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 в агрегированный запрос или есть какой-либо другой подход для реализации этого…??

Я пытаюсь изменить приведенный выше динамический запрос, чтобы он работал для любых запросов, переданных пользователем, но становится трудно обрабатывать все запросы.

Есть ли какой-либо лучший подход для решения этой ситуации, например, изменение запроса, переданного пользователем, или способ его обработки на стороне сервера, или как я должен изменить свой код для поддержки всех типов запросов, переданных пользователем..??

Любая помощь будет оценена по достоинству

Ответ №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)
 

Комментарии:

1. элементы и подпункты представляют собой две отдельные коллекции. и входной запрос не каждый раз один и тот же, он может быть чем угодно, например: { «запрос»: { $или: [{«имя»: «радж»}, {подразделы: {$gt: { $размер: 3}}}], «Подразделы.город»: «hyd»}, «сортировка»: { «имя»: 1}, «проекция»: { «_id»: 0, «ветвь»: 1, «Подразделы.loc»: 1 } } { «запрос»: { $или: [{«имя»: «raj»}, {«Подразделы.город»: {«$in» : [«hyd», «ncr»]}}], «Подразделы.список»: {«$размер»: 2}}, «сортировка»: { «имя»: 1}, «проекция»: { «_id»: 0, «филиал»: 1, «Подразделы.loc»: 1 } }

2. Ладно, я пропустил $lookup это . Однако он должен работать с любым query/sort/projection — до тех пор, пока синтаксис правильный.

3. Но обычные запросы (которые передаются пользователем) и агрегированные запросы различаются, поэтому нам нужно преобразовать обычные запросы в агрегированный запрос, например {«loc»: «hyd»} в {$eq: [«$loc», «hyd»]}. Это становится все труднее. Есть какие-либо предложения по этому вопросу..?

4. Сцена {$match: {loc: "hyd"}} работает идеально. Нет никаких причин преобразовывать его в { $match: { $expr: { $eq: ["$loc", "hyd"] } } }

5. Спасибо, это разбудило