Estoy tratando de crear una aplicación avanzada de hola mundo además de express y mongoose. Supongamos que tengo los siguientes esquemas:
const pollOptionsSchema = new Schema({ name: String, votes: { type: Number, default: 0 } }); const pollSchema = new Schema({ name: String, dateCreated: { type: Date, default: Date.now }, author: { type: Schema.Types.ObjectId }, options: [pollOptionsSchema] });Y cuando simplemente llamo
Poll.findOne({_id: req.params.id}).exec((err, data) => { if (err) console.log(err); // I receive next data: // { _id: 58ef3d2c526ced15688bd1ea, // name: 'Question', // author: 58dcdadfaea29624982e2fc6, // __v: 0, // options: // [ { name: 'stack', _id: 58ef3d2c526ced15688bd1ec, votes: 5 }, // { name: 'overflow', _id: 58ef3d2c526ced15688bd1eb, votes: 3 } ], // dateCreated: 2017-04-13T08:56:12.044Z } });La pregunta es cómo podría recibir los mismos datos + el número agregado de votos (es decir, 8 en el caso anterior) después de llamar a algún método en el nivel del modelo, por ejemplo:
// I want to receive: // { _id: 58ef3d2c526ced15688bd1ea, // name: 'Question', // author: 58dcdadfaea29624982e2fc6, // __v: 0, // totalNumberOfVotes: 8, // options: // [ { name: 'stack', _id: 58ef3d2c526ced15688bd1ec, votes: 5 }, // { name: 'overflow', _id: 58ef3d2c526ced15688bd1eb, votes: 3 } ], // dateCreated: 2017-04-13T08:56:12.044Z }¿O tal vez necesito implementar algún método adicional a nivel de documento, es decir (data.aggregate)?
Ya he revisado:
Pero no puedo utilizarlo para mi caso :(
Cualquier consejo será muy apreciado. ¡Gracias!
Use el operador $reduce dentro de una canalización $addFields para crear el campo totalNumberOfVotes . En su canalización agregada, el primer paso es $match que filtra el flujo de documentos para permitir que solo los documentos coincidentes pasen sin modificar a la siguiente etapa de la canalización y utiliza consultas estándar de MongoDB.
Considere ejecutar la siguiente operación agregada para obtener el resultado deseado:
Poll.aggregate([ { "$match": { "_id": mongoose.Types.ObjectId(req.params.id) } }, { "$addFields": { "totalNumberOfVotes": { "$reduce": { "input": "$options", "initialValue": 0, "in": { "$add" : ["$$value", "$$this.votes"] } } } } } ]).exec((err, data) => { if (err) console.log(err); console.log(data); });NB: Lo anterior funcionará para MongoDB 3.4 y superior.
Para otras versiones anteriores, primero necesitaría $unwind la matriz de options antes de agrupar los documentos desnormalizados dentro de un paso de canalización de $group y agregarlos con los acumuladores $sum , $push y $first .
El siguiente ejemplo muestra este enfoque:
Poll.aggregate([ { "$match": { "_id": mongoose.Types.ObjectId(req.params.id) } }, { "$unwind": { "path": "$options", "preserveNullAndEmptyArrays": true } }, { "$group": { "_id": "$_id", "totalNumberOfVotes": { "$sum": "$options.votes" }, "options": { "$push": "$options" }, "name": { "$first": "$name" }, "dateCreated": { "$first": "$dateCreated" }, "author": { "$first": "$author" } } } ]).exec((err, data) => { if (err) console.log(err); console.log(data); });