Cómo funciona el operador accumulator en MongoDB

Resumen

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

  • init: inicializa el estado. No recibe parámetros y retorna un objeto con las variables que vas a usar, por ejemplo sum y count en cero [2:50].
  • accumulate: recibe el estado actual más un nuevo valor y devuelve el estado actualizado. Aquí sumas el valor entrante y aumentas el contador [3:40].
  • merge: combina dos estados parciales. MongoDB la usa cuando procesa bloques en paralelo, así que recibe state1 y state2 y los une [4:30].
  • finalize: se ejecuta al final con el estado consolidado y produce la salida deseada, como dividir sum entre count para obtener una media [5:20].

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í:

  • accumulateArgs: recibe amenities_size como argumento que se pasa en cada iteración.
  • init: retorna { sum: 0, count: 0 }.
  • accumulate: retorna { sum: state.sum + value, count: state.count + 1 }.
  • merge: retorna { sum: state1.sum + state2.sum, count: state1.count + state2.count }.
  • finalize: retorna 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:

  1. La sintaxis dentro de las funciones es JavaScript puro, así que usa punto y coma al cerrar instrucciones.
  2. Fuera de las funciones estás en JSON, donde los objetos se separan con coma.
  3. Verifica los nombres exactos de los campos anidados como review_scores.review_scores_rating; un error de tipeo no lanza excepción pero rompe el filtro.
  4. Declara siempre 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.