/ / Запит на фільтрацію декількох елементів з масиву в результатах - mongodb, mongodb-запит, агрегація-фреймворк

Запит для фільтрації декількох елементів з масиву в результатах - mongodb, mongodb-query, aggregation-framework

У мене це працює, якщо я хочу запитувати атрибути співробітника, вказавши єдиний ідентифікатор електронної пошти.

db.employee.find({},{
_id: 0,
employee: {
$elemMatch: {
email: "john@companyx.com"
}
}})

Скажімо, якщо я хочу запитувати, вказавши більшеніж один ідентифікатор електронної пошти, щоб отримати атрибути кількох службовців. Я прочитав, це має щось спільне з $ або оператором, але я не впевнений, як це викласти.

Мої дані mongoDB, як показано нижче:

{
"_id" : ObjectId("53dbb05fa976627439d43884"),
"employee" : [
{
"email" : "john@companyx.com",
"deptName" : "x",

},
{
"email" : "keen@companyx.com",
"deptName" : "y",

},
{
"email" : "hung@companyx.com",
"deptName" : "y",

}
]
}

Відповіді:

1 для відповіді № 1

Фільтрування масиву може здійснюватися тільки рамки агрегації. Це дозволяє більше маніпулювати документом, ніж доступно за базовою проекцією.

Як і будь-який запит, ви завжди повинні використовувати a $match спочатку, щоб використовувати індекс, де це можливо. незалежно від того, які інші операції виконуються після цього:

db.employee.aggregate([

//  Always match first to reduce results
{ "$match": {
"employee.email": { "$in": ["john@companyx.com", "keen@companyx.com"] }
}},

// Unwind to de-normalize the array elements as documents
{ "$unwind": "$employee" },

// Match to "filter" the array content
{ "$match": {
"employee.email": { "$in": ["john@companyx.com", "keen@companyx.com"] }
}},

// Group back to a document with the array
{ "$group": {
"_id": "$_id",
"employee": { "$push": "$employee" }
}},

// Optionally project to remove the "_id" field from results
{ "$project": {
"_id": 0,
"employee": 1
}}
])

Отже, це пояснює основний процес. Знайшовши «документи», які відповідають вашим умовам, ви використовуєте $unwind щоб ефективно зробити кожен елемент масиву документом на своєму, обмінюючись будь-якими батьківськими полями. Додаткові $match є "фільтрувати" ці елементи в результатах. Коли $group робиться, потім тільки відповідні елементи повертаються в масив.

За допомогою MongoDB 2.6 ви можете це зробити іншим способом, який повинен працювати краще з більшими масивами. Є нові оператори, такі як $map для обробки масиву "in-line" без використання $unwind. Є також інші "встановити" параметри фільтрації як $setDifference. Таким чином, ви можете зробити це там, коли ваші документи завжди містять унікальні значення "електронної пошти" у власному масиві:

db.employee.aggregate([

//  Always match first to reduce results
{ "$match": {
"employee.email": { "$in": ["john@companyx.com", "keen@companyx.com"] }
}},

// Project filtered array content "in-line"
{ "$project": {
"_id": 0,
"employee": {
"$setDifference": [
{ "$map": {
"input": "$employee",
"as": "el",
"in": {
"$cond": [
{ "$or": [
{ "$eq": [ "$$el.email", "john@companyx.com" ] },
{ "$eq": [ "$$el.email", "keen@companyx.com" ] }
]},
"$$el",
false
]
}
}},
[false]
]
}
}}
])

Окрім згаданих раніше нових операторів, $cond Тут використовується оператор для оцінки кожного елемента масиву, який пройшов через $map щоб побачити, чи відповідає він умовам. Якщо так, елемент повертається в отриманий масив, інакше елемент є false.

The $setDifference тоді оператор "фільтрує" будь-який false значення з "набору" повертаються, як і будь-які дублікати, тому елементи масиву повинні бути унікальними у кожному документі, як згадувалося.

Для "неповторних" елементів завжди існує ця альтернатива першому методу в сучасних версіях:

db.employee.aggregate([

//  Always match first to reduce results
{ "$match": {
"employee.email": { "$in": ["john@companyx.com", "keen@companyx.com"] }
}},

// Redact removes document levels that do not match the condition
{ "$redact": {
"$cond": [
{ "$or": [
{ "$eq": [
{ "$ifNull": [ "$email", "john@companyx.com" ] },
"john@companyx.com"
]},
{ "$eq": [
{ "$ifNull": [ "$email", "keen@companyx.com" ] },
"keen@companyx.com"
]}
]},
"$$DESCEND",
"$$PRUNE"
]
}}
])

Це використовує $redact дещо видуманим способом видалити елементи масиву з документів, які не відповідають умові. Улов тут такий $redact є рекурсивним, тому ми перевіряємо наявність тестованого поля і там, де його не існує, просто повертаємо значення на збіг. Дійсно потрібен лише один $ifNull заява там насправді.

По суті, незалежно від обраного вами підходу рамки агрегації що має "супутню" маніпуляцію з документами, яка робить більше, ніж основна проекція.


0 для відповіді № 2

Якщо я зрозумів, що ви мали на увазі, я вважаю, що рішення для вас - це $in оператору, і я думаю, що вам слід поставити свій стан у параметрі селектора, а не в проекційному, якщо я зрозумів ваші потреби. Отже, ваш запит буде приблизно таким:

db.employee.find({email:{$in:["email1@foo.bar", "email2@bar.foo"]}},{_id:0})

0 для відповіді № 3

Оператор проекції $ elemMatch повертає лише один елемент із масиву, тому, думаю, ви можете розглянути сукупність, люблю

var emails = [ "john@companyx.com", "hung@companyx.com" ];
var match = {
$match : {
"employee.email" : {
$in : emails
}
}
};

db.employee.aggregate([ match, {
$unwind : "$employee"
}, match, {
$group : {
_id : "$_id",
employee : {
$push : "$employee"
}
}
}, {
$project : {
_id : 0,
employee : 1
}
} ]);