/ / Requête pour filtrer plusieurs éléments du tableau dans les résultats - mongodb, mongodb-query, framework d'agrégation

Requête pour filtrer plusieurs éléments du tableau dans les résultats - mongodb, mongodb-query, aggregation-framework

J'ai celui-ci qui fonctionne si je veux interroger les attributs de l'employé en spécifiant un identifiant de messagerie unique.

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

Disons si je veux interroger en spécifiant plusplusieurs identifiants de messagerie pour obtenir les attributs de plusieurs employés. J'ai lu, cela a quelque chose à voir avec $ ou opérateur, mais je ne sais pas comment l'exposer.

Mes données mongoDB selon l'exemple ci-dessous:

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

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

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

}
]
}

Réponses:

1 pour la réponse № 1

Le filtrage des tableaux ne peut être effectué que par cadre d'agrégation. Il permet une plus grande manipulation du document que celle disponible par projection de base.

Comme pour toute requête, vous devez toujours utiliser un $match pipeline en premier afin d'utiliser dans l'index dans la mesure du possible. quelles que soient les autres opérations effectuées par la suite:

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
}}
])

Cela explique donc le processus de base. Après avoir trouvé les "documents" qui correspondent à vos conditions, vous utilisez $unwind pour faire de chaque élément du tableau un document à part entière, en partageant tous les champs parents. $match est là pour "filtrer" ces éléments dans les résultats. Quand le $group est fait, alors seuls les éléments correspondants sont replacés dans le tableau.

Avec MongoDB 2.6, vous pouvez le faire d'une manière différente qui devrait mieux fonctionner avec des tableaux plus grands. Il y a de nouveaux opérateurs comme $map pour traiter un tableau "en ligne" sans utiliser $unwind. Il existe également d'autres options de filtrage "set" comme $setDifference. Ainsi, vous pouvez le faire lorsque vos documents contiennent toujours des valeurs "e-mail" uniques dans leur propre tableau:

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]
]
}
}}
])

Outre les nouveaux opérateurs mentionnés plus haut, le $cond L'opérateur est utilisé ici pour évaluer chaque élément du tableau passé $map pour voir s'il remplit les conditions. Si c'est le cas, l'élément est renvoyé dans le tableau résultant, sinon l'élément est false.

le $setDifference l'opérateur "filtre" tout false les valeurs de l '"ensemble" renvoyées, comme ce serait le cas pour les doublons, de sorte que les éléments du tableau devraient être uniques dans chaque document, comme mentionné.

Pour les éléments "non uniques", il existe toujours cette alternative à la première méthode dans les versions modernes:

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"
]
}}
])

Cela utilise $redact de manière légèrement artificielle pour supprimer les éléments du tableau des documents qui ne correspondent pas à la condition. Le hic ici, c'est que $redact est récursif, c'est pourquoi nous testons la présence du champ testé et lorsqu'il n'existe pas, il suffit de renvoyer une valeur à faire correspondre. Vraiment un seul besoin $ifNull déclaration là-dedans en fait.

Quelle que soit l’approche que vous choisissez, c’est cadre d'agrégation qui a la manipulation "gonflée" des documents qui fait plus que la projection de base.


0 pour la réponse № 2

Si j'ai compris ce que vous vouliez dire, je pense que la solution pour vous est la $in et je pense que vous devriez mettre votre condition dans le paramètre de sélection, pas dans celui de projection, si j'ai compris vos besoins. Votre requête serait donc quelque chose comme:

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

0 pour la réponse № 3

Opérateur de projection $ elemMatch ne renvoie qu'un élément du tableau, donc je pense que vous pouvez considérer agrégat, comme

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
}
} ]);