埋め込みオブジェクトと配列のワイルドカード インデックス
ワイルドカード インデックスは、埋め込みオブジェクトと配列フィールドにインデックスを作成する場合、特定の動作をします。
フィールドがオブジェクトの場合、ワイルドカード インデックスはオブジェクトに降順を返し、その内容にインデックスを作成します。 ワイルドカード インデックスは、検出された追加の埋め込みドキュメントに降順を続行します。
フィールドが配列の場合、ワイルドカード インデックスは配列を走査し、各要素にインデックスを作成します。
要素がオブジェクトの場合、ワイルドカード インデックスは オブジェクトに下降してその内容をインデックス化します。
要素が配列(親配列に直接埋め込まれている配列)の場合、ワイルドカード インデックスは埋め込まれた配列をトラバースせず、配列全体を単一の値としてインデックス化します。
他のすべてのフィールドには、インデックスにはプリミティブ値が保存されます。 プリミティブ値は、オブジェクト以外、配列以外の値です。
ワイルドカード インデックスは、プリミティブ値に達するまで、追加の埋め込みオブジェクトまたは配列をトラバースし続けます。 次に、プリミティブ値とそのフィールドへのフルパスのインデックスを作成します。
埋め込みオブジェクトのワイルドカード インデックス
ワイルドカード インデックスが埋め込みオブジェクトを検出すると、そのオブジェクトに降順を実行し、その内容をインデックス化します。 たとえば、次のドキュメントについて考えてみます。
db.users.insertOne( { account: { username: "SuperAdmin01", contact: { phone: "123-456-7890", email: "xyz@example.com" }, access: { group: "admin" } } } )
account
フィールドを含むワイルドカード インデックスは、 account
オブジェクトに下降して、その内容を走査してインデックスを作成します。
自体がオブジェクトである各サブフィールド(たとえば、
account.contact
やaccount.access
)、インデックスはオブジェクトに下降し、その内容を記録します。他のすべてのサブフィールドについて、インデックスはプリミティブ値をインデックスに記録します。
サンプル ドキュメントの場合、ワイルドカード インデックスは次のレコードをインデックスに追加します。
"account.username" : "SuperAdmin01"
"account.contact.phone" : "123-456-7890"
"account.contact.email" : "xyz@example.com"
"account.access.group" : "admin"
配列のワイルドカード インデックス
ワイルドカード インデックスは配列を検出すると、その配列を走査してその要素にインデックスを作成します。 配列要素自体が配列(埋め込み配列)の場合、インデックスはその内容を走査せず、埋め込み配列全体を値として記録します。
たとえば、次のドキュメントについて考えてみます。
db.fleet.insertOne( { "ship": { "coordinates" : [ [-5, 10], [-7, 8] ], "type": "Cargo Ship", "captains": [ { "name": "Francis Drake", "crew": [ "first mate", "carpenter" ] } ] } } )
ship
フィールドを含むワイルドカード インデックスは、 オブジェクトに下降して、その内容を走査してインデックスを作成します。
配列である各要素について
要素自体が配列(埋め込み配列など)の場合、インデックスは配列全体を値として記録します。
要素がオブジェクトの場合、インデックスは オブジェクトに降順で降順に並べられ、その内容を走査してインデックスを作成します。
要素がプリミティブ値の場合、インデックスはその値を記録します。
配列以外、オブジェクト以外のフィールドの場合、インデックスはプリミティブ値をインデックスに記録します。
サンプル ドキュメントの場合、ワイルドカード インデックスは次のレコードをインデックスに追加します。
"ship.coordinates" : [-5, 10]
"ship.coordinates" : [-7, 8]
"ship.type" : "Cargo Ship"
"ship.captains.name" : "Francis Drake"
"ship.captains.crew" : "first mate"
"ship.captains.crew" : "carpenter"
明示的な配列インデックスを持つクエリ
ワイルドカード インデックスでは、インデックス作成中に配列内の特定の要素の配列位置は記録されません。 ただし、MongoDB では、1 つ以上の明示的な配列インデックスを持つフィールドパスを含むクエリを実行するために、引き続きワイルドカード インデックスを使用する場合があります。
たとえば、次のドキュメントについて考えてみます。
db.fleet.insertOne( { "ship": { "coordinates" : [ [-5, 10], [-7, 8] ], "type": "Cargo Ship", "captains": [ { "name": "Francis Drake", "crew": [ "first mate", "carpenter" ] } ] } } )
ship
フィールドを含むワイルドカード インデックスを作成します。
db.fleet.createIndex( { "ship.$**": 1 } )
ship.coordinates
とship.captains
のインデックス レコードには、各要素の配列位置は含まれていません。 ワイルドカード インデックスは、要素をインデックスに記録するときに、配列要素の位置を無視します。 ただし、ワイルドカード インデックスは明示的な配列インデックスを含むクエリを引き続きサポートできます。
MongoDB は、ワイルドカード インデックスを使用して次のクエリを実行できます。
db.fleet.find( { "ship.captains.0.name": "Francis Drake" } )
このクエリは、サンプル ドキュメントを返します。
[ { _id: ObjectId("6350537db1fac2ee2e957efc"), ship: { coordinates: [ [ -5, 10 ], [ -7, 8 ] ], type: 'Cargo Ship', captains: [ { name: 'Francis Drake', crew: [ 'first mate', 'carpenter' ] } ] } } ]
MongoDB は、ワイルドカード インデックスを使用してこのクエリを実行することはできません。
db.fleet.find( { "ship.coordinates.0.1": 10 } )
ship.coordinates
フィールドには埋め込み配列が含まれています。 ワイルドカード インデックスは、埋め込まれた配列の個々の値をレコードしません。 代わりに、埋め込まれた配列全体が記録されます。 その結果、ワイルドカード インデックス は埋め込み配列値の一致をサポートできず、MongoDB はコレクションスキャンを使用してクエリを実行します。
配列インデックスの制限
MongoDB は、パスに8以下の明示的な配列インデックスが含まれている場合にのみ、クエリ内の指定されたフィールドパスを実行するためにワイルドカード インデックスを使用できます。 フィールドパスに8を超える明示的インデックスが含まれている場合、クエリを満たすために MongoDB は次のいずれかを実行します。
別の適格なインデックスを選択します。
コレクションスキャンを実行します。
ワイルドカード インデックス自体には、インデックス作成中にドキュメントを走査する深度に制限はありません。 この制限は、配列インデックスを明示的に正確に指定するクエリにのみ適用されます。