Vaše aktuální schéma má marks
datový typ pole jako řetězec a pro výpočet součtu potřebujete celočíselný datový typ pro váš agregační rámec. Na druhou stranu můžete použít MapReduce
pro výpočet součtu, protože umožňuje použití nativních metod JavaScriptu, jako je parseInt()
na vlastnosti vašeho objektu v jeho mapových funkcích. Celkově tedy máte dvě možnosti.
Možnost 1:Aktualizace schématu (změna datového typu)
První by bylo změnit schéma nebo přidat další pole do dokumentu, které má skutečnou číselnou hodnotu, nikoli reprezentaci řetězce. Pokud je velikost vašeho sbírkového dokumentu relativně malá, můžete použít kombinaci kurzoru mongodb find()
, forEach()
a update()
metody pro změnu schématu známek:
db.student.find({ "marks": { "$type": 2 } }).snapshot().forEach(function(doc) {
db.student.update(
{ "_id": doc._id, "marks": { "$type": 2 } },
{ "$set": { "marks": parseInt(doc.marks) } }
);
});
U relativně velkých velikostí kolekce bude výkon vaší databáze pomalý a doporučuje se použít hromadné aktualizace mongo pro toto:
Verze MongoDB>=2.6 a <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 verze 3.2 a novější:
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);
Možnost 2:Spusťte MapReduce
Druhým přístupem by bylo přepsat dotaz pomocí MapReduce
kde můžete použít funkci JavaScriptu parseInt()
.
Ve vašem MapReduce
operace, definovat mapové funkce, které zpracovávají každý vstupní dokument. Tato funkce mapuje převedené marks
hodnotu řetězce do subject
pro každý dokument a vydá subject
a převedené marks
pár. Zde je nativní funkce JavaScriptu parseInt()
lze aplikovat. Poznámka:ve funkci this
odkazuje na dokument, který operace zmenšení mapy zpracovává:
var mapper = function () {
var x = parseInt(this.marks);
emit(this.subject, x);
};
Dále definujte odpovídající funkci snížení pomocí dvou argumentů keySubject
a valuesMarks
. valuesMarks
je pole, jehož prvky jsou celé číslo marks
hodnoty emitované funkcí mapy a seskupené podle keySubject
.Funkce snižuje valuesMarks
pole k součtu jeho prvků.
var reducer = function(keySubject, valuesMarks) {
return Array.sum(valuesMarks);
};
db.student.mapReduce(
mapper,
reducer,
{
out : "example_results",
query: { subject : "maths" }
}
);
S vaší sbírkou výše uvedené umístí váš výsledek agregace MapReduce do nové sbírky db.example_results
. Tedy db.example_results.find()
vypíše:
/* 0 */
{
"_id" : "maths",
"value" : 163
}