Unir múltiples condiciones usando el operador Lookup en MongoDB
-
Unir múltiples condiciones usando el operador
$lookup
en MongoDB -
Cree una nueva colección y use la etapa de agregación
$group
para unir múltiples condiciones
Hoy veremos cómo unir múltiples condiciones usando el operador $lookup
en MongoDB. Además, también exploraremos algunos ejemplos que demuestran el uso de la etapa $group
y la etapa de agregación $unionWidth
.
Unir múltiples condiciones usando el operador $lookup
en MongoDB
Si tenemos MongoDB 3.6 o superior, podemos usar el operador de agregación $lookup
pipeline
para unir múltiples condiciones.
Para ello disponemos de dos colecciones denominadas users
y salaries
. También puede crear eso usando los siguientes comandos.
Código de ejemplo para crear colecciones:
> db.createCollection('users')
> db.createCollection('salaries')
Ejemplo de Código para Insertar Documentos en la Colección users
:
> db.users.insertMany(
[
{
username: 'userone',
age: 30,
gender: 'Female',
city: 'Lahore',
country: 'Pakistan'
},
{
username: 'usertwo',
age: 35,
gender: 'Male',
city: 'Florida',
country: 'United States'
}
]
)
Ejemplo de Código para Inserción de Documentos en el Cobro salaries
:
> db.salaries.insertMany(
[
{
username: 'userone',
salary: 3000
},
{
username: 'usertwo',
salary: 5000
}
]
)
Mostrar Datos de la Colección users
:
> db.users.find().pretty()
Producción :
{
"_id" : ObjectId("628deb40c1e812eeeb311439"),
"username" : "userone",
"age" : 30,
"gender" : "Female",
"city" : "Lahore",
"country" : "Pakistan"
}
{
"_id" : ObjectId("628deb40c1e812eeeb31143a"),
"username" : "usertwo",
"age" : 35,
"gender" : "Male",
"city" : "Florida",
"country" : "United States"
}
Mostrar Datos de la Colección salaries
:
> db.salaries.find().pretty()
Producción :
{
"_id" : ObjectId("628deb07c1e812eeeb311437"),
"username" : "userone",
"salary" : 3000
}
{
"_id" : ObjectId("628deb07c1e812eeeb311438"),
"username" : "usertwo",
"salary" : 5000
}
Después de crear las colecciones e insertar documentos, podemos explorar varios escenarios para unir múltiples condiciones. Empecemos con $lookup
.
Use el operador de agregación $lookup
pipeline
Código de ejemplo:
> db.users.aggregate([
{
$lookup: {
from: 'salaries',
let: {
user_name: '$username',
user_salary: 3000
},
pipeline: [{
$match: {
$expr: {
$and: [
{ $eq: ['$username', '$$user_name'] },
{ $gte: ['$salary','$$user_salary'] }
]
}
}
}],
as: 'usersalary'
}
}
]).pretty()
Producción :
{
"_id" : ObjectId("628deb40c1e812eeeb311439"),
"username" : "userone",
"age" : 30,
"gender" : "Female",
"city" : "Lahore",
"country" : "Pakistan",
"usersalary" : [
{
"_id" : ObjectId("628deb07c1e812eeeb311437"),
"username" : "userone",
"salary" : 3000
}
]
}
{
"_id" : ObjectId("628deb40c1e812eeeb31143a"),
"username" : "usertwo",
"age" : 35,
"gender" : "Male",
"city" : "Florida",
"country" : "United States",
"usersalary" : [
{
"_id" : ObjectId("628deb07c1e812eeeb311438"),
"username" : "usertwo",
"salary" : 5000
}
]
}
Aquí, obtenemos los documentos que cumplen dos condiciones.
- El campo
nombre de usuario
es el mismo en las coleccionesusers
ysalaries
. - El valor del campo
salario
es mayor o igual a3000
.
Solo obtenemos el documento que cumple ambas condiciones. Es posible que haya notado que el campo usersalary
es visible como una matriz de elementos donde cada elemento es un documento de la colección salaries
.
Podemos usar $unwind
, $addFields
y $project
para obtener los campos específicos de ambas colecciones (users
y salaries
) y formar un documento, como se demuestra en el siguiente ejemplo.
Código de ejemplo:
> db.users.aggregate([
{
$lookup: {
from: 'salaries',
let: {
user_name: '$username',
user_salary: 3000
},
pipeline: [{
$match: {
$expr: {
$and: [
{ $eq: ['$username', '$$user_name'] },
{ $gte: ['$salary','$$user_salary'] }
]
}
}
}],
as: 'usersalary'
}
},
{
$unwind:'$usersalary'
},
{
$addFields: {
salary: '$usersalary.salary'
}
},
{
$project: {
username: 1,
salary: 1
}
}
]).pretty()
Producción :
{
"_id" : ObjectId("628deb40c1e812eeeb311439"),
"username" : "userone",
"salary" : 3000
}
{
"_id" : ObjectId("628deb40c1e812eeeb31143a"),
"username" : "usertwo",
"salary" : 5000
}
El propósito de utilizar el operador $unwind
es deconstruir un campo de matriz desde los documentos de entrada hasta el documento de salida para cada elemento con el mismo nombre.
Si solo hay un elemento en la matriz, entonces el operador de etapa $unwind
aplana el objeto, que es el elemento mismo. El $addFields
une el campo salary
de un objeto o matriz al nivel raíz del documento.
Centrémonos en la razón para usar la etapa de filtro $project
antes de entender su uso en el ejemplo anterior. Si no usamos el $project
, obtendremos el campo salario
en el nivel raíz del documento y el objeto usersalary
, que no es necesario.
Aquí es donde usamos la etapa de filtro $project
y especificamos qué campos deben estar en la salida.
Podemos usar la solución alternativa que se proporciona a continuación si los requisitos del proyecto restringen el uso de $unwind
, $addFields
, $project
.
Código de ejemplo:
> db.users.aggregate([
{
$lookup: {
from: 'salaries',
let: {
user_name: '$username',
user_salary: 3000
},
pipeline: [{
$match: {
$expr: {
$and: [
{ $eq: ['$username', '$$user_name'] },
{ $gte: ['$salary','$$user_salary'] }
]
}
}
}],
as: 'usersalary'
}
},
{
$replaceRoot: {
newRoot: {
$mergeObjects:[
{
$arrayElemAt: [
"$usersalary", 0
]
},
{
salary: "$$ROOT.salary"
}
]
}
}
}
]
).pretty()
Producción :
{
"_id" : ObjectId("628deb07c1e812eeeb311437"),
"username" : "userone",
"salary" : 3000
}
{
"_id" : ObjectId("628deb07c1e812eeeb311438"),
"username" : "usertwo",
"salary" : 5000
}
Usamos el campo let
(opcional) para asignar los valores de los campos a las variables. Accedemos a estas variables en la etapa de pipeline
, donde especificamos la pipeline
para que se ejecute en diferentes colecciones.
Tenga en cuenta que también estamos utilizando la etapa $match
para aprovechar el operador de consulta de evaluación llamado $expr
, que compara el valor de los campos.
Además, $replaceRoot
es la última etapa de agregación de la pipeline
en la que utilizamos el operador $mergeObjects
para fusionar la salida $lookup
con la parte del documento $$ROOT
.
Usamos solo el operador $and
para unir las condiciones. También puede usar $or
o ambos operadores.
Cree una nueva colección y use la etapa de agregación $group
para unir múltiples condiciones
Código de ejemplo:
> db.users_salaries.insertMany(
db.users.find({}, {"_id": 0})
.toArray()
.concat(db.salaries.find({}, {"_id": 0}).toArray())
)
db.users_salaries.aggregate([
{ "$group": {
"_id": { "username": "$username" },
"salary": { "$push": "$salary" }
}}
])
Producción :
{ "_id" : { "username" : "userone" }, "salary" : [ 3000 ] }
{ "_id" : { "username" : "usertwo" }, "salary" : [ 5000 ] }
Para este ejemplo de código, creamos una nueva colección llamada users_salaries
, fusionamos dos colecciones llamadas users
y salaries
, y luego insertamos esos documentos en la colección recién creada. Luego, agrupe por el nombre de usuario
para obtener el resultado deseado.
También podemos obtener el mismo resultado (como se indicó anteriormente) sin crear una nueva colección. Para eso, usamos la etapa de agregación $unionWith
, que realiza una unión para dos colecciones.
Código de ejemplo:
> db.users.aggregate([
{ $set: { username: "$username" } },
{ $unionWith: {
coll: "salaries",
pipeline: [{ $set: { salary: "$salary" } }]
}},
{ $group: {
_id: { username: "$username"},
"salary": { "$push": "$salary" }
}}
])
Producción :
{ "_id" : { "username" : "userone" }, "salary" : [ 3000 ] }
{ "_id" : { "username" : "usertwo" }, "salary" : [ 5000 ] }