Make the MongoDB docs better! We value your opinion. Share your feedback for a chance to win $100.
Click here >
Docs Menu
Docs Home
/ /

$accumulator (operador de acumulador)

$accumulator

Define un operador de acumulación. Los acumuladores son operadores que mantienen su estado (por ejemplo, totales, máximos, mínimos y datos relacionados) a medida que los documentos avanzan por el pipeline. Utilizar el $accumulator operador para ejecutar tus propias funciones JavaScript para implementar un comportamiento no soportado por el languaje del query de MongoDB. Véase también $function.

$accumulator está disponible en estas etapas:

  • $bucket

  • $bucketAuto

  • $group

Importante

La ejecución de JavaScript dentro de un operador de agregación puede disminuir el rendimiento. Utilice solo el operador $accumulator si los operadores de pipeline proporcionados no pueden satisfacer las necesidades de su aplicación.

El operador $accumulator tiene esta sintaxis:

{
$accumulator: {
init: <code>,
initArgs: <array expression>, // Optional
accumulate: <code>,
accumulateArgs: <array expression>,
merge: <code>,
finalize: <code>, // Optional
lang: <string>
}
}
Campo
Tipo
Descripción

string o código

Función utilizada para inicializar el estado. La función init recibe sus argumentos del arreglo de expresiones initArgs. Puedes especificar la definición de función como el tipo BSON Code o String.

La función init tiene la siguiente forma:

function (<initArg1>, <initArg2>, ...) {
...
return <initialState>
}

Derramar en el disco o ejecutar una query en un clúster fragmentado puede hacer que el acumulador se calcule como una fusión de varias subacumulaciones, cada una de las cuales comienza llamando a init(). Asegúrese de que tus funciones init(), accumulate() y merge() sean compatibles con este modelo de ejecución.

Arreglo

opcional. Argumentos pasados a la función init.

initArgs tiene la siguiente forma:

[ <initArg1>, <initArg2>, ... ]

IMPORTANTE: Cuando se use en una $bucketAuto etapa, initArgs no puede referirse a la clave de grupo (es decir, no se puede usar la sintaxis $<fieldName>). En cambio, en una etapa $bucketAuto, solo puedes especificar valores constantes en initArgs.

string o código

Función utilizada para acumular documentos. La función accumulate recibe sus argumentos del estado actual y de la expresión de arreglo accumulateArgs. El resultado de la función accumulate se convierte en el nuevo estado. Puede especificar la definición de la función como tipo BSON Code o String.

La función accumulate tiene la siguiente forma:

function(state, <accumArg1>, <accumArg2>, ...) {
...
return <newState>
}

Arreglo

Argumentos pasados a la función accumulate. Puedes utilizar accumulateArgs para especificar qué valor(es) de campo pasar a la función accumulate.

accumulateArgs tiene la siguiente forma:

[ <accumArg1>, <accumArg2>, ... ]

string o código

Función utilizada para fusionar dos estados internos. merge debe ser un tipo BSON String o Code. merge devuelve el resultado combinado de los dos estados fusionados. Para obtener información sobre cuándo se llama a la función de fusión, consulta Fusionar dos estados con $merge.

La función merge tiene la siguiente forma:

function (<state1>, <state2>) {
<logic to merge state1 and state2>
return <newState>
}

string o código

opcional. Función utilizada para actualizar el resultado del acumulador.

La función finalize tiene la siguiente forma:

function (state) {
...
return <finalState>
}

String

El lenguaje utilizado en el código $accumulator.

IMPORTANTE: Actualmente, el único valor admitido para lang es js.

Los siguientes pasos describen cómo el operador $accumulator procesa los documentos:

  1. El operador comienza en un estado inicial, definido por la función init.

  2. Para cada documento, el operador actualiza el estado según la función accumulate. El primer argumento de la función accumulate es el estado actual, y se pueden especificar argumentos adicionales en el arreglo accumulateArgs.

  3. Cuando el operador necesita fusionar varios estados intermedios, ejecuta la función merge. Para obtener más información sobre cuándo se llama a la función de fusión, consulte Fusionar dos estados con $merge.

  4. Si se ha definido una función finalizar, una vez que se hayan procesado todos los documentos y se haya actualizado el estado en consecuencia, finalizar convierte el estado en una salida final.

Como parte de sus operaciones internas, el operador $accumulator puede necesitar fusionar dos estados intermedios separados. La función merge especifica cómo el operador debe fusionar dos estados.

La función merge siempre combina dos estados a la vez. En el evento de que haya que combinar más de dos estados, la combinación resultante de dos estados se combina con un único estado. Este proceso se repite hasta que se combinan todos los estados.

Por ejemplo, $accumulator puede necesitar combinar dos estados en los siguientes escenarios:

  • $accumulator Se ejecuta en un clúster fragmentado. El operador necesita fusionar los resultados de cada partición para obtener el resultado final.

  • Una sola operación de $accumulator supera su límite de memoria especificado. Si especifica la opción allowDiskUse, el operador almacena la operación en curso en disco y termina la operación en memoria. Una vez que la operación finalice, los resultados del disco y la memoria se fusionan utilizando la función merge.

El orden con el que MongoDB procesa documentos para las funciones init(), accumulate() y merge() puede variar y podría diferir del orden en que esos documentos se especifican para la función $accumulator.

Por ejemplo, considera una serie de documentos donde los campos _id son las letras del alfabeto:

{ _id: 'a' },
{ _id: 'b' },
{ _id: 'c' }
...
{ _id: 'z' }

A continuación, considere un pipeline de agregación que ordena los documentos por el campo _id y luego utiliza una función $accumulator para concatenar los valores del campo _id:

[
{
$sort: { _id: 1 }
},
{
$group: {
_id: null,
alphabet: {
$accumulator: {
init: function() {
return ""
},
accumulate: function(state, letter) {
return(state + letter)
},
accumulateArgs: [ "$_id" ],
merge: function(state1, state2) {
return(state1 + state2)
},
lang: "js"
}
}
}
}
]

MongoDB no garantiza que los documentos se procesen en orden clasificado, lo que significa que el campo alphabet no necesariamente se establecerá en abc...z.

Debido a este comportamiento, asegúrate de que tu función $accumulator no necesite procesar y retornar documentos en un orden específico.

Para usar $accumulator, debes habilitar el script del lado del servidor.

Si no utilizas $accumulator (o $function, $where o mapReduce), desactiva los scripts en el lado del servidor:

Consulta también ➤ Ejecutar MongoDB con opciones de configuración seguras.

MongoDB 6.0 mejora el motor interno JavaScript utilizado para JavaScript del lado del servidor, $accumulator, $function, y $where expresiones y de MozJS-60 a MozJS-91. Varias funciones de arreglos y strings obsoletas y no estándar que existían en MozJS-60 se eliminan en MozJS-91.

Nota

Este ejemplo muestra cómo utilizar el operador $accumulator para implementar el operador $avg que ya admite MongoDB. El objetivo de este ejemplo no es implementar una nueva funcionalidad, sino ilustrar el comportamiento y la sintaxis del operador $accumulator con una lógica familiar.

En mongosh, cree una colección de muestra llamada books con los siguientes documentos:

db.books.insertMany([
{ _id: 8751, title: "The Banquet", author: "Dante", copies: 2 },
{ _id: 8752, title: "Divine Comedy", author: "Dante", copies: 1 },
{ _id: 8645, title: "Eclogues", author: "Dante", copies: 2 },
{ _id: 7000, title: "The Odyssey", author: "Homer", copies: 10 },
{ _id: 7020, title: "Iliad", author: "Homer", copies: 10 }
])

La siguiente operación groups los documentos por author, y utiliza $accumulator para calcular el número promedio de copias en libros para cada autor:

db.books.aggregate([
{
$group :
{
_id : "$author",
avgCopies:
{
$accumulator:
{
init: function() { // Set the initial state
return { count: 0, sum: 0 }
},
accumulate: function(state, numCopies) { // Define how to update the state
return {
count: state.count + 1,
sum: state.sum + numCopies
}
},
accumulateArgs: ["$copies"], // Argument required by the accumulate function
merge: function(state1, state2) { // When the operator performs a merge,
return { // add the fields from the two states
count: state1.count + state2.count,
sum: state1.sum + state2.sum
}
},
finalize: function(state) { // After collecting the results from all documents,
return (state.sum / state.count) // calculate the average
},
lang: "js"
}
}
}
}
])

Esta operación devuelve el siguiente resultado:

{ _id: "Dante", avgCopies: 1.6666666666666667 }
{ _id: "Homer", avgCopies: 10 }

$accumulator define un estado inicial donde count y sum están ambos configurados a 0. Para cada documento que procesa el $accumulator, este actualiza el estado mediante:

  • Incrementando el count por 1 y

  • Agregar los valores del campo copies del documento al sum. La función accumulate puede acceder al campo copies porque se pasa en el campo accumulateArgs.

Con cada documento que se procesa, la función acumular devuelve el estado actualizado.

Una vez que todos los documentos hayan sido procesados, la función finalizar divide sum de las copias por count de los documentos para obtener el promedio. Esto remueve la necesidad de mantener un promedio calculado en tiempo real, ya que la función finalizar recibe el sum y count acumulados de todos los documentos.

Esta operación es equivalente al siguiente pipeline, que utiliza el operador $avg:

db.books.aggregate([
{
$group : {
_id : "$author",
avgCopies: { $avg: "$copies" }
}
}
])

Puedes utilizar la opción initArgs para variar el estado inicial de $accumulator. Esto puede ser útil si, por ejemplo, desea:

  • Utilice el valor de un campo que no esté en su estado para afectar su estado, o

  • Establece el estado inicial a un valor diferente según el grupo que se procese.

En mongosh, cree una colección de muestra llamada restaurants con los siguientes documentos:

db.restaurants.insertMany( [
{ _id: 1, name: "Food Fury", city: "Bettles", cuisine: "American" },
{ _id: 2, name: "Meal Macro", city: "Bettles", cuisine: "Chinese" },
{ _id: 3, name: "Big Crisp", city: "Bettles", cuisine: "Latin" },
{ _id: 4, name: "The Wrap", city: "Onida", cuisine: "American" },
{ _id: 5, name: "Spice Attack", city: "Onida", cuisine: "Latin" },
{ _id: 6, name: "Soup City", city: "Onida", cuisine: "Chinese" },
{ _id: 7, name: "Crave", city: "Pyote", cuisine: "American" },
{ _id: 8, name: "The Gala", city: "Pyote", cuisine: "Chinese" }
] )

Suponga que una aplicación permite a los usuarios query estos datos para encontrar restaurantes. Puede ser útil mostrar más resultados para la ciudad donde vive el usuario. Para este ejemplo, asumimos que la ciudad del usuario se almacena en una variable llamada userProfileCity.

La siguiente groups pipeline de agregación los documentos por city. La operación utiliza el $accumulator para mostrar un número diferente de resultados de cada ciudad, dependiendo de si la ciudad del restaurante coincide con la ciudad en el perfil del usuario:

Nota

Para ejecutar este ejemplo en mongosh, reemplace <userProfileCity> en initArgs con una string que contenga un valor real de ciudad, como Bettles.

1db.restaurants.aggregate([
2{
3 $group :
4 {
5 _id : { city: "$city" },
6 restaurants:
7 {
8 $accumulator:
9 {
10 init: function(city, userProfileCity) { // Set the initial state
11 return {
12 max: city === userProfileCity ? 3 : 1, // If the group matches the user's city, return 3 restaurants
13 restaurants: [] // else, return 1 restaurant
14 }
15 },
16
17 initArgs: ["$city", <userProfileCity>], // Argument to pass to the init function
18
19 accumulate: function(state, restaurantName) { // Define how to update the state
20 if (state.restaurants.length < state.max) {
21 state.restaurants.push(restaurantName);
22 }
23 return state;
24 },
25
26 accumulateArgs: ["$name"], // Argument required by the accumulate function
27
28 merge: function(state1, state2) {
29 return {
30 max: state1.max,
31 restaurants: state1.restaurants.concat(state2.restaurants).slice(0, state1.max)
32 }
33 },
34
35 finalize: function(state) { // Adjust the state to only return field we need
36 return state.restaurants
37 }
38
39 lang: "js"
40 }
41 }
42 }
43}
44])

Si el valor de userProfileCity es Bettles, esta operación arroja el siguiente resultado:

{ _id: { city: "Bettles" }, restaurants: { restaurants: [ "Food Fury", "Meal Macro", "Big Crisp" ] } }
{ _id: { city: "Onida" }, restaurants: { restaurants: [ "The Wrap" ] } }
{ _id: { city: "Pyote" }, restaurants: { restaurants: [ "Crave" ] } }

Si el valor de userProfileCity es Onida, esta operación arroja el siguiente resultado:

{ _id: { city: "Bettles" }, restaurants: { restaurants: [ "Food Fury" ] } }
{ _id: { city: "Onida" }, restaurants: { restaurants: [ "The Wrap", "Spice Attack", "Soup City" ] } }
{ _id: { city: "Pyote" }, restaurants: { restaurants: [ "Crave" ] } }

Si el valor de userProfileCity es Pyote, esta operación arroja el siguiente resultado:

{ _id: { city: "Bettles" }, restaurants: { restaurants: [ "Food Fury" ] } }
{ _id: { city: "Onida" }, restaurants: { restaurants: [ "The Wrap" ] } }
{ _id: { city: "Pyote" }, restaurants: { restaurants: [ "Crave", "The Gala" ] } }

Si el valor de userProfileCity es cualquier otro valor, esta operación devuelve el siguiente resultado:

{ _id: { city: "Bettles" }, restaurants: { restaurants: [ "Food Fury" ] } }
{ _id: { city: "Onida" }, restaurants: { restaurants: [ "The Wrap" ] } }
{ _id: { city: "Pyote" }, restaurants: { restaurants: [ "Crave" ] } }

La función init define un estado inicial que contiene max y restaurants campos. El campo max establece la cantidad máxima de restaurantes para ese grupo en particular. Si el campo city del documento coincide con userProfileCity, ese grupo contiene un máximo de 3 restaurantes. De lo contrario, si el documento _id no coincide con userProfileCity, el grupo contiene como máximo un solo restaurante. La función init recibe ambos argumentos city userProfileCity del initArgs arreglo.

Para cada documento que $accumulator procesa, se inserta el name del restaurante en el arreglo restaurants, siempre que ese nombre no exceda la longitud de restaurants respecto al valor max. Con cada documento que se procesa, la función acumular retorna el estado actualizado.

La función merge define cómo fusionar dos estados. La función concatena los restaurant arreglos de cada estado, y la longitud del arreglo resultante se limita usando el slice() método para asegurarse de que no supere el valor de max.

Una vez procesados todos los documentos, la función de finalización modifica el estado resultante para que solo devuelva los nombres de los restaurantes. Sin esta función, el campo max también se incluiría en el resultado, lo que no satisface ninguna necesidad de la aplicación.

Volver

Acumuladores

En esta página