Definição
$accumulatorDefines a custom accumulator operator. Accumulators are operators that maintain their state (e.g. totals, maximums, minimums, and related data) as documents progress through the pipeline. Use the
$accumulatoroperator to execute your own JavaScript functions to implement behavior not supported by the MongoDB Query Language. See also$function.$accumulatorestá disponível nestes estágios:Importante
Executing JavaScript inside of an aggregation operator may decrease performance. Only use the
$accumulatoroperator if the provided pipeline operators cannot fulfill your application's needs.
Sintaxe
The $accumulator operator has this syntax:
{ $accumulator: { init: <code>, initArgs: <array expression>, // Optional accumulate: <code>, accumulateArgs: <array expression>, merge: <code>, finalize: <code>, // Optional lang: <string> } }
Campo | Tipo | Descrição | ||||
|---|---|---|---|---|---|---|
String ou código | Função usada para inicializar o estado. A função A função O derramamento no disco ou a execução de uma query em um cluster fragmentado pode fazer com que o acumulador seja computado como uma mescla de vários subacumuladores, cada um dos quais começa chamando | |||||
Array | Opcional. Argumentos passados para a função
IMPORTANTE: Quando usado em um estágio | |||||
String ou código | Function used to accumulate documents. The A função | |||||
Array | Argumentos passados para a função
| |||||
String ou código | Function used to merge two internal states. A função | |||||
String ou código | Opcional. Função usada para atualizar o resultado do acúmulo. A função | |||||
String | The language used in the IMPORTANTE: Atualmente, o único valor suportado para |
Comportamento
The following steps outline how the $accumulator operator processes documents:
The operator begins at an initial state, defined by the init function.
Para cada documento, o operador atualiza o estado com base na função accumulate . O primeiro argumento da função acumular é o estado atual, e argumentos adicionais são especificados na array accumulateArgs.
When the operator needs to merge multiple intermediate states, it executes the merge function. For more information on when the merge function is called, see Merge Two States with
$merge.
Mesclar dois estados com $merge
As part of its internal operations, the $accumulator operator may need to merge two separate, intermediate states. The merge function specifies how the operator should merge two states.
A função de mesclagem sempre mescla dois estados de cada vez. Se mais de dois estados precisarem ser mesclados, a fusão resultante de dois estados será mesclada com um único estado. Esse processo se repete até que todos os estados sejam mesclados.
For example, $accumulator may need to combine two states in the following scenarios:
$accumulatoré executado em um cluster fragmentado. O operador precisa mesclar os resultados de cada fragmento para obter o resultado final.A single
$accumulatoroperation exceeds its specified memory limit. If you specify theallowDiskUseoption, the operator stores the in-progress operation on disk and finishes the operation in memory. Once the operation finishes, the results from disk and memory are merged together using the merge function.
Ordem de processamento de documentos
A ordem em que o MongoDB processa documentos para as funções init(), accumulate() e merge() pode variar e ser diferente da ordem em que esses documentos são especificados para a função $accumulator.
Por exemplo, considere uma série de documentos em que os campos _id são as letras do alfabeto:
{ _id: 'a' }, { _id: 'b' }, { _id: 'c' } ... { _id: 'z' }
Em seguida, considere um pipeline de agregação que classifica os documentos pelo campo _id e usa uma função $accumulator para concatenar os valores do 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" } } } } ]
O MongoDB não garante que os documentos sejam processados na ordem de classificação, o que significa que o campo alphabet não é necessariamente definido como abc...z.
Devido a esse comportamento, garanta que sua função $accumulator não precise processar e retornar documentos em uma ordem específica.
Javascript ativado
To use $accumulator, you must have server-side scripting enabled.
If you do not use $accumulator (or $function, $where, or mapReduce), disable server-side scripting:
Para uma instância
mongod, consulte opção de configuraçãosecurity.javascriptEnabledou opção de linha de comando--noscripting.Para instâncias do
mongos, consulte a opção de configuraçãosecurity.javascriptEnabledou a opção de linha de comando--noscripting.Em versões anteriores, o MongoDB não permite a execução de JavaScript em instâncias
mongos.
Consulte também ➤ Executar o MongoDB com opções de configuração seguras.
Funções de array e string não suportadas
MongoDB 6.0 upgrades the internal JavaScript engine used for server-side JavaScript, $accumulator, $function, and $where expressions and from MozJS-60 to MozJS-91. Several deprecated, non-standard array and string functions that existed in MozJS-60 are removed in MozJS-91.
Exemplos
Os exemplos nesta página usam dados do conjunto de dados sample_mflix. Para obter detalhes sobre como carregar este conjunto de dados em sua implantação do MongoDB autogerenciado, consulte Carregar o conjunto de dados de amostra. Se você fez alguma modificação nos bancos de dados de amostra, talvez seja necessário descartar e recriar os bancos de dados para executar os exemplos nesta página.
Usar $accumulator para implementar o $avg operador
Observação
This example walks through using the $accumulator operator to implement the $avg operator, which is already supported by MongoDB. The goal of this example is not to implement new functionality, but to illustrate the behavior and syntax of the $accumulator operator with familiar logic.
A operação a seguir groups os documentos por rated e usa $accumulator para calcular a média de imdb.rating para cada classificação de conteúdo. O estágio $match restringe a entrada às classificações de conteúdo G, PG, PG-13 e R e exclui documentos em que imdb.rating não é duplo, como uma string vazia:
db.movies.aggregate( [ { $match: { rated: { $in: [ "G", "PG", "PG-13", "R" ] }, "imdb.rating": { $type: "double" } } }, { $group: { _id: "$rated", avgRating: { $accumulator: { init: function() { // Set the initial state return { count: 0, sum: 0 } }, accumulate: function(state, rating) { // Define how to update the state return { count: state.count + 1, sum: state.sum + rating } }, accumulateArgs: [ "$imdb.rating" ], // 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" } } } }, { $sort: { _id: 1 } } ] )
[ { _id: 'G', avgRating: 6.628092243186578 }, { _id: 'PG', avgRating: 6.43525917926564 }, { _id: 'PG-13', avgRating: 6.404911676001717 }, { _id: 'R', avgRating: 6.456657633242984 } ]
Comportamento
The $accumulator defines an initial state where count and sum are both set to 0. For each document that the $accumulator processes, it updates the state by:
Incrementando o
countpor 1 eAdding the values of the document's
imdb.ratingfield to thesum. The accumulate function can access theimdb.ratingfield because it is passed in the accumulateArgs field.
Com cada documento processado, a função de acumulação retorna o estado atualizado.
Depois que todos os documentos tiverem sido processados, a função finalizar divide o sum das classificações pelo count dos documentos para obter a média. Isso remove a necessidade de manter uma média computada em execução, pois a função finalizar recebe o sum cumulativo e o count de todos os documentos.
Comparação com $avg
Esta operação é equivalente à seguinte pipeline, que utiliza o operador $avg:
db.movies.aggregate( [ { $match: { rated: { $in: [ "G", "PG", "PG-13", "R" ] }, "imdb.rating": { $type: "double" } } }, { $group: { _id: "$rated", avgRating: { $avg: "$imdb.rating" } } }, { $sort: { _id: 1 } } ] )
[ { _id: 'G', avgRating: 6.6280922431865825 }, { _id: 'PG', avgRating: 6.435259179265659 }, { _id: 'PG-13', avgRating: 6.404911676001723 }, { _id: 'R', avgRating: 6.456657633242999 } ]
Usar initArgs para variar o estado inicial por grupo
Você pode utilizar a opção initArgs para variar o estado inicial de $accumulator. Isso pode ser útil se você quiser, por exemplo:
Usar o valor de um campo que não está em seu estado para afetar seu estado, ou
Definir o estado inicial para um valor diferente com base no grupo que está sendo processado.
Suponha que um aplicação permita que os usuários consultem esses dados para encontrar filmes. Pode ser útil mostrar mais resultados para a classificação de conteúdo que o usuário prefere. Para este exemplo, a classificação do conteúdo preferencial do usuário é PG-13, que é passada para a função init como userProfileRated.
O seguinte pipeline de agregação groups os documentos por rated. A operação utiliza o $accumulator para exibir um número diferente de filmes de cada classificação de conteúdo, dependendo se a classificação do filme corresponde à classificação no perfil do usuário:
Observação
Para executar esse exemplo com uma classificação de conteúdo preferencial diferente, substitua "PG-13" na array initArgs por outro valor, como "R".
db.movies.aggregate( [ { $match: { rated: { $in: [ "G", "PG", "PG-13", "R" ] }, "imdb.rating": { $type: "double" } } }, { $sort: { "imdb.rating": -1, title: 1 } }, { $group: { _id: { rated: "$rated" }, movies: { $accumulator: { init: function(rated, userProfileRated) { // Set the initial state return { max: rated === userProfileRated ? 3 : 1, // If the group matches the user's rating, movies: [] // return 3 movies. Otherwise, return 1. } }, initArgs: [ "$rated", "PG-13" ], // Argument to pass to the init function accumulate: function(state, movieTitle) { // Define how to update the state if (state.movies.length < state.max) { state.movies.push(movieTitle); } return state; }, accumulateArgs: [ "$title" ], // Argument required by the accumulate function merge: function(state1, state2) { return { max: state1.max, movies: state1.movies.concat(state2.movies).slice(0, state1.max) } }, finalize: function(state) { // Adjust the state to only return the field we need return state.movies }, lang: "js" } } } }, { $sort: { "_id.rated": 1 } } ] )
[ { _id: { rated: 'G' }, movies: [ 'Modern Times' ] }, { _id: { rated: 'PG' }, movies: [ 'Life' ] }, { _id: { rated: 'PG-13' }, movies: [ 'A Brave Heart: The Lizzie Velasquez Story', 'The Dark Knight', 'The Lord of the Rings: The Return of the King' ] }, { _id: { rated: 'R' }, movies: [ 'The Shawshank Redemption' ] } ]
Comportamento
A função init define um estado inicial que contém os campos max e movies. O campo max define o número máximo de filmes para este grupo em particular. Se o campo rated do documento corresponder a userProfileRated, esse grupo conterá um máximo de 3 filmes. Caso contrário, se o campo rated do documento não corresponder a userProfileRated, o grupo conterá no máximo um único filme. A função init recebe os argumentos rated e userProfileRated da arrayinitArgs.
Para cada documento que o $accumulator processa, ele empurra o title do filme para a array movies, se esse título não colocar o comprimento de movies sobre o valor max. Com cada documento processado, a função de acumular retorna o estado atualizado.
A função de mesclagem define como mesclar dois estados. A função concatena as movies arrays de cada estado, e o comprimento da array resultante é limitado usando o método slice () para garantir que ela não exceda o max valor.
Depois que todos os documentos forem processados, a função finalizar modifica o estado resultante para retornar apenas os títulos dos filmes. Sem essa função, o campo max também seria incluído na saída, o que não atende a nenhuma necessidade do aplicação.