node.js - Mongoose sobrescribe el documento en lugar de los campos `$ set`
mongodb (2)
Digamos que tengo un documento:
{
_id: ''some_mongodb_id'',
name: ''john doe'',
phone: ''+12345678901'',
}
Quiero actualizar este documento:
.findOneAndUpdate({_id: ''some_mongodb_id''}, {name: ''Dan smith''})
Y el resultado debería ser este:
{
_id: ''some_mongodb_id'',
name: ''Dan smith'',
}
La propiedad, que no está especificada, debe eliminarse.
¿Cómo puedo hacer eso?
En realidad, pero por el hecho de que la mangosta en realidad está "jugando" con la actualización bajo las cubiertas, esta es la acción predeterminada de su envío a una función MongoDB normal.
Entonces, mangosta considera "sabio" como un método conveniente para "presumir" que pretendía emitir una instrucción
$set
aquí.
Como en realidad no desea hacer eso en este caso, desactive ese comportamiento a través de
{ overwrite: true }
en las opciones pasadas a cualquier método
.update()
:
Como un ejemplo completo:
const mongoose = require(''mongoose''),
Schema = mongoose.Schema;
mongoose.Promise = global.Promise;
mongoose.set(''debug'',true);
const uri = ''mongodb://localhost/test'',
options = { useMongoClient: true };
const testSchema = new Schema({
name: String,
phone: String
});
const Test = mongoose.model(''Test'', testSchema);
function log(data) {
console.log(JSON.stringify(data,undefined,2))
}
(async function() {
try {
const conn = await mongoose.connect(uri,options);
// Clean data
await Promise.all(
Object.keys(conn.models).map( m => conn.models[m].remove({}) )
);
// Create a document
let test = await Test.create({
name: ''john doe'',
phone: ''+12345678901''
});
log(test);
// This update will apply using $set for the name
let notover = await Test.findOneAndUpdate(
{ _id: test._id },
{ name: ''Bill S. Preston'' },
{ new: true }
);
log(notover);
// This update will just use the supplied object, and overwrite
let updated = await Test.findOneAndUpdate(
{ _id: test._id },
{ name: ''Dan Smith'' },
{ new: true, overwrite: true }
);
log(updated);
} catch (e) {
console.error(e);
} finally {
mongoose.disconnect();
}
})()
Produce:
Mongoose: tests.remove({}, {})
Mongoose: tests.insert({ name: ''john doe'', phone: ''+12345678901'', _id: ObjectId("596efb0ec941ff0ec319ac1e"), __v: 0 })
{
"__v": 0,
"name": "john doe",
"phone": "+12345678901",
"_id": "596efb0ec941ff0ec319ac1e"
}
Mongoose: tests.findAndModify({ _id: ObjectId("596efb0ec941ff0ec319ac1e") }, [], { ''$set'': { name: ''Bill S. Preston'' } }, { new: true, upsert: false, remove: false, fields: {} })
{
"_id": "596efb0ec941ff0ec319ac1e",
"name": "Bill S. Preston",
"phone": "+12345678901",
"__v": 0
}
Mongoose: tests.findAndModify({ _id: ObjectId("596efb0ec941ff0ec319ac1e") }, [], { name: ''Dan Smith'' }, { new: true, overwrite: true, upsert: false, remove: false, fields: {} })
{
"_id": "596efb0ec941ff0ec319ac1e",
"name": "Dan Smith"
}
Mostrar el documento está "sobreescrito" porque suprimimos la operación
$set
que de otro modo habría sido interpolada.
Las dos muestras se muestran primero sin la opción de
overwrite
, que aplica el modificador
$set
, y luego "con" la opción de
overwrite
, donde se respeta el objeto que pasó para la "actualización" y no se aplica dicho modificador
$set
.
Tenga en cuenta que así es como el controlador del nodo MongoDB hace esto "por defecto".
Por lo tanto, el comportamiento de agregar en el conjunto "implícito"
$set
lo está haciendo la mangosta, a menos que usted le diga que no lo haga.
NOTA La verdadera forma de "reemplazar" en realidad sería usar
replaceOne
, ya sea como el método API dereplaceOne()
o mediantebulkWrite()
. Laoverwrite
es un legado de cómo Mongoose quiere aplicar$set
como se describió y demostró anteriormente, sin embargo, la API oficial de MongoDB presentareplaceOne
como un " rey especial" de la operaciónupdate()
que no permite el uso de operadores atómicos como$set
dentro de la declaración y se producirá un error si lo intentas.Esto es mucho más claro semánticamente ya que las lecturas de reemplazo son muy claras en cuanto a para qué se usa realmente el método. Dentro de las llamadas API estándar a las variantes de
update()
por supuesto, todavía le permite omitir los operadores atómicos y simplemente reemplazará el contenido de todos modos. Pero se deben esperar advertencias.
Puede pasar la opción
upsert
y reemplazará el documento:
var collection = db.collection(''test'');
collection.findOneAndUpdate(
{''_id'': ''some_mongodb_id''},
{name: ''Dan smith Only''},
{upsert: true},
function (err, doc) {
console.log(doc);
}
);
Pero el problema aquí es que el
doc
en devolución de llamada se encuentra documento pero no actualizado.
Por lo tanto, debe realizar algo como esto:
var collection = db.collection(''test'');
collection.update(
{''_id'': ''some_mongodb_id''},
{name: ''Dan smith Only''},
{upsert: true},
function (err, doc) {
collection.findOne({''_id'': ''some_mongodb_id''}, function (err, doc) {
console.log(doc);
});
}
);