Ricerca…


Osservazioni

Costruire un elenco di operazioni di scrittura da eseguire in blocco per una singola raccolta.

Convertire un campo in un altro tipo e aggiornando l'intera raccolta in massa

Solitamente quando si vuole cambiare un tipo di campo con un altro, ad esempio la collezione originale può avere campi "numerici" o "data" salvati come stringhe:

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

L'obiettivo sarebbe quello di aggiornare una collezione enorme come sopra a

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

Per dati relativamente piccoli, è possibile ottenere quanto sopra citando la raccolta utilizzando snapshot con il metodo forEach() del cursore e aggiornando ciascun documento come segue:

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 } }
    );
});

Sebbene ciò sia ottimale per le piccole raccolte, le prestazioni con le raccolte di grandi dimensioni sono notevolmente ridotte poiché il looping di un set di dati di grandi dimensioni e l'invio di ogni operazione di aggiornamento per richiesta al server comporta una penalità computazionale.

L'API Bulk() viene in soccorso e migliora notevolmente le prestazioni poiché le operazioni di scrittura vengono inviate al server solo una volta alla rinfusa. L'efficienza viene raggiunta poiché il metodo non invia ogni richiesta di scrittura al server (come con l'istruzione di aggiornamento corrente all'interno del ciclo forEach() ) ma solo una volta ogni 1000 richieste, rendendo così gli aggiornamenti più efficienti e più rapidi di quelli attuali.


Usando lo stesso concetto sopra con il ciclo forEach() per creare i batch, possiamo aggiornare la raccolta in blocco come segue. In questa dimostrazione l'API Bulk() disponibile nelle versioni MongoDB >= 2.6 e < 3.2 utilizza il metodo initializeUnorderedBulkOp() per eseguire in parallelo, oltre che in un ordine non deterministico, le operazioni di scrittura nei batch.

Aggiorna tutti i documenti nella raccolta dei client modificando i campi salary e dob in valori numerical e datetime rispettivamente:

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();
    }
});

Il prossimo esempio si applica alla nuova versione di MongoDB 3.2 che ha deprecato l'API Bulk() e fornito un nuovo set di apis usando bulkWrite() .

Utilizza gli stessi cursori di cui sopra ma crea gli array con le operazioni di massa utilizzando lo stesso metodo di cursore forEach() ciascun documento di scrittura bulk all'array. Poiché i comandi di scrittura possono accettare non più di 1000 operazioni, è necessario raggruppare le operazioni per avere al massimo 1000 operazioni e ri-intializzare l'array quando il ciclo raggiunge l'iterazione 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
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow