部分インデックス
部分インデックスでは、コレクション内のドキュメントのうち、指定フィルター式を満たすコものにのみインデックスが作成されます。コレクションのドキュメントのサブセットがインデックス化されるため、ストレージ必要量が少なくなり、インデックスの作成と維持の実行コストが減少します。
部分インデックスの作成
partial
インデックスを作成するには、partialFilterExpression
オプションを指定したdb.collection.createIndex()
メソッドを使用します。partialFilterExpression
オプションは、次のことを使用してフィルター条件を指定するドキュメントを受け入れます。
たとえば、次の操作で作成される複合インデックスでは、rating
フィールドが 5 を超えるドキュメントのみがインデックス化されます。
db.restaurants.createIndex( { cuisine: 1, name: 1 }, { partialFilterExpression: { rating: { $gt: 5 } } } )
MongoDB ではすべてのインデックス タイプで partialFilterExpression
オプションを指定できます。時系列コレクションの TTL インデックスに partialFilterExpression
を指定する場合、フィルタリング可能なコレクションは metaField
のみです。
動作
クエリ カバレッジ
MongoDB では、インデックスの使用が不完全な結果セットにつながる場合、クエリまたはソート操作に部分インデックスは使用されません。
部分インデックスを使用するには、クエリ条件の一部としてフィルター式(またはフィルター式のサブセットが指定されるように変更されたフィルター式)をクエリに含める必要があります。
次のインデックスを例にしましょう。
db.restaurants.createIndex( { cuisine: 1 }, { partialFilterExpression: { rating: { $gt: 5 } } } )
次のクエリではインデックスを使用できます。クエリ述語に含まれる条件 rating: { $gte: 8 }
がインデックス フィルター式 rating: { $gt: 5
}
によってマッチングされるドキュメントのサブセットと一致するためです。
db.restaurants.find( { cuisine: "Italian", rating: { $gte: 8 } } )
ただし、次のクエリでは cuisine
フィールドに部分インデックスックスを使用できません。使用すると結果セットが不完全になるためです。具体的には、クエリ述語には条件 rating: { $lt: 8 }
が、インデックスにはフィルター rating: { $gt:
5 }
が含まれます。そのため、クエリ { cuisine: "Italian", rating: { $lt: 8 }
}
に一致するドキュメントの数は、インデックス作成時よりも多くなります(評価が 1 のイタリアン レストランなど)。
db.restaurants.find( { cuisine: "Italian", rating: { $lt: 8 } } )
同様に、次のクエリでも部分インデックスを使用できません。クエリ述語にフィルター式が含まれておらず、インデックスを使用すると不完全な結果セットが返されるためです。
db.restaurants.find( { cuisine: "Italian" } )
スパースインデックスとの比較
部分インデックスは、スパースインデックスよりも優先され、次の利点を有します。
どのドキュメントのインデックスを作成するかをより細かく制御できる。
スパースインデックスの上位機能を提供する。
スパースインデックスでは、インデックスが作成されたフィールドがあること(複合インデックスの場合は、インデックスが作成されたフィールドが複数あること)のみを基準にインデックスを作成するドキュメントが選択されます。
部分インデックスでは、指定フィルターを基準にインデックスエントリが決定されます。フィルターにはインデックス キー以外のフィールドを含めることができ、インデックスの有無以外の条件も指定可能です。たとえば、部分インデックスではスパースインデックスと同じ動作を実装できます。
db.contacts.createIndex( { name: 1 }, { partialFilterExpression: { name: { $exists: true } } } )
上記の部分インデックスでは、name
フィールドでスパースインデックスと同じクエリをサポートします。
ただし、部分インデックスでは、インデックス キー以外のフィールドにフィルター式を指定することもできます。たとえば、次の操作で作成される部分インデックスには、name
フィールドにインデックスがあるだけでなく、email
フィールドにフィルター式もあります。
db.contacts.createIndex( { name: 1 }, { partialFilterExpression: { email: { $exists: true } } } )
クエリオプティマイザがこのタイプの部分インデックスを選択するには、name
フィールドの条件に加え email
フィールドの null 以外の一致条件がクエリ述語に含まれている必要があります。
たとえば、次のクエリには、name
フィールドの条件と email
フィールドの null 以外の一致の両方が含まれているため、インデックスを使用できます。
db.contacts.find( { name: "xyz", email: { $regex: /\.org$/ } } )
一方、次のクエリでは、フィルター式{ email: { $exists: true } }
で許可されていない null マッチが email
フィールドに含まれているため、インデックスを使用できません。
db.contacts.find( { name: "xyz", email: { $exists: false } } )
部分的な TTL インデックス
部分インデックスは TTL インデックスにすることもできます。部分 TTL インデックスを使用すると、指定されたフィルター式に一致するドキュメントのみが期限切れになります。詳細については、「フィルター条件でドキュメントを期限切れにする」を参照してください。
制限事項
partialFilterExpression
オプションとsparse
オプションを両方指定することはできません。_id
インデックスは部分インデックスにできません。シャードキー インデックスは部分インデックスにできません。
クライアント側フィールドレベル暗号化またはQueryable Encryptionを使用している場合、
partialFilterExpression
は暗号化されたフィールドを参照できません。
例
コレクションでの部分インデックスの作成
次のようなドキュメントが含まれているコレクション restaurants
を例に挙げます。
{ "_id" : ObjectId("5641f6a7522545bc535b5dc9"), "address" : { "building" : "1007", "coord" : [ -73.856077, 40.848447 ], "street" : "Morris Park Ave", "zipcode" : "10462" }, "borough" : "Bronx", "cuisine" : "Bakery", "rating" : { "date" : ISODate("2014-03-03T00:00:00Z"), "grade" : "A", "score" : 2 }, "name" : "Morris Park Bake Shop", "restaurant_id" : "30075445" }
borough
と cuisine
の各フィールドに部分インデックスを追加すると、rating.grade
フィールドが A
のドキュメントのみでインデックスを作成できます。
db.restaurants.createIndex( { borough: 1, cuisine: 1 }, { partialFilterExpression: { 'rating.grade': { $eq: "A" } } } )
この場合、restaurants
コレクションへの次のクエリでは部分インデックスを使用して、rating.grade
が A
に等しいブロンクスのレストランが返されます。
db.restaurants.find( { borough: "Bronx", 'rating.grade': "A" } )
一方、次のクエリでは、クエリ述語にrating.grade
フィールドが含まれていないため、部分インデックスを使用できません。
db.restaurants.find( { borough: "Bronx", cuisine: "Bakery" } )
ユニーク制約を持つ部分インデックス
部分インデックスでは、コレクション内のドキュメントのうち、指定フィルター式を満たすコものにのみインデックスが作成されます。partialFilterExpression
とユニーク制約の両方を指定した場合、ユニーク制約はフィルター式を満たすドキュメントにのみ適用されます。部分インデックスでユニーク制約を指定しても、ドキュメントがフィルタリング条件を満たさない場合は、当該ユニーク制約を満たさないドキュメントも挿入されます。
たとえば、コレクション users
に次のドキュメントが含まれているとします。
{ "_id" : ObjectId("56424f1efa0358a27fa1f99a"), "username" : "david", "age" : 29 } { "_id" : ObjectId("56424f37fa0358a27fa1f99b"), "username" : "amanda", "age" : 35 } { "_id" : ObjectId("56424fe2fa0358a27fa1f99c"), "username" : "rajiv", "age" : 57 }
次の操作では、username
フィールドにユニーク制約と部分的なフィルター式 age: { $gte: 21 }
を指定するインデックスが作成されます。
db.users.createIndex( { username: 1 }, { unique: true, partialFilterExpression: { age: { $gte: 21 } } } )
このインデックスにより次のドキュメントは挿入できなくなります。指定されたユーザー名を持ち、age
フィールドが 21
を超えるドキュメントがすでにあるためです。
db.users.insertMany( [ { username: "david", age: 27 }, { username: "amanda", age: 25 }, { username: "rajiv", age: 32 } ] )
ただし、ユニーク制約が適用されるのは、age
が 21 以上のドキュメントのみであるため、ユーザー名が重複している次のドキュメントは許可されます。
db.users.insertMany( [ { username: "david", age: 20 }, { username: "amanda" }, { username: "rajiv", age: null } ] )