マルチキー インデックスの限界
インデックス スキャンの限界は、クエリ中に検索するインデックスの部分を定義します。 インデックスに対する複数の述語が存在する場合、MongoDB は交差または複合により、これらの述語の限界を組み合わせて、より小さな限界のスキャンを生成しようとします。
マルチキー インデックスの交差
限界のある交差は論理的な結合を指します(つまり 複数の限界の AND
)。 たとえば、2 つの限界が[ [ 3, Infinity ] ]
と[ [ -Infinity, 6 ] ]
の場合、その限界の交差は[ [ 3, 6 ] ]
になります。
インデックスの配列フィールドがある場合、配列に複数の述語を指定し、 マルチキー インデックスを使用できるクエリを考えてみましょう。 MongoDB では、 が述語に結合される場合、 マルチキー $elemMatch
インデックス の限界を交差できます。
たとえば、フィールドitem
と配列フィールドratings
を持つドキュメントを含むsurvey
コレクションを作成します。
db.survey.insertMany( [ { _id: 1, item: "ABC", ratings: [ 2, 9 ] }, { _id: 2, item: "XYZ", ratings: [ 4, 3 ] } ] )
ratings
配列にマルチキー インデックスを作成します。
db.survey.createIndex( { ratings: 1 } )
次のクエリでは、 $elemMatch
を使用して、両方の条件に一致する単一の要素が配列に少なくとも 1 つ含まれていることを要求します。
db.survey.find( { ratings : { $elemMatch: { $gte: 3, $lte: 6 } } } )
述語を個別に取得する。
3 以上の述語の限界(つまり
$gte: 3
)は[ [ 3, Infinity ] ]
です。6 以下の述語の限界(
$lte: 6
)は[ [ -Infinity, 6 ] ]
です。
クエリでは$elemMatch
を使用してこれらの述語を結合するため、MongoDB は境界を交差して次のようにすることができます。
ratings: [ [ 3, 6 ] ]
クエリが と配列フィールドの条件を満たさ$elemMatch
ない 場合、MongoDB はマルチキー インデックスの限界を交差できません。次のクエリを考えてみましょう。
db.survey.find( { ratings : { $gte: 3, $lte: 6 } } )
クエリは、 ratings
配列で、3 以上の要素が少なくとも 1 つと 6 以下の要素が少なくとも 1 つ検索します。 単一の要素が両方の条件を満たす必要はないため、MongoDB は境界を交差することはなく、 [ [ 3,
Infinity ] ]
または[ [ -Infinity, 6 ] ]
のいずれかを使用します。 MongoDB は、これら 2 つの限界のうちどちらを選択するかについて保証しません。
マルチキー インデックスの複合限界
複合限界の設定 とは、複合インデックスの複数のキーに対して限界を使用することを指します。 たとえば、複合インデックス{ a: 1, b: 1 }
が[ [
3, Infinity ] ]
のフィールドa
に限界があり、 [ [ -Infinity, 6 ]
]
のフィールドb
に限界があると、限界を複合させ、両方の限界を使用します。
{ a: [ [ 3, Infinity ] ], b: [ [ -Infinity, 6 ] ] }
MongoDB が 2 つの限界を複合化できない場合、MongoDB は常にインデックス スキャンを先頭フィールドの限界(この場合はa:
[ [ 3, Infinity ] ]
)で制限します。
配列フィールドの複合インデックス
複合マルチキー インデックス を検討します。つまり、インデックス フィールドの 1 つが配列である複合インデックス。 たとえば、フィールドitem
と配列フィールドratings
を持つドキュメントを含むsurvey
コレクションを作成します。
db.survey.insertMany( [ { _id: 1, item: "ABC", ratings: [ 2, 9 ] }, { _id: 2, item: "XYZ", ratings: [ 4, 3 ] } ] )
item
フィールドとratings
フィールドに複合インデックスを作成します。
db.survey.createIndex( { item: 1, ratings: 1 } )
次のクエリでは、インデックスの両方のキーに条件を指定します。
db.survey.find( { item: "XYZ", ratings: { $gte: 3 } } )
述語を個別に取得する。
item: "XYZ"
述語の限界は[ [ "XYZ", "XYZ" ] ]
です。ratings: { $gte: 3 }
述語の限界は[ [ 3, Infinity ] ]
です。
MongoDB は 2 つの限界を複合して、合計された限界を使用することができます。
{ item: [ [ "XYZ", "XYZ" ] ], ratings: [ [ 3, Infinity ] ] }
スカラー インデックス フィールド(WiredTiger)に対する範囲クエリ
WiredTiger および インメモリ ストレージ エンジン のみ、
マルチキー インデックスでは、MongoDB は、どのインデックス付きフィールドがインデックスをマルチキー インデックスにするかを追跡します。 この情報を追跡することで、MongoDB クエリ エンジンはより厳しいインデックス限界を使用できるようになります。
前述の複合インデックスは、スカラー フィールド[1] item
と配列フィールドratings
にあります。
db.survey.createIndex( { item: 1, ratings: 1 } )
WiredTiger および インメモリ ストレージ エンジンの場合、クエリ操作で複合マルチキー インデックスのインデックス付きスカラー フィールドに対して複数の述語が指定される場合、MongoDB はフィールドの境界を交差します。
たとえば、次の操作では、スカラー フィールドに範囲クエリを指定し、配列フィールドに範囲クエリを指定します。
db.survey.find( { item: { $gte: "L", $lte: "Z"}, ratings : { $elemMatch: { $gte: 3, $lte: 6 } } } )
MongoDB は、 item
から[ [ "L", "Z" ] ]
への評価と[[3.0, 6.0]]
への評価の境界を交差して、次の合計境界を使用します。
"item" : [ [ "L", "Z" ] ], "ratings" : [ [3.0, 6.0] ]
別の例えとして、スカラーフィールドがネストされたドキュメントに属する場所を考えてみましょう。 たとえば、次のドキュメントを含むsurvey
コレクションを作成します。
db.survey.insertMany( [ { _id: 1, item: { name: "ABC", manufactured: 2016 }, ratings: [ 2, 9 ] }, { _id: 2, item: { name: "XYZ", manufactured: 2013 }, ratings: [ 4, 3 ] } ] )
スカラー フィールド"item.name"
、 "item.manufactured"
、および配列フィールドratings
に複合マルチキー インデックスを作成します。
db.survey.createIndex( { "item.name": 1, "item.manufactured": 1, ratings: 1 } )
スカラー フィールドでクエリ述語を指定する次の操作を検討してください。
db.survey.find( { "item.name": "L" , "item.manufactured": 2012 } )
このクエリに、MongoDB は次の組み合わせた限界を使用できます。
"item.name" : [ ["L", "L"] ], "item.manufactured" : [ [2012.0, 2012.0] ]
以前のバージョンの MongoDB では、スカラー フィールドのこれらの限界を組み合わせることはできません。
[1] | スカラー フィールドとは、値がドキュメントでも配列でもないフィールドです。例: 値がstringまたは整数であるフィールドは、スカラー フィールドです。スカラー フィールドは、フィールド自体が配列またはドキュメントでない限り、ドキュメントにネストされたフィールドであることができます。 たとえば、ドキュメント{ a: { b: { c: 5, d: 5 } } } では、 c とd はスカラー フィールドですが、 a とb はスカラー フィールドではありません。 |
埋め込みドキュメントの配列のフィールドの複合インデックス
配列に埋め込みドキュメントが含まれている場合、埋め込みドキュメントに含まれるフィールドにインデックスを付けるには、インデックス仕様でドット付きフィールド名を使用します。 たとえば、埋め込みドキュメントの次の配列があるとします。
ratings: [ { score: 2, by: "mn" }, { score: 9, by: "anon" } ]
score
フィールドのドット付きフィールド名は"ratings.score"
です。
非配列フィールドと配列からのフィールドの複合限界
コレクションsurvey2
に、フィールドitem
と配列フィールドratings
を持つドキュメントが含まれているとします。
{ _id: 1, item: "ABC", ratings: [ { score: 2, by: "mn" }, { score: 9, by: "anon" } ] } { _id: 2, item: "XYZ", ratings: [ { score: 5, by: "anon" }, { score: 7, by: "wv" } ] }
非配列フィールドitem
と、配列 の 2 つのフィールドratings.score
とratings.by
に複合インデックスを作成します。
db.survey2.createIndex( { "item": 1, "ratings.score": 1, "ratings.by": 1 } )
次のクエリは、3 つのフィールドすべてに条件を指定します。
db.survey2.find( { item: "XYZ", "ratings.score": { $lte: 5 }, "ratings.by": "anon" } )
述語を個別に取得する。
item: "XYZ"
述語の限界は[ [ "XYZ", "XYZ" ] ]
です。score: { $lte: 5 }
述語の限界は[ [ -Infinity, 5 ] ]
です。by: "anon"
述語の限界は[ "anon", "anon" ]
です。
MongoDB は、クエリ述語とインデックス キー値に応じて、 item
キーの限界を"ratings.score"
の限界または"ratings.by"
の限界と複合化できます。 MongoDB は、 item
フィールドと複合化される限界について保証していません。 たとえば、MongoDB はitem
の限界と"ratings.score"
の限界を複合化することを選択します。
{ "item" : [ [ "XYZ", "XYZ" ] ], "ratings.score" : [ [ -Infinity, 5 ] ], "ratings.by" : [ [ MinKey, MaxKey ] ] }
または、MongoDB は、 item
の限界と"ratings.by"
の限界を複合化することもできます。
{ "item" : [ [ "XYZ", "XYZ" ] ], "ratings.score" : [ [ MinKey, MaxKey ] ], "ratings.by" : [ [ "anon", "anon" ] ] }
ただし、 "ratings.score"
の限界と"ratings.by"
の限界を複合化するには、クエリは$elemMatch
を使用する必要があります。 詳細については、「 配列からのインデックス フィールドの複合限界 」を参照してください。
配列からのインデックス フィールドの複合限界
同じ配列のインデックス キーの限界を複合するには、次のようにします。
インデックス キーは、フィールド名まで同じフィールド パスを共有する必要があります。
クエリでは、そのパス上で
$elemMatch
を使用してフィールドに述語を指定する必要があります。
埋め込みドキュメント内のフィールドの場合、ドット付きフィールド名( "a.b.c.d"
など)はd
のフィールドパスになります。 同じ配列のインデックス キーの限界を複合するには、 $elemMatch
がフィールド名自体を除くまでのパス上に存在する必要があります。つまり"a.b.c"
です。
たとえば、 ratings.score
フィールドと フィールドに 複合インデックスratings.by
を作成します。
db.survey2.createIndex( { "ratings.score": 1, "ratings.by": 1 } )
フィールド"ratings.score"
と"ratings.by"
はフィールドパスratings
を共有しています。 次のクエリでは、フィールドratings
で$elemMatch
を使用して、両方の条件に一致する単一の要素が配列に少なくとも 1 つ含まれていることを要求します。
db.survey2.find( { ratings: { $elemMatch: { score: { $lte: 5 }, by: "anon" } } } )
述語を個別に取得する。
score: { $lte: 5 }
述語の限界は[ [ -Infinity, 5 ] ]
です。by: "anon"
述語の限界は[ [ "anon", "anon" ] ]
です。
MongoDB は 2 つの限界を複合して、合計された限界を使用することができます。
{ "ratings.score" : [ [ -Infinity, 5 ] ], "ratings.by" : [ [ "anon", "anon" ] ] }
次のないクエリ: $elemMatch
クエリが$elemMatch
を持つインデックス付き配列フィールドの条件を満たさ ない 場合、MongoDB はその限界を複合化 できません 。次のクエリを考えてみましょう。
db.survey2.find( { "ratings.score": { $lte: 5 }, "ratings.by": "anon" } )
配列内の単一の埋め込みドキュメントは両方の条件を満たす必要はないため、MongoDB は限界を複合化しません。 複合インデックスを使用する場合、MongoDB がインデックスのすべてのフィールドを制限できない場合、MongoDB は常にインデックスの先頭フィールドを制限します(この場合は"ratings.score"
です)。
{ "ratings.score": [ [ -Infinity, 5 ] ], "ratings.by": [ [ MinKey, MaxKey ] ] }
$elemMatch
不完全パスの場合
クエリが埋め込みフィールドのパスに$elemMatch
を指定していない場合( フィールド名まで)、MongoDB は同じ配列のインデックス キーの限界を複合化できません。
たとえば、コレクションsurvey3
には、フィールドitem
と配列フィールドratings
を持つドキュメントが含まれています。
{ _id: 1, item: "ABC", ratings: [ { scores: [ { q1: 2, q2: 4 }, { q1: 3, q2: 8 } ], loc: "A" }, { scores: [ { q1: 2, q2: 5 } ], loc: "B" } ] } { _id: 2, item: "XYZ", ratings: [ { scores: [ { q1: 7 }, { q1: 2, q2: 8 } ], loc: "B" } ] }
フィールドと フィールドに 複合インデックス を作成します。ratings.scores.q1
ratings.scores.q2
db.survey3.createIndex( { "ratings.scores.q1": 1, "ratings.scores.q2": 1 } )
フィールド"ratings.scores.q1"
と"ratings.scores.q2"
はフィールドパス"ratings.scores"
を共有しており、 $elemMatch
はそのパスにある必要があります。
一方、次のクエリでは、 $elemMatch
は使用されていますが、必要なパスではありません。
db.survey3.find( { ratings: { $elemMatch: { 'scores.q1': 2, 'scores.q2': 8 } } } )
そのため、MongoDB は限界を複合化することはできず、インデックス スキャン中に"ratings.scores.q2"
フィールドは制約されません。 限界を複合するには、クエリがパス"ratings.scores"
で$elemMatch
を使用する必要があります。
db.survey3.find( { 'ratings.scores': { $elemMatch: { 'q1': 2, 'q2': 8 } } } )