定義
- $accumulator
- カスタム アキュムレータ演算子を定義します。アキュムレーターは、ドキュメントがパイプラインを通って処理される際に、ドキュメントの状態(たとえば、合計、最大、最小、および関連データ)を維持する演算子です。MongoDB のクエリ言語でサポートされていない動作を実装するには、 - $accumulator演算子を使用して独自の JavaScript 関数を実行します。- $functionも参照してください。- $accumulatorは、次のステージで使用できます。- 重要- 集計演算子内で JavaScript を実行すると、パフォーマンスが低下する可能性があります。 - $accumulator提供された パイプライン演算子 が アプリケーションのニーズを満たせない場合にのみ、 演算子を使用してください。
構文
$accumulator演算子の構文は次のとおりです。
{   $accumulator: {     init: <code>,     initArgs: <array expression>,        // Optional     accumulate: <code>,     accumulateArgs: <array expression>,     merge: <code>,     finalize: <code>,                    // Optional     lang: <string>   } } 
| フィールド | タイプ | 説明 | ||||
|---|---|---|---|---|---|---|
| 文字列またはコード | 状態を初期化するために使用される関数。  
 ディスクにスピルしたり、シャーディングされたクラスターでクエリを実行したりすると、アキュムレータが複数のサブアキュムレーションのマージとして計算される場合があります。この場合の各サブアキュムレーションは  | |||||
| 配列 | オプション。 
 重要:  | |||||
| 文字列またはコード | ドキュメントを累積するために使用される関数。  
  | |||||
| 配列 | 
 
  | |||||
| 文字列またはコード | 2 つの内部状態をマージするために使用される関数。  
  | |||||
| 文字列またはコード | オプション。アキュムレーションの結果を更新するために使用される関数。 
  | |||||
| 文字列 | 
 重要: 現在、 | 
動作
次の手順は、 $accumulator演算子がドキュメントを処理する方法の概要です。
- 演算子は、init 関数によって定義された初期状態から開始されます。 
- 各ドキュメントについて、 演算子はaccumulate関数に基づいて状態をアップデートします。 accumulate関数の最初の引数は現在の状態で、追加の引数はaccumulateArgs配列で指定します。 
- 演算子が複数の中間状態をマージする必要がある場合、マージ関数を実行します。 マージ関数が呼び出されるタイミングの詳細については、「 - $mergeを使用して 2 つの状態をマージする 」を参照してください。
- finalize 関数が定義されている場合、すべてのドキュメントが処理され、それに応じて状態がアップデートされると、finalize は状態を最終出力に変換します。 
2 つの状態をマージ $merge
$accumulator演算子は、内部操作の一部として、2 つの個別の中間状態をマージする必要がある場合があります。 merge関数は、演算子が 2 つの状態をマージする方法を指定します。
merge 関数は常に、2 つの状態を一度にマージします。2 つ以上の状態をマージする必要がある場合、2 つの状態がマージされたものが 1 つの状態にマージされます。すべての状態がマージされるまで、このプロセスが繰り返されます。
たとえば、次のシナリオでは$accumulatorは 2 つの状態を組み合わせる必要がある場合があります。
- $accumulatorはシャーディングされたクラスターで実行されます。演算子は、最終結果を得るために各シャードからの結果をマージする必要があります。
- 単一の - $accumulator操作が指定されたメモリ制限を超えています。- allowDiskUseオプションを指定すると、 演算子は進行中の操作をディスクに保存し、メモリ内で操作を終了します。 操作が完了すると、ディスクとメモリの結果が マージ関数を使用してまとめられます。
ドキュメント処理順序
MongoDB がinit() 、 accumulate() 、 merge()関数のドキュメントを処理する順序はさまざまであり、それらのドキュメントが$accumulator関数に指定される順序と異なる場合があります。
たとえば、 _idフィールドがアルファベットの文字である一連のドキュメントを考えてみましょう。
{ _id: 'a' }, { _id: 'b' }, { _id: 'c' } ... { _id: 'z' } 
次に、ドキュメントを_idフィールドでソートし、 $accumulator関数を使用して_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 では、ドキュメントがソート順で処理されることは保証されません。つまり、 alphabetフィールドは必ずしもabc...zに設定されるわけではありません。
この動作により、 $accumulator関数ではドキュメントを特定の順序で処理して返す必要がなくなりました。
Javascript有効
$accumulatorを使用するには、サーバー側スクリプトを有効にする必要があります。
$accumulator(または $function、$where、mapReduce)を使用しない場合は、サーバー側スクリプトを無効にします。
- mongodインスタンスについては、- security.javascriptEnabled構成オプションまたは- --noscriptingコマンドライン オプションを参照してください。
- mongosインスタンスについては、- security.javascriptEnabled構成オプションまたは- --noscriptingコマンドライン オプションを参照してください。In earlier versions, MongoDB does not allow JavaScript execution on- mongosinstances.
➤ 安全な構成オプションを使用して MongoDB を実行するも参照してください。
サポートされていない配列関数と文字列関数
MongoDB 6.0 では、サーバーサイド JavaScript 、 $accumulator 、 $function 、 $where式に使用される内部 JavaScript エンジンが MozJS- 60から MozJS- 91 にアップグレードされています。MozJS-60 に存在した非推奨、非標準の配列関数と文字列関数は、MozJS-91 で一部削除されました。
例
$accumulatorを使用して 演算子を実装$avg
注意
この例では、 $accumulator演算子を使用して$avg演算子を実装する方法について説明します。この演算子は MongoDB ですでにサポートされています。 この例の目的は新しい機能を実装することではなく、 $accumulator演算子の動作と構文を既知のロジックで説明することです。
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 } ]) 
次の操作では、ドキュメントをauthorごとにgroupsし、 $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"       }     }   } } ]) 
結果
この操作は、次の結果を返します。
{ _id: "Dante", avgCopies: 1.6666666666666667 } { _id: "Homer", avgCopies: 10 } 
動作
$accumulatorは、 countとsumの両方が0に設定されている初期状態を定義します。 $accumulatorが処理するドキュメントごとに、状態を次の方法で更新します。
- countを1増やし、
- Adding the values of the document's - copiesfield to the- sum. accumulate関数は- copiesフィールドにアクセスできます。これはaccumulateArgsフィールドで渡されるためです。
プロセスされるドキュメントごとに、accumulate 関数はアップデートした状態を返します。
すべてのドキュメントが処理されると、 finalize関数はコピーのsumをドキュメントのcountで割って平均を取得します。 This removes the need to keep a running computed average, since the finalize function receives the cumulative sum and count of all documents.
との比較 $avg
この操作は、 $avg演算子を使用する次のパイプラインと同等です。
db.books.aggregate([ {   $group : {     _id : "$author",     avgCopies: { $avg: "$copies" }   } } ]) 
initArgsを使用して初期状態をグループごとに変更する
でinitArgsオプションを使用すると、 $accumulatorの初期状態を変更できます。 これは、たとえば次のような場合に役立ちます。
- 自分の状態にないフィールドの値を使用して自分の状態に影響を与えるか、または 
- 現在処理中のグループに基づいて、初期状態を異なる値に設定します。 
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" } ] ) 
あるアプリケーションで、ユーザーがこのデータをクエリしてレストランを検索できるとします。ユーザーが住んでいる都市の結果表示数を増やすと便利な場合があります。この例では、ユーザーの都市がuserProfileCityという変数で呼び出されることを想定します。
次の集計パイプラインは、 cityのドキュメントをgroupsします。 この操作では$accumulatorを使用して、レストランの都市がユーザーのプロファイル内の都市と一致するかどうかに応じて、各都市からの異なる数の結果が表示されます。
1 db.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 ]) 
結果
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" ] } } 
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" ] } } 
userProfileCityの値が Pyoteの場合、この操作は以下の結果を返します。
{ _id: { city: "Bettles" }, restaurants: { restaurants: [ "Food Fury" ] } } { _id: { city: "Onida" }, restaurants: { restaurants: [ "The Wrap" ] } } { _id: { city: "Pyote" }, restaurants: { restaurants: [ "Crave", "The Gala" ] } } 
userProfileCity の値がその他の値の場合、この操作は次の結果を返します。
{ _id: { city: "Bettles" }, restaurants: { restaurants: [ "Food Fury" ] } } { _id: { city: "Onida" }, restaurants: { restaurants: [ "The Wrap" ] } } { _id: { city: "Pyote" }, restaurants: { restaurants: [ "Crave" ] } } 
動作
初期max restaurants化関数は、 フィールドと フィールドを含む初期状態を定義します。maxフィールドには、その特定のグループのレストランの最大数が設定されます。 ドキュメントのcityフィールドがuserProfileCityと一致する場合、そのグループには最大3レストランが含まれます。 そうではなく、ドキュメント_idがuserProfileCityと一致しない場合、グループには最大で 1 件のレストランが含まれます。 初期化関数は、 initArgs配列からcity userProfileCity引数の両方を受け取ります。
$accumulatorが処理するドキュメントごとに、レストランのnameをrestaurants配列にプッシュします。ただし、その名前によって、 restaurantsの長さがmax値を超えない場合に限ります。 プロセスされるドキュメントごとに、 accumulate関数はアップデートした状態を返します。
merge 関数は、2 つの状態をマージする方法を定義します。この関数は各州のrestaurant 配列をまとめて連結し、結果の配列の長さは slice() メソッドを使用して制限され、max 値を超えないようにします。
すべてのドキュメントが処理されると、 finalize関数は結果の状態を変更し、レストランの名前のみを返すようにします。 この関数を使用しない場合、 maxフィールドも出力に含まれるため、アプリケーションのニーズは満たされません。