Il tuo schema attuale ha i marks
tipo di dati del campo come stringa e hai bisogno di un tipo di dati intero per il tuo framework di aggregazione per calcolare la somma. D'altra parte, puoi usare MapReduce
per calcolare la somma poiché consente l'uso di metodi JavaScript nativi come parseInt()
sulle proprietà dell'oggetto nelle sue funzioni di mappa. Quindi nel complesso hai due scelte.
Opzione 1:Aggiorna schema (cambia tipo di dati)
Il primo sarebbe modificare lo schema o aggiungere un altro campo nel documento che abbia il valore numerico effettivo e non la rappresentazione della stringa. Se le dimensioni del tuo documento di raccolta sono relativamente piccole, puoi utilizzare una combinazione del cursore di mongodb find()
, forEach()
e update()
metodi per modificare lo schema dei voti:
db.student.find({ "marks": { "$type": 2 } }).snapshot().forEach(function(doc) {
db.student.update(
{ "_id": doc._id, "marks": { "$type": 2 } },
{ "$set": { "marks": parseInt(doc.marks) } }
);
});
Per raccolte di dimensioni relativamente grandi, le prestazioni del tuo db saranno lente e si consiglia di utilizzare aggiornamenti collettivi mongo per questo:
Versioni MongoDB>=2.6 e <3.2:
var bulk = db.student.initializeUnorderedBulkOp(),
counter = 0;
db.student.find({"marks": {"$exists": true, "$type": 2 }}).forEach(function (doc) {
bulk.find({ "_id": doc._id }).updateOne({
"$set": { "marks": parseInt(doc.marks) }
});
counter++;
if (counter % 1000 === 0) {
// Execute per 1000 operations
bulk.execute();
// re-initialize every 1000 update statements
bulk = db.student.initializeUnorderedBulkOp();
}
})
// Clean up remaining operations in queue
if (counter % 1000 !== 0) bulk.execute();
MongoDB versione 3.2 e successive:
var ops = [],
cursor = db.student.find({"marks": {"$exists": true, "$type": 2 }});
cursor.forEach(function (doc) {
ops.push({
"updateOne": {
"filter": { "_id": doc._id } ,
"update": { "$set": { "marks": parseInt(doc.marks) } }
}
});
if (ops.length === 1000) {
db.student.bulkWrite(ops);
ops = [];
}
});
if (ops.length > 0) db.student.bulkWrite(ops);
Opzione 2:esegui MapReduce
Il secondo approccio sarebbe riscrivere la query con MapReduce
dove puoi usare la funzione JavaScript parseInt()
.
Nel tuo MapReduce
operazione, definire la funzione mappa che elabora ogni documento di input. Questa funzione mappa i marks
convertiti valore stringa al subject
per ogni documento ed emette il subject
e convertito marks
coppia. Qui è dove la funzione nativa JavaScript parseInt()
può essere applicato. Nota:nella funzione, this
si riferisce al documento che sta elaborando l'operazione di riduzione della mappa:
var mapper = function () {
var x = parseInt(this.marks);
emit(this.subject, x);
};
Quindi, definisci la corrispondente funzione di riduzione con due argomenti keySubject
e valuesMarks
. valuesMarks
è un array i cui elementi sono gli interi marks
valori emessi dalla funzione map e raggruppati per keySubject
.La funzione riduce i valuesMarks
array alla somma dei suoi elementi.
var reducer = function(keySubject, valuesMarks) {
return Array.sum(valuesMarks);
};
db.student.mapReduce(
mapper,
reducer,
{
out : "example_results",
query: { subject : "maths" }
}
);
Con la tua raccolta, quanto sopra inserirà il risultato dell'aggregazione MapReduce in una nuova raccolta db.example_results
. Pertanto, db.example_results.find()
produrrà:
/* 0 */
{
"_id" : "maths",
"value" : 163
}