Ditt nuvarande schema har marks
fältdatatyp som sträng och du behöver en heltalsdatatyp för ditt aggregeringsramverk för att räkna ut summan. Å andra sidan kan du använda MapReduce
för att beräkna summan eftersom det tillåter användning av inbyggda JavaScript-metoder som parseInt()
på dina objektegenskaper i dess kartfunktioner. Så totalt sett har du två val.
Alternativ 1:Uppdatera schema (Ändra datatyp)
Det första skulle vara att ändra schemat eller lägga till ett annat fält i ditt dokument som har det faktiska numeriska värdet inte strängrepresentationen. Om ditt samlingsdokument är relativt litet kan du använda en kombination av mongodbs markör find()
, forEach()
och update()
metoder för att ändra ditt märkesschema:
db.student.find({ "marks": { "$type": 2 } }).snapshot().forEach(function(doc) {
db.student.update(
{ "_id": doc._id, "marks": { "$type": 2 } },
{ "$set": { "marks": parseInt(doc.marks) } }
);
});
För relativt stora samlingsstorlekar kommer din db-prestanda att vara långsam och det rekommenderas att använda mongo massuppdateringar för detta:
MongoDB versioner>=2.6 och <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 och senare:
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);
Alternativ 2:Kör MapReduce
Den andra metoden skulle vara att skriva om din fråga med MapReduce
där du kan använda JavaScript-funktionen parseInt()
.
I din MapReduce
operation, definiera kartfunktionen som bearbetar varje inmatat dokument. Denna funktion mappar de konverterade marks
strängvärde till subject
för varje dokument och avger subject
och konverterade marks
par. Det är här den inbyggda JavaScript-funktionen parseInt()
Kan appliceras. Obs:i funktionen, this
hänvisar till dokumentet som kartminskningsoperationen bearbetar:
var mapper = function () {
var x = parseInt(this.marks);
emit(this.subject, x);
};
Därefter definierar du motsvarande reduceringsfunktion med två argument keySubject
och valuesMarks
. valuesMarks
är en array vars element är heltal marks
värden som avges av kartfunktionen och grupperade efter keySubject
. Funktionen minskar valuesMarks
array till summan av dess element.
var reducer = function(keySubject, valuesMarks) {
return Array.sum(valuesMarks);
};
db.student.mapReduce(
mapper,
reducer,
{
out : "example_results",
query: { subject : "maths" }
}
);
Med din samling kommer ovanstående att placera ditt MapReduce-aggregationsresultat i en ny samling db.example_results
. Alltså db.example_results.find()
kommer att mata ut:
/* 0 */
{
"_id" : "maths",
"value" : 163
}