Su esquema actual tiene las marks
tipo de datos de campo como cadena y necesita un tipo de datos entero para que su marco de agregación calcule la suma. Por otro lado, puedes usar MapReduce
para calcular la suma ya que permite el uso de métodos JavaScript nativos como parseInt()
en las propiedades de su objeto en sus funciones de mapa. Entonces, en general, tienes dos opciones.
Opción 1:Actualizar esquema (cambiar tipo de datos)
La primera sería cambiar el esquema o agregar otro campo en su documento que tenga el valor numérico real, no la representación de cadena. Si el tamaño del documento de su colección es relativamente pequeño, puede usar una combinación del cursor de mongodb find()
, forEach()
y update()
métodos para cambiar su 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 tamaños de colección relativamente grandes, el rendimiento de su base de datos será lento y se recomienda usar actualizaciones masivas de mongo por esto:
Versiones de MongoDB>=2.6 y <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 versión 3.2 y posteriores:
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);
Opción 2:Ejecutar MapReduce
El segundo enfoque sería volver a escribir su consulta con MapReduce
donde puede usar la función JavaScript parseInt()
.
En tu MapReduce
operación, defina la función de mapa que procesa cada documento de entrada. Esta función asigna las marks
convertidas valor de cadena al subject
para cada documento, y emite el subject
y marks
convertidas par. Aquí es donde la función nativa de JavaScript parseInt()
puede ser aplicado. Nota:en la función, this
se refiere al documento que está procesando la operación map-reduce:
var mapper = function () {
var x = parseInt(this.marks);
emit(this.subject, x);
};
A continuación, defina la función de reducción correspondiente con dos argumentos keySubject
y valuesMarks
. valuesMarks
es una matriz cuyos elementos son los enteros marks
valores emitidos por la función map y agrupados por keySubject
.La función reduce los valuesMarks
arreglo a la suma de sus elementos.
var reducer = function(keySubject, valuesMarks) {
return Array.sum(valuesMarks);
};
db.student.mapReduce(
mapper,
reducer,
{
out : "example_results",
query: { subject : "maths" }
}
);
Con su colección, lo anterior pondrá su resultado de agregación de MapReduce en una nueva colección db.example_results
. Por lo tanto, db.example_results.find()
generará:
/* 0 */
{
"_id" : "maths",
"value" : 163
}