Tengo dificultades para agregar otro campo a todos los registros de la colección del user con un valor de cada documento. Estoy familiarizado con el uso $set con db.model.updateMany y $addFields con canalización de agregación. He usado ambos para resolver problemas en el pasado, en este caso, tengo que realizar algunos cálculos lógicos antes de agregar el valor. , justo aquí es donde radica mi problema.
Digamos, tengo el esquema como este:
{ "users": [ { "wallets": {...}, "avatar": "", "isVerified": false, "suspended": false, "country": "Nigeria", "_id": "123", "resetPasswordToken": "", "email": "example@gmail.com", "phone": "08012398743", "name": "Agbakwuru Nnaemeka Kennedy ", "role": "user", }, {...} } Quiero agregar un nuevo campo phoneNumber , que tomará el valor del phone de campo existente, pero antes de agregarlo, me gustaría ejecutar una lógica en él, ya que algunos de los valores del teléfono tienen espacios en blanco, la mayoría de ellos no son correctos formateado, y me gustaría anteponer el código de país al valor del phone , antes de agregarlo al nuevo campo phoneNumber .
Pude hacer esto usando un cursor del método Mongoose db.mode.aggregate , con $match filter, y agregando el campo a cada documento con la canalización agregada $addFields , esto demostró tomar mucho tiempo, tuve para detener la operación, ya que está tardando demasiado en ejecutarse.
Me gustaría creer que hay una mejor manera, por favor, agradeceré cualquier ayuda.
Editar:
Aquí está la agregación que estoy usando:
const userCursor = User.aggregate([{$match: {phone: {$exists: true}}}]); for await (const doc of userCursor) { await User.findByIdAndUpdate(doc._id, {$set: { phoneNumber: convertPhoneNumber({phoneNumber: doc.phone.replace(/\s+/g, "")})} }); } El convertPhoneNumber es un método de ayuda que definí en mis utils para anteponer el código de trato del país al número de teléfono.
Intentaría ejecutar un script como ese directamente en la línea de comando de mongo , o Robo3T:
db.getCollection("users").find({}).forEach( doc => { doc.users.forEach( user => { // do your logic here let phoneNumber = "12345"; phoneNumber = "+007" + phoneNumber; user.phoneNumber = phoneNumber; }) db.users.save(doc); })Todavía tomará un tiempo en más de 300k documentos, pero espere unos minutos.
Puede usar $function y llamar a ese código javascript en la base de datos.
Esto requiere >=MongoDB 4.4
db.Users.update( {phone: {$exists: true}}, [{$set: {phoneNumber: { "$function": { "body": YOUR_convertPhoneNumber_FUNCTION_DEF, "args": ["$phoneNumber"], "lang": "js" } }])Además, si el código de convertPhoneNumber se puede escribir en MongodBD usando operadores agregados, también puede evitar el javascript.
Lo anterior es una actualización de tubería y podemos usar todos los operadores agregados al actualizar.
Editar
Si mongoose tiene problemas con la $function o el método del controlador nodejs tiene problemas con la actualización de la canalización, también puede hacerlo.
db.runCommand( { update: "yourCollectionName", updates: [ { q: {phone: {$exists: true}}, u: [{$set: {phoneNumber: { "$function": { "body": YOUR_convertPhoneNumber_FUNCTION_DEF, "args": ["$phoneNumber"], "lang": "js" } }], multi: true } ], ordered: false } )Puede probar la operación masiva , esto actualiza la colección en lotes de 1000 documentos:
var bulkOperations = []; db.getCollection("users").find({}).forEach(doc => { doc.users.forEach(user => { user.phoneNumber = convertPhoneNumber({phoneNumber: user.phone.replace(/\s+/g, "")}); }) bulkOperations.push({ updateOne: { filter: { id: doc._id }, update: { $set: { users: doc.users } } } }); if (bulkOperations.length > 1000) { db.getCollection("users").bulkWrite(bulkOperations, { ordered: false }); bulkOperations = []; } }) if (bulkOperations.length > 0) db.getCollection("users").bulkWrite(bulkOperations, { ordered: false });