node.js mongodb mongoose

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 de replaceOne() o mediante bulkWrite() . La overwrite es un legado de cómo Mongoose quiere aplicar $set como se describió y demostró anteriormente, sin embargo, la API oficial de MongoDB presenta replaceOne como un " rey especial" de la operación update() 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); }); } );