#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. Спасибо, это разбудило