クイックフィックス:解決策
The field 'total' must be an accumulator object というエラーが表示されるのは、MongoDB が複数の値を 1 つのグループにまとめる方法を判断できないためです。$group ステージでは、フィールド値を直接割り当てることはできません。データの結合方法をエンジンに伝えるために、$sum、$push、$first などのアキュムレータ演算子を使用する必要があります。
誤った例:
db.orders.aggregate([
{
$group: {
_id: "$customerId",
total: "$amount" // ここでエラーが発生します
}
}
])
修正例:
db.orders.aggregate([
{
$group: {
_id: "$customerId",
total: { $sum: "$amount" } // これで MongoDB は合算方法を認識します
}
}
])
覚えておいてください:$group ステージのすべてのフィールド(_id を除く)は、認識されたアキュムレータ演算子をちょうど 1 つ含むオブジェクトである必要があります。
なぜこのエラーが発生するのか
$group ステージを「じょうご(漏斗)」と考えてみてください。MongoDB が ID に基づいて 100 個の異なるドキュメントを 1 つのバケツに集めるとき、それらのドキュメント内の特定のフィールドをどう処理すべきかという指示が必要です。単純な 1 対 1 の割り当てができる $project ステージとは異なり、$group ステージではデータをマージするための戦略が必要になります。
単純な文字列("$amount" など)やハードコードされた数値(10 など)を渡すと、エンジンは混乱してしまいます。エンジンは、ドル記号($)で始まる演算子を含むオブジェクトを期待しています。その演算子がないと、集計パイプラインは即座に停止します。
このエラーを引き起こす 3 つの一般的なパターン
1. フィールドをそのまま「パススルー」しようとする
開発者は、カテゴリ名のように、グループ内のすべてのドキュメントで同じであることがわかっているフィールドを含めたいと考えることがよくあります。しかし、それでも単に参照するだけでは不十分です。
誤り: productName: "$name"
解決策: $first または $last を使用して、MongoDB がそのグループで最初に見つけたドキュメントから値を取得します。
$group: {
_id: "$productId",
productName: { $first: "$name" },
totalStock: { $sum: "$quantity" }
}
2. SQL の「COUNT」の癖
SQL に慣れている場合、カウント用のフィールドに静的な整数を設定しようとするかもしれません。MongoDB では、グループステージ内でそれは機能しません。
誤り: userCount: 1
解決策: $sum を使用し、数値の 1 を渡します。これにより、MongoDB は処理するドキュメントごとに合計を 1 ずつ増やすようになります。
$group: {
_id: "$city",
userCount: { $sum: 1 }
}
3. 演算子の単純なタイポ(入力ミス)
演算子のスペルミスは致命的です。$sum の代わりに $summm と入力すると、MongoDB はそれを有効なアキュムレータとして認識しません。カスタムオブジェクトを渡そうとしていると見なされますが、ここでは許可されていません。
一般的なニーズに対する実践的な修正
合計の計算
数値フィールドには $sum を使用します。ある地域の売上記録が 50 件ある場合、これを 1 つの総売上額にまとめます。
$group: {
_id: "$region",
totalRevenue: { $sum: "$sales" }
}
リストの作成
特定のロールを持つすべてのユーザー名のリストを表示したいですか? $push を使用してすべての値の配列を作成するか、リストを一意に保ちたい(重複なし)場合は $addToSet を使用します。
$group: {
_id: "$role",
allUsers: { $push: "$username" }
}
平均値と極値の検索
データ分析には、$avg、$min、$max を使用します。これらは、都市の最高気温や、1 か月の平均注文額を見つけるのに最適です。
$group: {
_id: "$month",
highestTemp: { $max: "$temp" },
averageOrder: { $avg: "$price" }
}
修正を確認する方法
推測するだけでなく、次の 3 つの手順でパイプラインをテストしてください。
- mongosh でテストする:
$groupステージだけを MongoDB Shell にコピーします。赤いエラーメッセージの代わりにデータが返されれば、構文は正しいです。 - 実行計画(Explain Plan)を確認する: クエリに
.explain("executionStats")を追加します。これにより、実際に数百万のドキュメントを処理することなく、パイプラインが有効かどうかを確認できます。 - データ型を監査する: もし
$sumが0を返す場合、フィールドが数値(Int32 または Double)ではなく文字列(String)として保存されている可能性があります。アキュムレータは数値型に対してのみ機能します。

