Текущата ви схема има marks
поле тип данни като низ и имате нужда от целочислен тип данни за вашата рамка за агрегиране, за да изчислите сумата. От друга страна, можете да използвате MapReduce
за изчисляване на сумата, тъй като позволява използването на собствени JavaScript методи като parseInt()
върху свойствата на вашия обект в неговите функции за карта. Така че като цяло имате два избора.
Опция 1:Актуализиране на схема (промяна на типа данни)
Първото би било да промените схемата или да добавите друго поле във вашия документ, което има действителната числена стойност, а не представянето на низ. Ако размерът на вашия документ за колекция е сравнително малък, можете да използвате комбинация от курсора на mongodb find()
, forEach()
и update()
методи за промяна на вашата схема за маркировки:
db.student.find({ "marks": { "$type": 2 } }).snapshot().forEach(function(doc) {
db.student.update(
{ "_id": doc._id, "marks": { "$type": 2 } },
{ "$set": { "marks": parseInt(doc.marks) } }
);
});
За сравнително големи размери на колекция, вашата db производителност ще бъде бавна и се препоръчва да използвате групови актуализации на mongo за това:
Версии на MongoDB>=2.6 и <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 версия 3.2 и по-нова:
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);
Вариант 2:Стартирайте MapReduce
Вторият подход би бил да пренапишете вашата заявка с MapReduce
където можете да използвате функцията на JavaScript parseInt()
.
Във вашия MapReduce
операция, дефинирайте функцията за карта, която обработва всеки входен документ. Тази функция картографира преобразуваните marks
низова стойност към subject
за всеки документ и излъчва subject
и конвертирани marks
двойка. Тук е основната функция на JavaScript parseInt()
може да се прилага. Забележка:във функцията, this
се отнася до документа, който операцията за намаляване на картата обработва:
var mapper = function () {
var x = parseInt(this.marks);
emit(this.subject, x);
};
След това дефинирайте съответната функция за намаляване с два аргумента keySubject
и valuesMarks
. valuesMarks
е масив, чиито елементи са целите числа marks
стойности, излъчвани от функцията map и групирани по keySubject
.Функцията намалява valuesMarks
масив към сумата от неговите елементи.
var reducer = function(keySubject, valuesMarks) {
return Array.sum(valuesMarks);
};
db.student.mapReduce(
mapper,
reducer,
{
out : "example_results",
query: { subject : "maths" }
}
);
С вашата колекция горното ще постави вашия резултат от агрегирането на MapReduce в нова колекция db.example_results
. Така, db.example_results.find()
ще изведе:
/* 0 */
{
"_id" : "maths",
"value" : 163
}