Seu esquema atual tem as
marks
tipo de dados de campo como string e você precisa de um tipo de dados inteiro para sua estrutura de agregação para calcular a soma. Por outro lado, você pode usar MapReduce
para calcular a soma, pois permite o uso de métodos JavaScript nativos como parseInt()
nas propriedades do seu objeto em suas funções de mapa. Então, no geral, você tem duas opções. Opção 1:atualizar esquema (alterar tipo de dados)
A primeira seria alterar o esquema ou adicionar outro campo em seu documento que tenha o valor numérico real e não a representação da string. Se o tamanho do seu documento de coleção for relativamente pequeno, você pode usar uma combinação do cursor do mongodb
find()
, forEach()
e update()
métodos para alterar seu esquema de marcas:db.student.find({ "marks": { "$type": 2 } }).snapshot().forEach(function(doc) {
db.student.update(
{ "_id": doc._id, "marks": { "$type": 2 } },
{ "$set": { "marks": parseInt(doc.marks) } }
);
});
Para tamanhos de coleção relativamente grandes, o desempenho do banco de dados será lento e é recomendável usar atualizações em massa do mongo por esta:
Versões do MongoDB>=2.6 e <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 versão 3.2 e mais recente:
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);
Opção 2:execute o MapReduce
A segunda abordagem seria reescrever sua consulta com MapReduce onde você pode usar a função JavaScript
parseInt()
. Em seu MapReduce operação, defina a função map que processa cada documento de entrada. Esta função mapeia as
marks
convertidas valor da string para o subject
para cada documento e emite o subject
e converteu marks
par. É aqui que a função nativa JavaScript parseInt()
pode ser aplicado. Nota:na função, this
refere-se ao documento que a operação map-reduce está processando:var mapper = function () {
var x = parseInt(this.marks);
emit(this.subject, x);
};
Em seguida, defina a função de redução correspondente com dois argumentos
keySubject
e valuesMarks
. valuesMarks
é uma matriz cujos elementos são o inteiro marks
valores emitidos pela função map e agrupados por keySubject
.A função reduz os valuesMarks
array para a soma de seus elementos. var reducer = function(keySubject, valuesMarks) {
return Array.sum(valuesMarks);
};
db.student.mapReduce(
mapper,
reducer,
{
out : "example_results",
query: { subject : "maths" }
}
);
Com sua coleção, o acima colocará o resultado da agregação do MapReduce em uma nova coleção
db.example_results
. Assim, db.example_results.find()
emitirá:/* 0 */
{
"_id" : "maths",
"value" : 163
}