/ / Consulta para filtrar múltiples elementos de la matriz en resultados: mongodb, mongodb-query, aggregation-framework

Consulta para filtrar múltiples elementos de la matriz en los resultados - mongodb, mongodb-query, aggregation-framework

Tengo este funcionando si quiero consultar los atributos del empleado especificando una sola identificación de correo electrónico.

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

Digamos si quiero consultar especificando másde una ID de correo electrónico para obtener atributos de varios empleados. Lo leí, tiene algo que ver con $ u operator, pero no estoy seguro de cómo exponerlo ...

Mis datos mongoDB según el siguiente ejemplo:

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

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

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

}
]
}

Respuestas

1 para la respuesta № 1

El filtrado de matriz solo puede hacerlo marco de agregación. Permite una mayor manipulación del documento de la que está disponible por proyección básica.

Sin embargo, al igual que cualquier consulta, siempre debe usar un $match tubería primero para hacer uso de en el índice siempre que sea posible. independientemente de qué otras operaciones se realicen después:

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

Eso explica el proceso básico. Después de encontrar los "documentos" que coinciden con sus condiciones, utiliza $unwind para que cada elemento de la matriz sea un documento propio, compartiendo los campos principales. $match está ahí para "filtrar" esos elementos en los resultados. Cuando el $group hecho, entonces solo los elementos coincidentes se vuelven a colocar en la matriz.

Con MongoDB 2.6 puede hacer esto de una manera diferente que debería funcionar mejor con matrices más grandes. Hay nuevos operadores como $map para procesar una matriz "en línea" sin usar $unwind. También hay otras opciones de filtrado "establecidas" como $setDifference. Entonces puede hacer esto donde sus documentos siempre contienen valores únicos de "correo electrónico" en su propia matriz:

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

Aparte de los nuevos operadores mencionados anteriormente, el $cond El operador se utiliza aquí para evaluar cada elemento de la matriz que se pasa $map para ver si cumple con las condiciones. Si es así, el elemento se devuelve en la matriz resultante, de lo contrario, el elemento es false.

los $setDifference operador luego "filtra" cualquier false los valores del "conjunto" devueltos, como sería cualquier duplicado, por lo que los elementos de la matriz tendrían que ser únicos en cada documento como se mencionó.

Para elementos "no únicos", siempre existe esta alternativa al primer método en las versiones modernas:

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

Esto usa $redact de forma ligeramente artificial para eliminar los elementos de la matriz de los documentos que no coinciden con la condición. El problema aquí es que $redact es recursivo, de modo que es por eso que probamos la presencia del campo probado y donde no existe simplemente devolvemos un valor para que coincida. Realmente solo necesita uno $ifNull declaración allí en realidad.

Esencialmente, cualquier enfoque que elija, es el marco de agregación que tiene la manipulación "mejorada" de documentos que hace más de lo que puede hacer la proyección básica.


0 para la respuesta № 2

Si he entendido lo que querías decir, creo que la solución para ti es $in operador, y creo que debe poner su condición en el parámetro selector, no en el de proyección, si he entendido sus necesidades. Entonces su consulta sería algo como:

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

0 para la respuesta № 3

Operador de proyección $ elemMatch solo devuelve un elemento de la matriz, así que creo que puedes considerar agregar, me gusta

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