Edgar A. Gonzalez Ambriz
Pongo a su disposición el siguiente link:
El operador $accumulator de MongoDB te permite construir un miniprograma en JavaScript dentro de una etapa del aggregation framework. Es ideal cuando necesitas inicializar variables, acumular valores documento por documento y consolidar un resultado final, todo dentro del mismo pipeline.
Qué hace el operador accumulator y por qué es útil
Piensa en $accumulator como una evolución de $function. Mientras $function deja una función suelta dentro de una etapa, $accumulator te da un esqueleto completo para programar lógica más compleja: una función puede llamar a otra, guardar estado y devolver un único resultado al final.
Esto significa que puedes crear cálculos personalizados que las funciones nativas de MongoDB no cubren, manteniendo el procesamiento del lado del servidor.
¿Qué es el operador $accumulator en MongoDB? Es un operador del aggregation framework que te deja definir funciones JavaScript para inicializar, acumular y combinar valores dentro de una etapa, devolviendo un resultado final agregado.
Cuáles son las cuatro funciones obligatorias de $accumulator
El operador exige una estructura clara con cuatro funciones, cada una con un rol específico en el ciclo de acumulación.
Cómo funcionan init, accumulate, merge y finalize
Además, debes declarar el campo lang con el valor "js" para indicar que el código está escrito en JavaScript.
Por qué existe merge si ya hay accumulate
La función merge puede parecer redundante, pero su razón de ser es el procesamiento paralelo. MongoDB divide los documentos en bloques, ejecuta accumulate en cada bloque y luego usa merge para fusionar esos estados parciales en uno solo. Sin esta función, la operación no podría escalar en paralelo.
Cómo aplicar accumulator a un caso real con Airbnb
La hipótesis del ejercicio es directa: ¿las propiedades con más amenidades tienden a tener mejores calificaciones? Para probarlo se construye un pipeline de tres etapas sobre la colección de Airbnb [1:30].
Filtrar y contar amenidades antes de agrupar
La primera etapa usa $match para filtrar por review_scores.review_scores_rating mayor a 90, quedándote solo con propiedades de muy buena calificación.
La segunda etapa aplica $addFields junto con el operador $size para crear un campo nuevo llamado amenities_size, que guarda cuántos elementos tiene el array amenities de cada documento [2:10].
¿Para qué sirve el operador $size en MongoDB? Devuelve la cantidad de elementos de un array. En este caso, cuenta cuántas amenidades tiene cada propiedad sin necesidad de hacer $unwind.
Agrupar con group y null para activar accumulator
La tercera etapa es un $group con un detalle clave: el campo _id va en null. Esto se hace solo cuando trabajas con $accumulator, porque la propia función se encarga de procesar todos los documentos como un único conjunto.
Dentro de ese grupo se define el campo media, que recibe el operador $accumulator configurado así:
{ sum: 0, count: 0 }.{ sum: state.sum + value, count: state.count + 1 }.{ sum: state1.sum + state2.sum, count: state1.count + state2.count }.state.sum / state.count, es decir, la media de amenidades.Qué resultado entrega el pipeline y cómo interpretarlo
Al ejecutar el pipeline con propiedades de calificación mayor a 90, se obtiene una media de 25 amenidades por propiedad [7:30]. Al cambiar el filtro a calificaciones menores a 30, la media baja, lo que sugiere una correlación: a más amenidades publicadas, mejores calificaciones tienden a recibir las propiedades.
Algunos puntos prácticos a tener en cuenta cuando escribas tu propio acumulador:
lang: "js" dentro del operador.El verdadero valor de $accumulator es que te da lo mejor de dos mundos: la flexibilidad de programar en JavaScript y la potencia del aggregation framework corriendo en paralelo. ¿Qué otro caso de uso se te ocurre para esta herramienta? Compártelo en los comentarios.
En el caso te aparezca el error:
"$accumulator not allowed in this atlas tier"
En la anterior clase comenté un post similar. Actualmente, las instancias sin servidor no admiten las siguientes acciones: Usar JavaScript del lado del servidor, como $where, $function, $accumulator y map-reduce. Seguramente es mi versión de MongoD en el momento que hago el curso.
Puedes utilizar esta solución provisional sin "$accumulator" para esta consulta:
use('sample_airbnb'); db.listingsAndReviews.aggregate([ { $match: { "review_scores.review_scores_rating": { $gt: 90 } } }, { $addFields: { amenitiesSize: { $size: "$amenities" } } }, { $group: { _id: null, amenitiesSizes: { $push: "$amenitiesSize" } } }, { $unwind: "$amenitiesSizes" }, { $group: { _id: null, total: { $sum: "$amenitiesSizes" }, count: { $sum: 1 } } }, { $addFields: { media: { $round: [{ $divide: ["$total", "$count"] }, 2] } } }, { $project: { media: 1 } } ])
use('sample_airbnb') db.listingAndReviews.aggregate([ { $match: {"review_scores.review_scores_raiting": {$gt: 90}} }, { $addFields: { amenitiesSize: {$size: "$amenities"} } }, { $group: { _id: null, media: { $accumulator: { init: function() { return {sum: 0, count: 0}; }, accumulateArgs: ["$amenitiesSize"], accumulate: function(state, size){ return{sum: state.sum + size, count: state.count +1}; }, merge: function(state1, state2){ return { sum: satet1.sum + state2.sum, count: state1.count + state2.count, }; }, finalize: function(state) { return state.sum / state.count }, lang: "js" } } } } ])
Edgar A. Gonzalez Ambriz
Pongo a su disposición el siguiente link:
Waldir Zapata Garcia
Carla Antonella Palmieri
// experimento para saber la media de amenities por rating desde 91% al 100% use('sample_airbnb') db.listingsAndReviews.aggregate([ { $match: {"review_scores.review_scores_rating": {$gt: 90}} }, { $addFields: { amenitiesSize: {$size: "$amenities"} } }, { $group: { _id: "$review_scores.review_scores_rating", totalAmenities: { $sum: "$amenitiesSize" }, totalProperties: { $sum: 1 } } }, { $sort: { _id: -1 } }, { $project: { property_score: "$_id", _id: 0, mediaAmenities: { $divide: ["$totalAmenities", "$totalProperties"] } } } ])
var db=db.getSiblingDB("sample_airbnb") db.listingsAndReviews.aggregate([ { $match: { "review_scores.review_scores_rating": { $gte: 90}, } }, { //para guadar el acumulador en un campo $addFields:{ amenitiesSize: {$size: "$amenities"} } }, { $group: { _id: null, media: { $accumulator: { init: function(){ return {sum:0, count:0}; }, accumulateArgs:["$amenitiesSize"], accumulate: function(state, size){ return {sum: state.sum + size, count: state.count+1}; }, // se crea una funcion para juntar documentos merge: function(state1, state2){ return { sum: state1.sum + state2.sum, count: state1.count + state2.count }; }, //finalizar finalize: function(state){ return state.sum /state.count }, lang:"js" } } } } ] ); #resultado [ { "_id": null, "media": 24.81186283595922 } ]