Twój aktualny schemat zawiera marks
pole typu danych jako ciąg i potrzebujesz typu danych całkowitych, aby struktura agregacji mogła obliczyć sumę. Z drugiej strony możesz użyć MapReduce
do obliczenia sumy, ponieważ pozwala na użycie natywnych metod JavaScript, takich jak parseInt()
we właściwościach obiektu w jego funkcjach mapy. Więc ogólnie masz dwie możliwości.
Opcja 1:Aktualizuj schemat (zmień typ danych)
Pierwszym z nich jest zmiana schematu lub dodanie innego pola w dokumencie, które ma rzeczywistą wartość liczbową, a nie reprezentację ciągu. Jeśli rozmiar dokumentu kolekcji jest stosunkowo mały, możesz użyć kombinacji kursora mongodb find()
, forEach()
i update()
metody zmiany schematu znaków:
db.student.find({ "marks": { "$type": 2 } }).snapshot().forEach(function(doc) {
db.student.update(
{ "_id": doc._id, "marks": { "$type": 2 } },
{ "$set": { "marks": parseInt(doc.marks) } }
);
});
W przypadku stosunkowo dużych rozmiarów kolekcji wydajność bazy danych będzie niska i zaleca się użycie aktualizacje zbiorcze mongo w tym celu:
Wersje MongoDB>=2.6 i <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 w wersji 3.2 i nowszej:
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);
Opcja 2:Uruchom MapReduce
Drugie podejście polegałoby na przepisaniu zapytania za pomocą MapReduce
gdzie możesz użyć funkcji JavaScript parseInt()
.
W swoim MapReduce
operacji, zdefiniuj funkcję mapy, która przetwarza każdy dokument wejściowy. Ta funkcja mapuje przekonwertowane marks
wartość ciągu do subject
dla każdego dokumentu i emituje subject
i przekonwertowane marks
para. W tym miejscu natywna funkcja JavaScript parseInt()
można zastosować. Uwaga:w funkcji this
odnosi się do dokumentu, który przetwarza operacja map-reduce:
var mapper = function () {
var x = parseInt(this.marks);
emit(this.subject, x);
};
Następnie zdefiniuj odpowiednią funkcję zmniejszania za pomocą dwóch argumentów keySubject
i valuesMarks
. valuesMarks
to tablica, której elementami są liczby całkowite marks
wartości emitowane przez funkcję map i pogrupowane według keySubject
.Funkcja zmniejsza valuesMarks
tablicę do sumy jej elementów.
var reducer = function(keySubject, valuesMarks) {
return Array.sum(valuesMarks);
};
db.student.mapReduce(
mapper,
reducer,
{
out : "example_results",
query: { subject : "maths" }
}
);
W przypadku Twojej kolekcji powyższe spowoduje umieszczenie wyniku agregacji MapReduce w nowej kolekcji db.example_results
. Zatem db.example_results.find()
wypisze:
/* 0 */
{
"_id" : "maths",
"value" : 163
}