Recherche…


Remarques

Construire une liste d'opérations d'écriture à exécuter en bloc pour une collection unique.

Conversion d'un champ en un autre type et mise à jour de la totalité de la collection en bloc

Généralement, le cas où l'on veut changer un type de champ en un autre, par exemple, la collection d'origine peut avoir des champs "numériques" ou "date" enregistrés en tant que chaînes:

{
    "name": "Alice",
    "salary": "57871",
    "dob": "1986-08-21"
},
{
    "name": "Bob",
    "salary": "48974",
    "dob": "1990-11-04"
}

L’objectif serait de mettre à jour une collection colossale comme ci-dessus pour

{
    "name": "Alice",
    "salary": 57871,
    "dob": ISODate("1986-08-21T00:00:00.000Z")
},
{
    "name": "Bob",
    "salary": 48974,
    "dob": ISODate("1990-11-04T00:00:00.000Z")
}

Pour des données relativement petites, on peut obtenir ce qui précède en itérant la collection en utilisant un snapshot avec la méthode forEach() du curseur et en mettant à jour chaque document comme suit:

db.test.find({
    "salary": { "$exists": true, "$type": 2 },
    "dob": { "$exists": true, "$type": 2 }
}).snapshot().forEach(function(doc){ 
    var newSalary = parseInt(doc.salary),
        newDob = new ISODate(doc.dob);        
    db.test.updateOne(
        { "_id": doc._id },
        { "$set": { "salary": newSalary, "dob": newDob } }
    );
});

Bien que cela soit optimal pour les petites collections, les performances avec des collections importantes sont considérablement réduites, car la lecture en boucle d'un ensemble de données volumineux et l'envoi de chaque opération de mise à jour par requête entraînent une pénalité informatique.

L'API Bulk() vient à la rescousse et améliore considérablement les performances car les opérations d'écriture ne sont envoyées qu'une seule fois au serveur. L'efficacité est atteinte car la méthode n'envoie pas toutes les demandes d'écriture au serveur (comme avec l'instruction de mise à jour actuelle dans la boucle forEach() ) mais seulement une fois sur 1000, ce qui rend les mises à jour plus efficaces et plus rapides.


En utilisant le même concept ci-dessus avec la boucle forEach() pour créer les lots, nous pouvons mettre à jour la collection en bloc comme suit. Dans cette démonstration, l’API Bulk() disponible dans les versions MongoDB >= 2.6 et < 3.2 utilise la méthode initializeUnorderedBulkOp() pour exécuter en parallèle, ainsi que dans un ordre non déterministe, les opérations d’écriture dans les lots.

Il met à jour tous les documents de la collection clients en changeant les salary et dob champs à numerical et datetime valeurs respectivement:

var bulk = db.test.initializeUnorderedBulkOp(),
    counter = 0; // counter to keep track of the batch update size

db.test.find({
    "salary": { "$exists": true, "$type": 2 },
    "dob": { "$exists": true, "$type": 2 }
}).snapshot().forEach(function(doc){ 
    var newSalary = parseInt(doc.salary),
        newDob = new ISODate(doc.dob);
    bulk.find({ "_id": doc._id }).updateOne({ 
        "$set": { "salary": newSalary, "dob": newDob }
    });

    counter++; // increment counter
    if (counter % 1000 == 0) {
        bulk.execute(); // Execute per 1000 operations and re-initialize every 1000 update statements
        bulk = db.test.initializeUnorderedBulkOp();
    }
});

L'exemple suivant s'applique à la nouvelle version 3.2 MongoDB qui a depuis abandonné l'API Bulk() et fourni un nouvel ensemble d' bulkWrite() utilisant bulkWrite() .

Il utilise les mêmes curseurs que ci-dessus, mais crée les tableaux avec les opérations en bloc en utilisant la même méthode de curseur forEach() pour pousser chaque document en bloc dans le tableau. Les commandes d'écriture ne pouvant pas accepter plus de 1 000 opérations, il est nécessaire de regrouper les opérations pour avoir au maximum 1 000 opérations et de ré-initialiser le tableau lorsque la boucle atteint l'itération 1000:

var cursor = db.test.find({
        "salary": { "$exists": true, "$type": 2 },
        "dob": { "$exists": true, "$type": 2 }
    }),
    bulkUpdateOps = [];

cursor.snapshot().forEach(function(doc){ 
    var newSalary = parseInt(doc.salary),
        newDob = new ISODate(doc.dob);
    bulkUpdateOps.push({ 
        "updateOne": {
            "filter": { "_id": doc._id },
            "update": { "$set": { "salary": newSalary, "dob": newDob } }
         }
    });

    if (bulkUpdateOps.length === 1000) {
        db.test.bulkWrite(bulkUpdateOps);
        bulkUpdateOps = [];
    }
});         

if (bulkUpdateOps.length > 0) { db.test.bulkWrite(bulkUpdateOps); }


Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow