エラーメッセージ検索クエリを最適化しようとした際、MongoDBから予期せぬ制限を受けたのではないでしょうか。このエラーは、2つの異なる配列フィールドに対して複合インデックスを作成しようとした瞬間に発生します。
MongoServerError: cannot index parallel arrays [tags] [categories]
マイグレーションスクリプトを実行している場合でも、手動でシェルコマンドを実行している場合でも、この操作は即座に失敗します。これはバグではなく、MongoDBのインデックスエンジンの仕様によるハードコードされた制限です。
なぜMongoDBはこの操作をブロックするのかMongoDBは、配列内のすべての要素に対してインデックスエントリを作成します(これを「マルチキーインデックス」と呼びます)。1つのインデックスで2つの配列を組み合わせようとすると、MongoDBは数学的な悪夢である「デカルト積(直積)」に直面します。
例えば、1つのドキュメントに10個のタグと5個のカテゴリがある場合、MongoDBはその1つのレコードだけで50個の個別のインデックスエントリを作成しなければなりません。100万件のドキュメントがある本番スケールでは、単純なインデックスが5,000万件のエントリに爆発的に膨れ上がる可能性があります。この巨大な増加はRAMの使用量を圧迫し、書き込みパフォーマンスを大幅に低下させます。
具体的なシナリオ例えば、productsコレクションに以下のような構造のドキュメントがあるとします。
{
"name": "Ergonomic Chair",
"tags": ["office", "furniture", "sale"],
"categories": ["home", "pro"]
}
ダッシュボードのフィルターを最適化するために、以下のコマンドを実行したとします。
db.products.createIndex({ tags: 1, categories: 1 })
tagsとcategoriesの両方が配列であるため、MongoDBはパフォーマンスの低下を防ぐために、このインデックスの作成を拒否します。
エラーの解決方法MongoDBに1つの複合インデックスで2つの配列をインデックス化させることはできません。代わりに、特定のクエリパターンに合わせた戦略を選択してください。
解決策1:個別のインデックスを使用する1つの複合インデックスではなく、2つの個別のインデックスを作成します。MongoDBのオプティマイザは、両方のフィールドを使用するクエリを処理するために**インデックス交差(Index Intersection)**を使用するほど十分に賢明です。
// 2つの独立したインデックスを作成
db.products.createIndex({ tags: 1 })
db.products.createIndex({ categories: 1 })
これは完璧な複合インデックスよりもわずかに遅くなりますが、スキーマを再設計することなく、90%のユースケースで問題を解決できます。
解決策2:1つの配列と1つのスカラーフィールドをインデックス化するMongoDBでは、正確に1つの配列を含む複合インデックスは許可されています。フィールドの1つが実際には単一の文字列や数値(スカラー)である場合は、データがそれを反映していることを確認してください。
両方を配列として保持する必要がある場合は、結果を最も絞り込める(「選択性」が高い)フィールドをインデックス化し、statusやpriceのような配列ではないフィールドをインデックスに追加します。
// 1つの配列とステータス文字列を組み合わせる
db.products.createIndex({ tags: 1, status: 1 })
解決策3:スキーマの再設計(埋め込みオブジェクト)特定のペアで常にフィルタリングを行う高パフォーマンスなクエリが必要な場合は、データをオブジェクトの単一の配列に構造化し直します。
// 以前の構造: tags: ["A", "B"], categories: ["X", "Y"]
// 新しい構造:
// metadata: [{ t: "A", c: "X" }, { t: "B", c: "Y" }]
// サブフィールドをインデックス化
db.products.createIndex({ "metadata.t": 1, "metadata.c": 1 })
この方法では、配列がmetadataの1つだけになるため、制限を回避できます。MongoDBは、考えられるすべての組み合わせを計算することなく、ペアを直接インデックス化します。
修正の確認選択した修正を適用した後、クエリが実際に効率的であることを確認します。フィルターに対してexplain()コマンドを実行してください。
db.products.find({
tags: "furniture",
categories: "home"
}).explain("executionStats")
winningPlan内のIXSCANステージを探します。COLLSCANが表示されている場合は、クエリがコレクション内のすべてのドキュメントをスキャンしていることを意味し、パフォーマンス上の大きな懸念事項となります。

