Ihr aktuelles Schema hat die marks
Felddatentyp als Zeichenfolge und Sie benötigen einen ganzzahligen Datentyp für Ihr Aggregationsframework, um die Summe zu berechnen. Andererseits können Sie MapReduce
verwenden um die Summe zu berechnen, da es die Verwendung von nativen JavaScript-Methoden wie parseInt()
erlaubt auf Ihre Objekteigenschaften in seinen Kartenfunktionen. Insgesamt haben Sie also zwei Möglichkeiten.
Option 1:Schema aktualisieren (Datentyp ändern)
Die erste wäre, das Schema zu ändern oder ein weiteres Feld in Ihrem Dokument hinzuzufügen, das den tatsächlichen numerischen Wert und nicht die Zeichenfolgendarstellung enthält. Wenn Ihr Sammlungsdokument relativ klein ist, können Sie eine Kombination aus dem Mongodb-Cursor find()
verwenden , forEach()
und update()
Methoden zum Ändern Ihres Markierungsschemas:
db.student.find({ "marks": { "$type": 2 } }).snapshot().forEach(function(doc) {
db.student.update(
{ "_id": doc._id, "marks": { "$type": 2 } },
{ "$set": { "marks": parseInt(doc.marks) } }
);
});
Bei relativ großen Sammlungsgrößen wird Ihre DB-Leistung langsam sein und es wird empfohlen, Mongo-Massenaktualisierungen dazu:
MongoDB-Versionen>=2.6 und <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-Version 3.2 und neuer:
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);
Option 2:MapReduce ausführen
Der zweite Ansatz wäre, Ihre Abfrage mit MapReduce
neu zu schreiben wo Sie die JavaScript-Funktion parseInt()
verwenden können .
In Ihrem MapReduce
Operation, definieren Sie die Zuordnungsfunktion, die jedes Eingabedokument verarbeitet. Diese Funktion bildet die konvertierten marks
ab String-Wert zum subject
für jedes Dokument und gibt den subject
aus und umgewandelte marks
Paar. Hier setzt die native JavaScript-Funktion parseInt()
an Kann Angewandt werden. Hinweis:in der Funktion this
bezieht sich auf das Dokument, das von der Map-Reduce-Operation verarbeitet wird:
var mapper = function () {
var x = parseInt(this.marks);
emit(this.subject, x);
};
Als nächstes definieren Sie die entsprechende Reduce-Funktion mit zwei Argumenten keySubject
und valuesMarks
. valuesMarks
ist ein Array, dessen Elemente die ganzzahligen marks
sind Werte, die von der Kartenfunktion ausgegeben und nach keySubject
gruppiert werden .Die Funktion reduziert die valuesMarks
Array zur Summe seiner Elemente.
var reducer = function(keySubject, valuesMarks) {
return Array.sum(valuesMarks);
};
db.student.mapReduce(
mapper,
reducer,
{
out : "example_results",
query: { subject : "maths" }
}
);
Mit Ihrer Sammlung wird das Obige Ihr MapReduce-Aggregationsergebnis in eine neue Sammlung db.example_results
einfügen . Also db.example_results.find()
wird ausgegeben:
/* 0 */
{
"_id" : "maths",
"value" : 163
}