On this page本页内容
$accumulator
New in version 4.4.在版本4.4中新增。
Defines 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 使用$accumulator
operator to execute your own JavaScript functions to implement behavior not supported by the MongoDB Query Language. $accumulator
运算符执行您自己的JavaScript函数,以实现MongoDB查询语言不支持的行为。See also 另请参见$function
.$function
。
$accumulator
is available in these stages:在以下阶段可用:
Executing JavaScript inside of an aggregation operator may decrease performance. 在聚合运算符内部执行JavaScript可能会降低性能。Only use the 仅当提供的管道运算符不能满足应用程序的需要时,才使用$accumulator
operator if the provided pipeline operators cannot fulfill your application's needs.$accumulator
运算符。
The $accumulator
operator has this syntax:$accumulator
运算符具有以下语法:
{ $accumulator: { init: <code>, initArgs: <array expression>, // Optional accumulate: <code>, accumulateArgs: <array expression>, merge: <code>, finalize: <code>, // Optional lang: <string> } }
init | String or Code |
function (<initArg1>, <initArg2>, ...) { ... return <initialState> } |
initArgs | Array |
[ <initArg1>, <initArg2>, ... ]
|
accumulate | String or Code |
function(state, <accumArg1>, <accumArg2>, ...) { ... return <newState> } |
accumulateArgs | Array |
[ <accumArg1>, <accumArg2>, ... ] |
merge | String or Code |
function (<state1>, <state2>) { <logic to merge state1 and state2> return <newState> } |
finalize | String or Code |
function (state) { ... return <finalState> } |
lang | String |
|
The following steps outline how the 以下步骤概述了$accumulator
operator processes documents:$accumulator
运算符如何处理文档:
init
函数定义。accumulate
函数更新状态。accumulate
函数的第一个参数是当前状态,可以在accumulateArgs
数组中指定其他参数。merge
函数。$merge
.merge
函数的详细信息,请参阅使用$merge
合并两个状态。finalize
函数,则在处理完所有文档并相应更新状态后,finalize
将状态转换为最终输出。$merge
$Merge
合并两个状态As part of its internal operations, the 作为内部操作的一部分,$accumulator
operator may need to merge two separate, intermediate states. $accumulator
运算符可能需要合并两个独立的中间状态。The merge function specifies how the operator should merge two states.merge
函数指定运算符应如何合并两个状态。
For example, 例如,$accumulator
may need to combine two states when:$accumulator
可能需要在以下情况下组合两种状态:
$accumulator
A single 单个$accumulator
operation exceeds its specified memory limit. $accumulator
操作超过其指定的内存限制。If you specify the 如果指定allowDiskUse
option, the operator stores the in-progress operation on disk and finishes the operation in memory. allowDiskUse
选项,则运算符将正在进行的操作存储在磁盘上,并在内存中完成操作。Once the operation finishes, the results from disk and memory are merged together using the merge function.操作完成后,使用merge函数将磁盘和内存的结果合并在一起。
To use 要使用$accumulator
, you must have server-side scripting enabled.$accumulator
,必须启用服务器端脚本。
If you do not use 如果不使用$accumulator
(or $function
, $where
, or mapReduce
), disable server-side scripting:$accumulator
(或$function
、$where
或mapReduce
),请禁用服务器端脚本:
mongod
instance, see security.javascriptEnabled
configuration option or --noscripting
command-line option.mongod
实例,请参阅security.javascriptEnabled
配置选项或--noscripting
命令行选项。For a mongos
instance, see security.javascriptEnabled
configuration option or the --noscripting
command-line option starting in MongoDB 4.4.
mongos
instances.mongos
实例上执行JavaScript。See also ➤ Run MongoDB with Secure Configuration Options.请参阅➤ 使用安全配置选项运行MongoDB。
$accumulator
to Implement the $avg
Operator$accumulator
实现$avg
运算符This example walks through using the 本示例演示了如何使用$accumulator
operator to implement the $avg
operator, which is already supported by MongoDB. $accumulator
运算符实现$avg
运算符,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.$accumulator
运算符的行为和语法。
In 在mongosh
, create a sample collection named books
with the following documents:mongosh
中,使用以下文档创建名为books
的示例集合:
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 } ])
The following operation 下面的操作按groups
the documents by author
, and uses $accumulator
to compute the average number of copies across books for each author:author
对文档进行分组,并使用$accumulator
计算每个作者的跨书平均副本数:
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" } } } } ])
This operation returns the following result:此操作返回以下结果:
{ "_id" : "Dante", "avgCopies" : 1.6666666666666667 } { "_id" : "Homer", "avgCopies" : 10 }
The $accumulator
defines an initial state where count
and sum
are both set to 0
. $accumulator
定义了一个初始状态,其中count
和sum
都设置为0。For each document that the 对于$accumulator
processes, it updates the state by:$accumulator
处理的每个文档,它通过以下方式更新状态:
count
by 1 andcount
递增1,并且copies
field to the sum
. copies
字段的值添加到sum
。copies
field because it is passed in the accumulateArgs field.copies
字段,因为它是在accumulateArgs字段中传递的。With each document that is processed, the accumulate function returns the updated state.对于处理的每个文档,累积函数返回更新的状态。
Once all documents have been processed, the finalize function divides the 处理完所有文档后,finalize函数将副本sum
of the copies by the count
of documents to obtain the average. sum
除以文档count
,以获得平均值。This removes the need to keep a running computed average, since the finalize function receives the cumulative 这消除了保持运行计算平均值的需要,因为finalize函数接收所有文档的sum
and count
of all documents.sum
和和count
。
$avg
$avg
的比较This operation is equivalent to the following pipeline, which uses the 此操作相当于使用$avg
operator:$avg
运算符的以下管道:
db.books.aggregate([ { $group : { _id : "$author", avgCopies: { $avg: "$copies" } } } ])
initArgs
to Vary the Initial State by GroupinitArgs
按组更改初始状态You can use the initArgs option in to vary the initial state of 您可以使用中的$accumulator
. initArgs
选项更改$accumulator
的初始状态。This can be useful if you want to, for example:例如,如果您希望:
In 在mongosh
, create a sample collection named restaurants
with the following documents:mongosh
中,使用以下文档创建名为restaurants
的样本集合:
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" } ])
Suppose an application allows users to query this data to find restaurants. 假设一个应用程序允许用户查询这些数据以查找餐馆。It may be useful to show more results for the city where the user lives. 显示用户居住的城市的更多结果可能是有用的。For this example, we assume that the user's city is called in a variable called 对于本例,我们假设在名为userProfileCity
.userProfileCity
的变量中调用用户的城市。
The following aggregation pipeline 以下聚合管道按groups
the documents by city
. city
对文档进行分组。The operation uses the 该操作使用$accumulator
to display a different number of results from each city depending on whether the restaurant's city matches the city in the user's profile:$accumulator
显示来自每个城市的不同数量的结果,具体取决于餐厅所在城市是否与用户配置文件中的城市匹配:
db.restaurants.aggregate([ { $group : { _id : { city: "$city" }, restaurants: { $accumulator: { init: function(city, userProfileCity) { // Set the initial state return { max: city === userProfileCity ? 3 : 1, // If the group matches the user's city, return 3 restaurants restaurants: [] // else, return 1 restaurant } }, initArgs: ["$city", <userProfileCity>], // Argument to pass to the init function accumulate: function(state, restaurantName) { // Define how to update the state if (state.restaurants.length < state.max) { state.restaurants.push(restaurantName); } return state; }, accumulateArgs: ["$name"], // Argument required by the accumulate function merge: function(state1, state2) { return { max: state1.max, restaurants: state1.restaurants.concat(state2.restaurants).slice(0, state1.max) } }, finalize: function(state) { // Adjust the state to only return field we need return state.restaurants } lang: "js" } } } } ])
If the value of 如果userProfileCity
is Bettles
, this operation returns the following result:userProfileCity
的值为Bettles
,则此操作将返回以下结果:
{ "_id" : { "city" : "Bettles" }, "restaurants" : { "restaurants" : [ "Food Fury", "Meal Macro", "Big Crisp" ] } } { "_id" : { "city" : "Onida" }, "restaurants" : { "restaurants" : [ "The Wrap" ] } } { "_id" : { "city" : "Pyote" }, "restaurants" : { "restaurants" : [ "Crave" ] } }
If the value of 如果userProfileCity
is Onida
, this operation returns the following result:userProfileCity
的值为Onida
,则此操作将返回以下结果:
{ "_id" : { "city" : "Bettles" }, "restaurants" : { "restaurants" : [ "Food Fury" ] } } { "_id" : { "city" : "Onida" }, "restaurants" : { "restaurants" : [ "The Wrap", "Spice Attack", "Soup City" ] } } { "_id" : { "city" : "Pyote" }, "restaurants" : { "restaurants" : [ "Crave" ] } }
If the value of 如果userProfileCity
is Pyote
, this operation returns the following result:userProfileCity
的值为Pyote
,则此操作将返回以下结果:
{ "_id" : { "city" : "Bettles" }, "restaurants" : { "restaurants" : [ "Food Fury" ] } } { "_id" : { "city" : "Onida" }, "restaurants" : { "restaurants" : [ "The Wrap" ] } } { "_id" : { "city" : "Pyote" }, "restaurants" : { "restaurants" : [ "Crave", "The Gala" ] } }
If the value of 如果userProfileCity
is any other value, this operation returns the following result:userProfileCity
的值是任何其他值,则此操作将返回以下结果:
{ "_id" : { "city" : "Bettles" }, "restaurants" : { "restaurants" : [ "Food Fury" ] } } { "_id" : { "city" : "Onida" }, "restaurants" : { "restaurants" : [ "The Wrap" ] } } { "_id" : { "city" : "Pyote" }, "restaurants" : { "restaurants" : [ "Crave" ] } }
The init function defines an initial state containing max
and restaurants
fields. init
函数定义包含max
和restaurants
字段的初始状态。The max
field sets the maximum number of restaurants for that particular group. max
字段设置该特定组的最大餐厅数。If the document's 如果文档的city
field matches userProfileCity
, that group contains a maximum of 3 restaurants. city
字段与userProfileCity
匹配,则该组最多包含3家餐厅。Otherwise, if the document 否则,如果文档_id
does not match userProfileCity
, the group contains at most a single restaurant. _id
与userProfileCity
不匹配,则该组最多包含一个餐厅。The init function receives both the city
userProfileCity
arguments from the initArgs array.init
函数从initArgs数组接收city和userProfileCity参数。
For each document that the 对于$accumulator
processes, it pushes the name
of the restaurant to the restaurants
array, provided that name would not put the length of restaurants
over the max
value. $accumulator
处理的每个文档,它将餐馆的name
推送到restaurants
数组,前提是该名称不会使restaurants
的长度超过max
值。With each document that is processed, the accumulate function returns the updated state.对于处理的每个文档,accumulate
函数返回更新的状态。
The merge function defines how to merge two states. merge
函数定义如何合并两个状态。The function concatenates the 该函数将每个状态的restaurant
arrays from each state together, and the length of the resulting array is limited using the slice() method to ensure that it does not exceed the max
value.restaurant
数组连接在一起,并使用slice()
方法限制结果数组的长度,以确保其不超过最大值。
Once all documents have been processed, the finalize function modifies the resulting state to only return the names of the restaurants. 处理完所有文档后,finalize
函数将修改结果状态,以仅返回餐厅名称。Without this function, the 如果没有此功能,max
field would also be included in the output, which does not fulfill any needs for the application.max
字段也将包含在输出中,这不能满足应用程序的任何需求。