ワイルドカード インデックス
MongoDB は、クエリをサポートするためにフィールドまたはフィールドのセットにインデックスを作成することをサポートしています。 MongoDB は動的スキーマをサポートしているため、アプリケーションは名前が事前に確認できないフィールドや任意のフィールドに対してクエリを実行できます。
バージョン MongoDB の新機能: 4.2
MongoDB 4.2 では、不明なフィールドや任意のフィールドに対するクエリをサポートするための ワイルドカード インデックス が導入されています。
userMetadata
フィールドの下でユーザー定義データを取得し、そのデータに対するクエリをサポートするアプリケーションを考えてみましょう。
{ "userMetadata" : { "likes" : [ "dogs", "cats" ] } } { "userMetadata" : { "dislikes" : "pickles" } } { "userMetadata" : { "age" : 45 } } { "userMetadata" : "inactive" }
管理者は、 userMetadata
の任意のサブフィールドに対するクエリをサポートするインデックスを作成したいと考えています。
userMetadata
のワイルドカード インデックスは、 userMetadata
、 userMetadata.likes
、 userMetadata.dislikes
、 userMetadata.age
の単一フィールド クエリをサポートできます。
db.userData.createIndex( { "userMetadata.$**" : 1 } )
インデックスは次のクエリをサポートできます。
db.userData.find({ "userMetadata.likes" : "dogs" }) db.userData.find({ "userMetadata.dislikes" : "pickles" }) db.userData.find({ "userMetadata.age" : { $gt : 30 } }) db.userData.find({ "userMetadata" : "inactive" })
userMetadata
の非ワイルドカード インデックスは、 userMetadata
の値に対するクエリのみをサポートできます。
重要
ワイルドカード インデックスは、ワークロードベースのインデックス プランニングを置き換えるように設計されていません。 クエリをサポートするインデックスの作成の詳細については、「 クエリをサポートするインデックスの作成」を参照してください。 ワイルドカード インデックスの制限に関する詳細なドキュメントについては、「ワイルドカード インデックスの制限 」を参照してください。
ワイルドカード インデックスの作成
重要
ワイルドカード インデックスを作成するには、 mongod
featureCompatibilityVersionが4.2
である必要があります。 fCV を設定する手順については、「 MongoDB 5.0 配置で機能の互換性バージョンを設定する 」を参照してください。
ワイルドカード インデックスは、 createIndexes
データベースコマンドまたはそのshellヘルパー( createIndex()
またはcreateIndexes()
を使用して作成できます。
フィールドにワイルドカード インデックスを作成する
特定のフィールドの値をインデックス化するには:
db.collection.createIndex( { "fieldA.$**" : 1 } )
このワイルドカード インデックスを使用すると、MongoDB はfieldA
のすべての値をインデックス化します。 フィールドがネストされたドキュメントまたは配列の場合、ワイルドカード インデックスはドキュメントまたは配列に再帰し、ドキュメントまたは配列内のすべてのフィールドの値を保存します。
たとえば、 product_catalog
コレクション内のドキュメントにはproduct_attributes
フィールドが含まれている場合があります。 product_attributes
フィールドには、埋め込みドキュメントや配列など、任意のネストされたフィールドを含めることができます。
{ "product_name" : "Spy Coat", "product_attributes" : { "material" : [ "Tweed", "Wool", "Leather" ] "size" : { "length" : 72, "units" : "inches" } } } { "product_name" : "Spy Pen", "product_attributes" : { "colors" : [ "Blue", "Black" ], "secret_feature" : { "name" : "laser", "power" : "1000", "units" : "watts", } } }
次の操作を実行すると、product_attributes
フィールドでワイルドカード インデックスが作成されます。
db.products_catalog.createIndex( { "product_attributes.$**" : 1 } )
ワイルドカード インデックスは、 product_attributes
またはその埋め込みフィールドに対する任意の単一フィールド クエリをサポート可能です。
db.products_catalog.find( { "product_attributes.size.length" : { $gt : 60 } } ) db.products_catalog.find( { "product_attributes.material" : "Leather" } ) db.products_catalog.find( { "product_attributes.secret_feature.name" : "laser" } )
注意
パス固有のワイルドカード インデックス構文はwildcardProjection
オプションと互換性がありません。 詳細については、 wildcard
インデックスのオプション を参照してください。
すべてのフィールドにワイルドカード インデックスを作成する
ドキュメント内のすべてのフィールド( _id
を除く)の値をインデックス化するには、インデックスキーとして"$**"
を指定します。
db.collection.createIndex( { "$**" : 1 } )
このワイルドカード インデックスを使用すると、MongoDB はコレクション内の核ドキュメントのすべてのフィールドにインデックスを作成します。 特定のフィールドがネストされたドキュメントまたは配列の場合、ワイルドカード インデックスはドキュメントまたは配列に再帰し、ドキュメントまたは配列内のすべてのフィールドの値を保存します。
例については、「すべてのフィールドパスでのワイルドカード インデックスの作成 」を参照してください。
注意
ワイルドカード インデックスでは、デフォルトで_id
フィールドが省略されます。 ワイルドカード インデックスに_id
フィールドを含めるには、 wildcardProjection
ドキュメントに明示的に含める必要があります。 詳細については、 wildcard
インデックスのオプション を参照してください。
複数の特定フィールドに対するワイルドカード インデックスの作成
ドキュメント内の複数の特定のフィールドの値をインデックス化するには:
db.collection.createIndex( { "$**" : 1 }, { "wildcardProjection" : { "fieldA" : 1, "fieldB.fieldC" : 1 } } )
このワイルドカード インデックスを使用すると、MongoDB はコレクション内の各ドキュメントの指定されたフィールドのすべての値をインデックス化します。 特定のフィールドがネストされたドキュメントまたは配列の場合、ワイルドカード インデックスはドキュメントまたは配列に再帰し、ドキュメントまたは配列内のすべてのフィールドの値を保存します。
注意
ワイルドカード インデックスは、 _id
フィールドを明示的に含める場合を 除き、 wildcardProjection
ドキュメントに包含・除外ステートメントを混在させることはできません。 wildcardProjection
の詳細については、 wildcard
インデックスのオプション を参照してください。
複数の特定フィールドを除外するワイルドカード インデックスの作成
特定のフィールドパスを除くドキュメント内のすべてのフィールドのフィールドにインデックスを付けるには
db.collection.createIndex( { "$**" : 1 }, { "wildcardProjection" : { "fieldA" : 0, "fieldB.fieldC" : 0 } } )
このワイルドカード インデックスを使用すると、MongoDB は指定されたフィールドパスを除くコレクション内の各ドキュメントのすべてのフィールドにインデックスを作成します。 特定のフィールドがネストされたドキュメントまたは配列の場合、ワイルドカード インデックスはドキュメントまたは配列に再帰し、ドキュメントまたは配列内のすべてのフィールドの値を保存します。
例については、「ワイルドカード インデックスのカバレッジから特定のフィールドを除外する 」を参照してください。
注意
ワイルドカード インデックスは、 _id
フィールドを明示的に含める場合を 除き、 wildcardProjection
ドキュメントに包含・除外ステートメントを混在させることはできません。 wildcardProjection
の詳細については、 wildcard
インデックスのオプション を参照してください。
正規化されたワイルドカード インデックスの表示
MongoDB5.0以降、ワイルドカード インデックスは作成後に正規化されます。ワイルドカード プロジェクション フィールドが同等のフィルターを しない限り、同じExpress キー パターン を使用して複数のワイルドカード インデックスを作成できます。
たとえば、ワイルドカード インデックスを作成します。
db.books.createIndex( { "$**" : 1 }, { wildcardProjection : {a: 1, "b.c": 1 } } )
db.collection.getIndexes()
メソッドでインデックスを表示する。
db.books.getIndexes()
結果は、正規化された形式で表示されます。
{ v: 2, key: { _id: 1 }, name: '_id_' }, { v: 2, key: { '$**': 1 }, name: '$**_1', wildcardProjection: { a: true, b: { c: true }, _id: false } }
Considerations
ワイルドカード インデックスは、任意のクエリ述語で最大1 つのフィールドをサポートできます。 ワイルドカード インデックス クエリのサポートの詳細については、 ワイルドカード インデックス クエリとソートのサポート を参照してください。
ワイルドカード インデックスを作成するには、
mongod
featureCompatibilityVersionが4.2
である必要があります。 fCV を設定する手順については、「 MongoDB 5.0 配置で機能の互換性バージョンを設定する 」を参照してください。ワイルドカード インデックスでは、デフォルトで _id フィールドが省略されます。
_id
フィールドをワイルドカード インデックスに含めるには、ワイルドカード プロジェクション ドキュメントに明示的に含める必要があります({ "_id" : 1 }
)。コレクション内に複数のワイルドカード インデックスを作成できます。
ワイルドカード インデックスは、コレクション内の他のインデックスと同じフィールドをカバーする場合があります。
ワイルド インデックスはスパース インデックスであるため、インデックス フィールドに null 値が含まれていても、インデックス フィールドを持つドキュメントのエントリのみが含まれます。
動作
ワイルドカード インデックスは、オブジェクト(埋め込みドキュメント)または配列であるフィールドをインデックス化する場合、特定の動作をします。
フィールドがオブジェクトの場合、ワイルドカード インデックスはオブジェクトに降順を返し、その内容にインデックスを作成します。 ワイルドカード インデックスは、検出された追加の埋め込みドキュメントに降順を続行します。
フィールドが配列の場合、ワイルドカード インデックスは配列を走査し、各要素にインデックスを作成します。
配列内の要素がオブジェクトの場合、ワイルドカード インデックスはオブジェクトに下降して、上記で説明したようにその内容にインデックスを作成します。
要素が配列、つまり親配列に直接埋め込まれている配列の場合、ワイルドカード インデックスは埋め込まれた配列をトラバースせず、配列全体を単一の値としてインデックス化します。
他のすべてのフィールドについては、プリミティブ(オブジェクトまたは配列)値をインデックスに記録します。
ワイルドカード インデックスは、プリミティブ値(オブジェクトまたは配列ではないフィールド)に達するまで、追加のネストされたオブジェクトまたは配列をトラバースします。 次に、このプリミティブ値とそのフィールドへのフルパスのインデックスを作成します。
たとえば、次のドキュメントについて考えてみます。
{ "parentField" : { "nestedField" : "nestedValue", "nestedObject" : { "deeplyNestedField" : "deeplyNestedValue" }, "nestedArray" : [ "nestedArrayElementOne", [ "nestedArrayElementTwo" ] ] } }
parentField
を含むワイルドカード インデックスは次のエントリを記録します。
"parentField.nestedField" : "nestedValue"
"parentField.nestedObject.deeplyNestedField" : "deeplyNestedValue"
"parentField.nestedArray" : "nestedArrayElementOne"
"parentField.nestedArray" : ["nestedArrayElementTwo"]
parentField.nestedArray
のレコードには各要素の配列位置が含まれていないことに注意してください。 ワイルドカード インデックスは、要素をインデックスに記録するときに、配列要素の位置を無視します。 ワイルドカード インデックスは、明示的な配列インデックスを含むクエリを引き続きサポートできます。 詳細については、「明示的な配列インデックスを含むクエリ」を参照してください。
ネストされたオブジェクトでのワイルドカード インデックスの動作の詳細については、「ネストされたオブジェクト 」を参照してください。
ネストされた配列でのワイルドカード インデックスの動作の詳細については、「ネストされた配列 」を参照してください。
ネストされたオブジェクト
ワイルドカード インデックスはネストされたオブジェクトを検出すると、そのオブジェクトに降順を返し、その内容をインデックス化します。 例:
{ "parentField" : { "nestedField" : "nestedValue", "nestedArray" : ["nestedElement"] "nestedObject" : { "deeplyNestedField" : "deeplyNestedValue" } } }
parentField
を含むワイルドカード インデックスは、 オブジェクトに下降して、その内容を走査してインデックスを作成します。
それ自体がオブジェクト(つまり、埋め込みドキュメント)である各フィールドについては、 オブジェクトに下降してその内容をインデックス化します。
配列である各フィールドについて、配列を走査し、その内容をインデックス化します。
他のすべてのフィールドについては、プリミティブ(オブジェクトまたは配列)値をインデックスに記録します。
ワイルドカード インデックスは、プリミティブ値(オブジェクトまたは配列ではないフィールド)に達するまで、追加のネストされたオブジェクトまたは配列をトラバースします。 次に、このプリミティブ値とそのフィールドへのフルパスのインデックスを作成します。
サンプル ドキュメントの場合、ワイルドカード インデックスは次のレコードをインデックスに追加します。
"parentField.nestedField" : "nestedValue"
"parentField.nestedObject.deeplyNestedField" : "deeplyNestedValue"
"parentField.nestedArray" : "nestedElement"
ネストされた配列でのワイルドカード インデックスの動作の詳細については、「ネストされた配列 」を参照してください。
ネストされた配列
ワイルドカード インデックスがネストされた配列を検出すると、その配列を走査してその要素にインデックスを作成しようとします。 配列が親配列(埋め込み配列)の要素である場合、ワイルドカード インデックスはその内容を走査せず、代わりに配列全体を値として記録します。 例:
{ "parentArray" : [ "arrayElementOne", [ "embeddedArrayElement" ], "nestedObject" : { "nestedArray" : [ "nestedArrayElementOne", "nestedArrayElementTwo" ] } ] }
parentArray
を含むワイルドカード インデックスは、 配列に下降して、その内容を走査してインデックスを作成します。
配列である各要素(つまり、埋め込み配列)について、配列全体を値としてインデックスします。
オブジェクトである各要素については、 オブジェクトに下降してその内容を走査し、インデックスを作成します。
他のすべてのフィールドについては、プリミティブ(オブジェクトまたは配列)値をインデックスに記録します。
ワイルドカード インデックスは、プリミティブ値(オブジェクトまたは配列ではないフィールド)に達するまで、追加のネストされたオブジェクトまたは配列をトラバースします。 次に、このプリミティブ値とそのフィールドへのフルパスのインデックスを作成します。
サンプル ドキュメントの場合、ワイルドカード インデックスは次のレコードをインデックスに追加します。
"parentArray" : "arrayElementOne"
"parentArray" : ["embeddedArrayElement"]
"parentArray.nestedObject.nestedArray" : "nestedArrayElementOne"
"parentArray.nestedObject.nestedArray" : "nestedArrayElementTwo"
parentField.nestedArray
のレコードには各要素の配列位置が含まれていないことに注意してください。 ワイルドカード インデックスは、要素をインデックスに記録するときに、配列要素の位置を無視します。 ワイルドカード インデックスは、明示的な配列インデックスを含むクエリを引き続きサポートできます。 詳細については、「明示的な配列インデックスを含むクエリ」を参照してください。
Index Display
MongoDB 5.0.16以降、 wildcardProjection
フィールドにはインデックスプロジェクションが送信された形式で保存されます。 それ以前のバージョンでは、プロジェクションは正規化された形式で保存されている場合があります。
サーバーは同じ方法でインデックスを使用しますが、 listIndexes
} コマンドとdb.collection.getIndexes()
コマンドの出力には違いが生じる場合があります。
制限事項
ワイルドカード インデックスを使用してコレクションをシャーディングすることはできません。 シャーディングしたいフィールドに非ワイルドカード インデックスを作成します。 シャードキー選択の詳細については、「シャードキー 」を参照してください。
複合インデックスは作成できません。
ワイルドカード インデックスに次のプロパティを指定することはできません。
ワイルドカード構文を使用して次のインデックス タイプは作成できません。
重要
ワイルドカード インデックスは、ワイルドカード テキスト インデックスとは区別され、互換性もありません。ワイルドカード インデックスは $text
演算子を使用するクエリをサポートしていません。
ワイルドカード インデックス作成の制限に関する詳細なドキュメントについては、「互換性のないインデックス タイプまたはプロパティ 」を参照してください。
ワイルドカード インデックス クエリ/ソートのサポート
カバード クエリ
ワイルドカード インデックスは、次のすべてが当てはまる 場合にのみ 、 カバード クエリ をサポートできます。
クエリ プランナーが、クエリ述語を満たすワイルドカード インデックスを選択する
クエリ述語が、ワイルドカード インデックスの対象となるフィールドを 1 つだけ指定している
プロジェクションは、
_id
を明示的に除外し、クエリ フィールドのみを含めます。指定されたクエリ フィールドは配列ではない
employees
コレクションの次のワイルドカード インデックスを検討します。
db.products.createIndex( { "$**" : 1 } )
次の操作では、1 つのフィールド lastName
をクエリし、結果のドキュメントから他のすべてのフィールドをプロジェクトします。
db.products.find( { "lastName" : "Doe" }, { "_id" : 0, "lastName" : 1 } )
指定されたlastName
が配列ではないことを前提とすると、MongoDB はカバード クエリをサポートするために$**
ワイルドカード インデックスを使用できます。
マルチフィールドクエリ述語
ワイルドカード インデックスは、最大1 つのクエリ述語フィールドをサポートできます。 つまり、
MongoDB では、非ワイルドカード インデックスを使用してクエリ述語の 1 つの部分を満たすことができず、ワイルドカードを使用して別の部分を満たすことはできません。
MongoDB では、クエリ述語の一部を満たすために 1 つのワイルドカード インデックスを使用し、別の部分を満たすために別のワイルドカード インデックスを使用することはできません。
1 つのワイルドカード インデックスが複数のクエリ フィールドをサポートできる場合でも、MongoDB はワイルドカード インデックスを使用してクエリ フィールドの 1 つだけをサポートできます。 残りのすべてのフィールドは、インデックスなしで解決されます。
ただし、MongoDB では、クエリ$or
または集計$or
演算子の各独立した引数を満たすために同じワイルドカード インデックスが使用される場合があります。
ソートを含むクエリ
MongoDB では、次のすべてが当てはまる 場合にのみsort()
、 を満たすためにワイルドカード インデックスを使用できます。
クエリ プランナーが、クエリ述語を満たすワイルドカード インデックスを選択する
sort()
はクエリ述語フィールドのみを指定します。指定されたフィールドは配列ではない
上記の条件が満たされない場合、MongoDB はソートにワイルドカード インデックスを使用できません。 MongoDB は、クエリ述語とは異なるインデックスを必要とするsort()
操作をサポートしていません。 詳細については、「インデックスの交差とソート 」を参照してください。
products
コレクションの次のワイルドカード インデックスを検討します。
db.products.createIndex( { "product_attributes.$**" : 1 } )
次の操作では、単一のフィールドproduct_attributes.price
をクエリし、その同じフィールドでソートします。
db.products.find( { "product_attributes.price" : { $gt : 10.00 } }, ).sort( { "product_attributes.price" : 1 } )
指定されたprice
が配列ではないことを前提とすると、MongoDB はproduct_attributes.$**
ワイルドカード インデックスを使用してfind()
とsort()
の両方を満たすことができます。
サポートされていないクエリパターン
ワイルドカード インデックスは次のクエリ パターンをサポートしていません。
フィールドが存在しないかどうかを確認するクエリ
フィールドがドキュメントまたは配列と等しいかどうかを確認するクエリ
フィールドが null と等しいかどうかを確認するクエリ
詳細については、「サポートされていないクエリと集計パターン 」を参照してください。
明示的な配列インデックスを持つクエリ
MongoDB ワイルドカード インデックスでは、インデックス作成中に配列内の特定の要素の配列位置は記録されません。 ただし、MongoDB では、1 つ以上の明示的な配列インデックスを持つフィールドパスを含むクエリに応答するためにワイルドカード インデックスを選択することもできます(たとえば、 parentArray.0.nestedArray.0
)。 連続するネストされた配列ごとにインデックスの限界を定義する複雑さのため、MongoDB は、そのパスに8
個を超える明示的な配列インデックスが含まれている場合、クエリ内の特定のフィールドパスにクエリを実行するためにワイルドカード インデックスを考慮しません。 MongoDB では、クエリ内の他のフィールドパスに応答するために、ワイルドカード インデックスを使用することも検討できます。
以下に例を挙げます。
{ "parentObject" : { "nestedArray" : [ "elementOne", { "deeplyNestedArray" : [ "elementTwo" ] } ] } }
MongoDB では、次のクエリを満たすためにparentObject
を含むワイルドカード インデックスを選択できます。
"parentObject.nestedArray.0" : "elementOne"
"parentObject.nestedArray.1.deeplyNestedArray.0" : "elementTwo"
クエリ述語内の特定のフィールドパスで 8 個を超える明示的な配列インデックスが指定されている場合、MongoDB は、そのフィールドパスを応答するためにワイルドカード インデックスを考慮しません。 MongoDB は、クエリに答えるために別の適格なインデックスを選択するか、コレクションスキャンを実行します。
ワイルドカード インデックス自体には、インデックス作成中にドキュメントを走査する深度に制限がないことに注意してください。この制限は、配列インデックスを明示的に指定するクエリにのみ適用されます。 明示的な配列インデックスなしで同じクエリを発行すると、MongoDB はクエリに答えるために ワイルドカード インデックス を選択する場合があります。
"parentObject.nestedArray" : "elementOne"
"parentObject.nestedArray.deeplyNestedArray" : "elementTwo"