MongoDBは配列を強力にサポートしており、ドキュメントモードで多くの柔軟性を提供します。たとえば、配列をドキュメントに埋め込んだり、ドキュメントを配列に埋め込んだりすることもできます。ただし、MongoDBには、配列を操作する際の落とし穴がいくつかあります。この投稿では、MongoDBがインデックスと配列に関して抱えている問題のいくつかを見ていきます。
マルチキーインデックス
MongoDBでは、配列フィールドにインデックスを付けて、配列内の各要素のインデックスエントリを作成できます。結果のインデックスは「マルチキー」インデックスと呼ばれます。マルチキーインデックスは、スカラー値または埋め込みドキュメントに対して作成できます。マルチキーインデックスの詳細については、このドキュメントを参照してください。
マルチキーインデックスは便利ですが、いくつかの制限があります。
- 複合マルチキーインデックスを作成する場合、配列であるフィールドは最大で1つです。
- 複合インデックスをシャードキーにすることはできません。
- 複合インデックスをハッシュインデックスにすることはできません。
マルチキーインデックスの最も興味深い側面の1つは、インデックスの共通部分の境界を計算する方法です。
インデックス交差境界
MongoDBのドキュメントでインデックスの交差境界を定義する方法は次のとおりです。
「インデックススキャンの境界は、クエリ中に検索するインデックスの部分を定義します。インデックス上に複数の述語が存在する場合、MongoDBはこれらの述語の境界を
配列の範囲クエリ
簡単な例から始めて、MongoDBが配列のクエリのインデックス境界を計算する方法を見てみましょう。コレクションに次の3つのドキュメントがあると仮定します。
{x: 65} {x: 35} {x: [12,95]}
次のクエリを発行します:
db.coll.find({x :{ $gt :22, $lt:55})
クエリは十分に単純です。答えは{x:35}であると期待しますが、クエリは次を返します:
{x:35} {x:[25,95]}
その理由は、MongoDBが配列を処理する方法にあります。配列の同じ要素が両方の条件に一致する必要はありません。各条件に一致する要素が1つある限り、それは一致します。したがって、この場合、境界は[22、Infinity]と[-Infinity、55]です。 「elemMatch」演算子は使用されないため、MongoDBはインデックスの共通部分を使用しません。 MongoDBは、これらの範囲[22、Infinity]または[-Infinity、55]のどちらをクエリの実行に使用するかを指定しません。
インデックス交差を使用する場合は、次のクエリを使用する必要があります:
db.coll.find(x :{ $elemMatch:{$gt :22,$lt:55}})
これを使用すると、MongoDBはインデックスの境界と交差し、境界として[22、55]を使用します。予想どおり、このクエリは結果を返しません(elemMatchは非配列と一致しません)。したがって、基本的に、配列に対する範囲クエリは、$elemMatch演算子がないとかなり役に立ちません。
複合マルチキーインデックス–配列フィールドと非配列フィールドの混合
次のドキュメントを含むコレクションについて考えてみます。
{item: 35, prices:[250,35]} ...... {item: 106, prices:[1500,65]}
このコレクションに複合インデックスを追加します:
db.ensureIndex({item:1, prices:1});
次に、簡単なクエリを実行しましょう:
db. coll. find({item: {$gt:12, $lt:65}});
範囲が固定された非配列アイテムを使用しているため、クエリは非常に単純に見えます。インデックスの交差境界は、クエリのitem:[[12,65]]のようなものになると思いますが、Explainを実行すると、次のように表示されます:
"indexBounds" : { "item" : [ [ -Infinity, 65 ] ], "prices" : [ [ { "$minElement" : 1 }, { "$maxElement" : 1 } ] ] },
その理由は、MongoDBがこれがマルチキーインデックスであることを検出し、クエリが配列フィールドを使用していないにもかかわらず、インデックス境界の交差を処理しないためです。ストーリーの教訓は、インデックスに配列フィールドと非配列フィールドを混在させる場合は、インデックスの共通部分の境界に常に注意を払うことです。確率はそれが効率的ではないということです。