エラーが発生する理由
MongoServerError: FieldPath field names may not start with '$' というメッセージが表示される場合、通常は集計(aggregation)パイプラインの構文に問題があることを意味します。これは、ドル記号($)で始まる文字列を使用して、新しいフィールド名(キー)を指定しようとしたときに発生します。
MongoDBは $ プレフィックスを特別なシグナルとして扱います。主に以下の2つの用途で使用されます:
- 演算子(Operators):
$match、$group、$limitなどのコマンド。 - フィールドパス参照: 値(
"$price"など)として使用される場合、MongoDBに対して「price」フィールドからデータを取得するように指示します。
データベースは、予約されたプレフィックスを出力用のラベルとして使用しようとしているため、混乱してしまいます。MongoDBでは、これらのコンテキストにおいてキーの先頭に $ を使用することを許可していません。
// このコードは必ず失敗します
db.orders.aggregate([
{
$project: {
"$total_cost": "$amount" // エラー!キーを $ で始めることはできません
}
}
]);
よくあるシナリオと迅速な修正方法
1. $project または $addFields での入力ミス
多くの開発者は、値に $ を入力する習慣があるため、誤ってフィールド名にもそれを含めてしまうことでこのエラーに遭遇します。フィールド名を変更する場合、新しい名前は $ を含まないクリーンな文字列である必要があります。
修正方法: コロンの左側から $ を削除します。
// ❌ 誤り
{ $project: { "$user_display_name": "$name" } }
// ✅ 正しい
{ $project: { "user_display_name": "$name" } }
2. $group での集計合計の命名
$group ステージでは、_id に $ を使用してフィールドを参照できます(また、多くの場合そうすべきです)。しかし、その後に続く $sum や $avg などのアキュムレータには、標準的な名前を付ける必要があります。例えば、500件のトランザクションの収益を計算する場合、その合計値のラベルをドル記号で始めることはできません。
// ❌ 誤り
db.sales.aggregate([
{
$group: {
_id: "$category",
"$monthly_revenue": { $sum: "$price" } // エラー: "$monthly_revenue" は無効です
}
}
]);
// ✅ 正しい
db.sales.aggregate([
{
$group: {
_id: "$category",
"monthly_revenue": { $sum: "$price" } // 名前を直接使用します
}
}
]);
3. Node.js または Python での動的キーの処理
アプリケーションが動的にフィールド名を生成する場合、知らずのうちに $ を含む変数を渡してしまうことがあります。これは、ユーザーが送信したフォームデータや外部APIのペイロードを処理する際によく発生します。
// Node.js の動的キーの例
const rawInput = "$profit";
// ❌ これはクエリをクラッシュさせます
const pipeline = [
{ $addFields: { [rawInput]: { $subtract: ["$revenue", "$costs"] } } }
];
// ✅ 修正方法: 最初に文字列をサニタイズ(クリーンアップ)します
const cleanKey = rawInput.startsWith('$') ? rawInput.slice(1) : rawInput;
const safePipeline = [
{ $addFields: { [cleanKey]: { $subtract: ["$revenue", "$costs"] } } }
];
4. 括弧や中括弧の不一致
中括弧(波括弧)が不足していると、MongoDBは演算子を実際のフィールド名であると誤認してしまうことがあります。$sum を中括弧で囲むのを忘れると、パーサーは $sum が作成しようとしているキーであると判断します。
// ❌ 誤り(パーサーはここで $sum をキーとして認識します)
{ $project: { total: $sum: "$items" } }
// ✅ 正しい
{ $project: { total: { $sum: "$items" } } }
修正を確認する方法
どこにエラーがあるか推測するのではなく、以下の手順で特定しましょう:
- MongoDB Compass を使用する: パイプラインを Aggregation タブに貼り付けます。Compass は失敗した特定のステージをリアルタイムで強調表示するため、何百行ものコードを調べる手間が省けます。
- ステージを切り分ける: パイプラインに10個のステージがある場合、最後の5個をコメントアウトしてみてください。動作すれば、エラーは後半部分にあります。原因が見つかるまで範囲を絞り込んでいきます。
- ドライバーのログを確認する: 多くの場合、完全なスタックトレースが、検証エラーの原因となった正確なオブジェクトキーを指し示しています。
構文エラーを避けるためのベストプラクティス
一貫性を保つことが、これらのエラーに対する最善の防御策です。クエリをきれいに保つために、以下のルールに従ってください:
- 厳格な命名規則: スキーマ内でデータベースのフィールド名を
$で始めないでください。MongoDB 5.0 以降では一部のドキュメントで許可されていますが、集計が非常に困難になり、エラーが発生しやすくなります。 - キー vs 値のメンタルモデル: 黄金律を覚えましょう。キーはカスタムラベル(
$なし)、値は既存データへの参照($を使用)です。 - IDE を使用する: MongoDB 拡張機能を備えた VS Code などのツールは、構文のハイライト機能を提供します。これらは、「実行」を押す前に、配置ミスのある
$にフラグを立ててくれることが多いです。

