insertar - relacionar dos colecciones en mongodb
Cómo usar la agregación MongoDB para operaciones de conjunto de propósito general(unión, intersección, diferencia) (2)
Me he encontrado con una implementación de propósito especial de operaciones de conjunto, pero nada para el caso general. ¿Cuál es el caso general para realizar operaciones de conjunto (específicamente intersección, unión, diferencia simétrica). Esto es más fácil de entender usando javascript en un $ donde o un mapa reducido, pero quiero saber cómo hacer esto en agregación para obtener un rendimiento nativo.
La mejor manera de ilustrar esta pregunta es con un ejemplo. Digamos que tengo un registro con 2 matrices / conjuntos:
db.colors.insert({
_id: 1,
left : [''red'', ''green''],
right : [''green'', ''blue'']
});
Quiero encontrar la unión, intersección y diferencia de las matrices ''izquierda'' y ''derecha''. Aún mejor, pictóricamente quiero encontrar:
Unión -> [''rojo'', ''verde'', ''azul'']
Intersección -> [''verde'']
Diferencia simétrica -> [''rojo'', ''azul'']
El más fácil de estos tres que usan agregación es la intersección **. El caso general de eso se puede hacer usando la agregación de esta manera:
Intersección:
db.colors.aggregate([
{''$unwind'' : "$left"},
{''$unwind'' : "$right"},
{''$project'': {
value:"$left",
same:{$cond:[{$eq:["$left","$right"]}, 1, 0]}
}
},
{''$group'' : {
_id: {id:''$_id'', val:''$value''},
doesMatch:{$max:"$same"}
}
},
{''$match'' :{doesMatch:1}},
]);
Los otros dos se vuelven un poco más difíciles. Que yo sepa, no hay una manera de combinar dos campos separados en el mismo documento. Sería bueno tener $ add, $ combine, o $ addToSet en la fase de proyecto $ project, pero esto no existe. Entonces lo mejor que podemos hacer es decir si algo se ha cruzado o no. Podemos comenzar ambas agregaciones con lo siguiente:
db.colors.aggregate([
{''$unwind'' : "$left"},
{''$unwind'' : "$right"},
{''$project'': {
left:"$left",
right:''$right'',
same:{$cond:[{$eq:["$left","$right"]}, 1, 0]}
}
},
{''$group'' : {
_id:{id:''$_id'', left:''$left''},
right:{''$addToSet'':''$right''},
sum: {''$sum'':''$same''},
}
},
{''$project'': {
left:{val:"$_id.left",inter:"$sum"},
right:''$right'',
}
},
{''$unwind'' : "$right"},
{''$project'': {
left:"$left",
right:''$right'',
same:{$cond:[{$eq:["$left.val","$right"]}, 1, 0]}
}
},
{''$group'' : {
_id:{id:''$_id.id'', right:''$right''},
left:{''$addToSet'':''$left''},
sum: {''$sum'':''$same''},
}
},
{''$project'': {
right:{val:"$_id.right",inter:"$sum"},
left:''$left'',
}
},
{''$unwind'' : "$left"},
{''$group'' : {
_id:''$_id.id'',
left:{''$addToSet'':''$left''},
right: {''$addToSet'':''$right''},
}
},
]);
Esta agregación en la muestra proporcionada en la pregunta arrojará un resultado como este:
{
"_id" : 1,
"left" : [
{
"val" : "green",
"inter" : 1
},
{
"val" : "red",
"inter" : 0
}
],
"right" : [
{
"val" : "blue",
"inter" : 0
},
{
"val" : "green",
"inter" : 1
}
]
}
Desde aquí podemos obtener la intersección agregando lo siguiente a la agregación:
{''$project'': {
left:"$left"
}
},
{''$unwind'' : "$left"},
{''$match'' : {''left.inter'': 1}},
{''$group'' : {
_id:''$_id'',
left:{''$addToSet'':''$left''},
}
},
Podemos encontrar la diferencia y el complemento relativo agregando lo siguiente al final de la agregación base:
{''$unwind'' : "$left"},
{''$match'' : {''left.inter'': 0}},
{''$unwind'' : "$right"},
{''$match'' : {''right.inter'': 0}},
{''$group'' : {
_id:''$_id'',
left:{''$addToSet'':''$left''},
right:{''$addToSet'':''$right''},
}
},
Desafortunadamente, no parece haber una buena forma de combinar elementos diferentes de diferentes campos. Para obtener la unión, parece mejor hacer eso desde el cliente. O si desea filtrar, hágalo en cada conjunto individualmente.
Versión 2.6+ solamente:
A partir de la versión 2.6 de MongoDB, esto se ha vuelto mucho más fácil. Ahora puede hacer lo siguiente para resolver este problema:
Unión
db.colors.aggregate([
{''$project'': {
union:{$setUnion:["$left","$right"]}
}
}
]);
Intersección
db.colors.aggregate([
{''$project'': {
int:{$setIntersection:["$left","$right"]}
}
}
]);
Complemento relativo
db.colors.aggregate([
{''$project'': {
diff:{$setDifference:["$left","$right"]}
}
}
]);
Diferencia simétrica
db.colors.aggregate([
{''$project'': {
diff:{$setUnion:[{$setDifference:["$left","$right"]}, {$setDifference:["$right","$left"]}]}
}
}
]);
Nota: Hay un ticket que solicita que se agregue una diferencia simétrica como característica central en lugar de tener que hacer la unión de dos diferencias de conjunto.