MongoDB tiene un $push
operador y un $addToSet
operador, los cuales hacen algo muy similar.
Ambos operadores agregan valores a una matriz existente. La principal diferencia está en la forma en que manejan las matrices que ya contienen el valor que está tratando de agregar, y también en los modificadores que se pueden usar.
Las diferencias
Valores existentes | Si el valor ya existe en la matriz, $push aún agregará el valor (lo que resultará en valores duplicados). Sin embargo, $addToSet solo agrega el valor si aún no existe en la matriz. Por lo tanto, si el valor ya existe, $addToSet no lo agregará (no hará nada). |
Modificadores | El $push El operador se puede usar con modificadores adicionales, como $sort , $slice y $position , mientras que $addToSet no puede (al menos, no a partir de MongoDB 4.4). |
Valores existentes
Supongamos que tenemos una colección con los siguientes documentos:
db.players.find()
Resultado:
{ "_id" : 1, "scores" : [ 1, 5, 3 ] } { "_id" : 2, "scores" : [ 8, 17, 18 ] } { "_id" : 3, "scores" : [ 3, 5, 5 ] }
Usemos $addToSet
para intentar agregar un valor a una de las matrices.
db.players.update(
{ _id: 3 },
{ $addToSet: { scores: 5 } }
)
Salida:
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 0 })
Esto nos dice que hubo un documento coincidente (documento 3), pero no fue modificado. No se modificó porque el valor que intentamos insertar (5
) ya existe en la matriz.
Miremos en la colección:
db.players.find()
Resultado:
{ "_id" : 1, "scores" : [ 1, 5, 3 ] } { "_id" : 2, "scores" : [ 8, 17, 18 ] } { "_id" : 3, "scores" : [ 3, 5, 5 ] }
Como era de esperar, el documento 3 no se ha modificado.
Intentemos $push
en cambio:
db.players.update(
{ _id: 3 },
{ $push: { scores: 5 } }
)
Salida:
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
Esta vez vemos que el documento fue modificado.
Podemos verificar esto revisando la colección nuevamente:
db.products.find()
Resultado:
{ "_id" : 1, "scores" : [ 1, 5, 3 ] } { "_id" : 2, "scores" : [ 8, 17, 18 ] } { "_id" : 3, "scores" : [ 3, 5, 5, 5 ] }
Modificadores
El $push
El operador se puede usar con modificadores como $position
, $sort
y $slice
.
El $addToSet
El operador no se puede usar con estos modificadores.
Esto es lo que sucede si trato de usar estos modificadores con $addToSet
:
db.players.update(
{ _id: 3 },
{
$addToSet: {
scores: {
$each: [ 12 ],
$position: 0,
$sort: 1,
$slice: 5
}
}
}
)
Salida:
WriteResult({ "nMatched" : 0, "nUpserted" : 0, "nModified" : 0, "writeError" : { "code" : 2, "errmsg" : "Found unexpected fields after $each in $addToSet: { $each: [ 12.0 ], $position: 0.0, $sort: 1.0, $slice: 5.0 }" } })
El mensaje de error nos dice que $position
, $sort
y $slice
son campos inesperados (es decir, no deberían estar ahí).
Probemos los mismos modificadores con $push
:
db.players.update(
{ _id: 3 },
{
$push: {
scores: {
$each: [ 12 ],
$position: 0,
$sort: 1,
$slice: 5
}
}
}
)
Salida:
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
Esta vez funcionó sin errores.
Verifique el resultado:
db.players.find()
Resultado:
{ "_id" : 1, "scores" : [ 1, 5, 3 ] } { "_id" : 2, "scores" : [ 8, 17, 18 ] } { "_id" : 3, "scores" : [ 3, 5, 5, 5, 12 ] }
Podemos ver que el valor ha sido añadido. Aunque especificamos $position: 0
, también especificamos $sort: 1
, lo que significa que la matriz se ordenó después de que la posicionáramos.
También especificamos $slice: 5
, que limitaba la matriz a solo 5 elementos (que resultó ser exactamente cuántos elementos había en la matriz de todos modos).