sql >> Base de Datos >  >> NoSQL >> MongoDB

¿Cuál es el enfoque correcto para actualizar muchos registros en MongoDB usando Mongoose?

El enfoque de crear un criterio que consta de todos los identificadores de documentos y luego realizar la actualización seguramente causará problemas potenciales. Cuando itera una lista de documentos que envían una operación de actualización con cada documento, en Mongoose corre el riesgo de explotar su servidor, especialmente cuando se trata de un gran conjunto de datos porque no está esperando que se complete una llamada asíncrona antes de pasar a la siguiente. iteración. Básicamente, estará construyendo una "pila" de operaciones no resueltas hasta que esto cause un problema:Stackoverflow.

Tomemos, por ejemplo, supongamos que tiene una serie de ID de documentos que desea actualizar el documento coincidente en el campo de estado:

const processedIds = [
  "57a0a96bd1c6ef24376477cd",
  "57a052242acf5a06d4996537",
  "57a052242acf5a06d4996538"
];

donde puede usar updateMany() método

Model.updateMany(
  { _id: { $in: processedIds } }, 
  { $set: { status: "processed" } }, 
  callback
);

o, alternativamente, para conjuntos de datos realmente pequeños, puede usar paraCada() método en la matriz para iterarlo y actualizar su colección:

processedIds.forEach(function(id)){
  Model.update({ _id: id}, { $set: { status: "processed" } }, callback);
});

Lo anterior está bien para pequeños conjuntos de datos. Sin embargo, esto se convierte en un problema cuando se enfrenta a miles o millones de documentos para actualizar, ya que realizará repetidas llamadas al servidor de código asíncrono dentro del bucle.

Para superar esto, use algo como eachLimit e itere sobre la matriz realizando una operación de actualización de MongoDB para cada elemento sin realizar nunca más de x actualizaciones paralelas al mismo tiempo.

El mejor enfoque sería usar la API masiva para esto, que es extremadamente eficiente en el procesamiento de actualizaciones masivas. La diferencia en el rendimiento frente a llamar a la operación de actualización en todos y cada uno de los muchos documentos es que, en lugar de enviar las solicitudes de actualización al servidor con cada iteración, la API masiva envía las solicitudes una vez cada 1000 solicitudes (por lotes).

Para las versiones de Mongoose >=4.3.0 que admiten MongoDB Server 3.2.x , puede usar bulkWrite() para actualizaciones El siguiente ejemplo muestra cómo puede hacer esto:

const bulkUpdateCallback = function(err, r){
  console.log(r.matchedCount);
  console.log(r.modifiedCount);
}

// Initialize the bulk operations array
const bulkUpdateOps = [], counter = 0;

processedIds.forEach(function (id) {
  bulkUpdateOps.push({
    updateOne: {
      filter: { _id: id },
      update: { $set: { status: "processed" } }
    }
  });
  counter++;

  if (counter % 500 == 0) {
    // Get the underlying collection via the Node.js driver collection object
    Model.collection.bulkWrite(bulkUpdateOps, { ordered: true, w: 1 }, bulkUpdateCallback);
    bulkUpdateOps = []; // re-initialize
  }
})

// Flush any remaining bulk ops
if (counter % 500 != 0) {
  Model.collection.bulkWrite(bulkOps, { ordered: true, w: 1 }, bulkUpdateCallback);
}

Para las versiones de Mongoose ~3.8.8 , ~3.8.22 , 4.x que admiten MongoDB Server >=2.6.x , podría usar la API masiva de la siguiente manera

var bulk = Model.collection.initializeOrderedBulkOp(),
    counter = 0;

processedIds.forEach(function(id) {
    bulk.find({ "_id": id }).updateOne({ 
        "$set": { "status": "processed" }
    });

    counter++;
    if (counter % 500 == 0) {
        bulk.execute(function(err, r) {
           // do something with the result
           bulk = Model.collection.initializeOrderedBulkOp();
           counter = 0;
        });
    }
});

// Catch any docs in the queue under or over the 500's
if (counter > 0) {
    bulk.execute(function(err,result) {
       // do something with the result here
    });
}