HOWTO · MongoDB

MongoDB の一意のインデックス

この教育記事では、一意のインデックスとは何か、MongoDB で一意のインデックスを作成する方法について説明します。 また、MongoDB でユーザーのメールを一意にする方法についても簡単に説明します。

このページの内容

このチュートリアルでは、一意のインデックスとは何か、MongoDB で作成する方法など、一意のインデックスについて学習します。 さらに、MongoDB でユーザーのメールを一意にするプロセスについても簡単に説明します。

この記事の目次は次のとおりです。

  1. MongoDB の一意のインデックス
  2. MongoDB で一意のインデックスを作成する
  3. MongoDBにおけるユニークインデックスの振る舞い
  4. Mongoose で一意のメールを検証する

MongoDB の一意のインデックス

一意のインデックスは、インデックス付きフィールドに重複する値が含まれていないことを保証し、インデックス付きフィールドが一意であることを保証します。 コレクションの構築中に、MongoDB はデフォルトで _id 列に一意のインデックスを生成します。

db.collection.createIndex() コマンドを使用して、unique オプションを true に設定して一意のインデックスを生成します。

db.collection.createIndex( <key and index type specification>, { unique: true } )

単一フィールドの一意のインデックス

mongosh で次の手順を使用して、members コレクションの user_id フィールドに一意のインデックスを作成します。

db.members.createIndex( { "user_id": 1 }, { unique: true } )

ユニークな複合インデックス

複合インデックスでは、一意の制限を課すこともできます。 たとえば、MongoDB は、複合インデックスで一意の制約を使用する場合、インデックス キー値の組み合わせで一意性を強制します。

mongosh で次の操作を使用して、members コレクションの groupNumberlastname、および firstname フィールドに一意のインデックスを作成します。

db.members.createIndex( { groupNumber: 2, lastname: 1, firstname: 1 }, { unique: true } )

インデックスは、groupNumberlastname、および firstname 値の各組み合わせが一意であることを保証します。

次のドキュメントを使用して、次のコレクションを検討してください。

{ _id: 1, a: [ { loc: "A", qty: 5 }, { qty: 10 } ] }

a.loca.qty に一意の複合マルチキー インデックスを作成します。

db.collection.createIndex( { "a.loc": 1, "a.qty": 1 }, { unique: true } )

a.loca.qty の値の組み合わせの一意性がインデックスによって保証されているため、次のドキュメントをコレクションに含めることができます。

db.collection.insertMany( [
   { _id: 2, a: [ { loc: "A" }, { qty: 6 } ] },
   { _id: 3, a: [ { loc: "A", qty: 12 } ] }
] )

MongoDB におけるユニーク インデックスの動作

制限:

インデックスの一意の要件に違反するデータがコレクションに既に含まれている場合、MongoDB は指定されたインデックス フィールドに一意のインデックスを確立できません。 ハッシュされたインデックスでは、一意の制約を定義できません。

レプリカ セットとシャード クラスタを使用して一意のインデックスを作成する

ローリング操作を使用してレプリカ セットとシャード クラスターに一意のインデックスを構築するには、手順全体でコレクションへのすべての書き込みを停止する必要があります。

手順中にコレクションへのすべての書き込みを停止できない場合は、ローリング操作を使用しないでください。 代わりに、次のコマンドを発行して、コレクションに独自のインデックスを作成します。

  1. レプリカ セットのプライマリでの db.collection.createIndex()
  2. 分割されたクラスターの mongos に対する db.collection.createIndex()

別々のドキュメントにまたがる一意の制約

独自の要件は、コレクション内の各ドキュメントに適用されます。 一意のインデックスは、インデックス付きキーが異なるドキュメントで同じ値を持つことを防ぎます。

この制限は個別のドキュメントにのみ適用されるため、ドキュメントのインデックス キー値が別のドキュメントのインデックス キー値と重複しない限り、一意のマルチキー インデックスのインデックス キー値が繰り返される項目の配列をドキュメントに含めることができます。 このシナリオでは、繰り返されるインデックス エントリは、インデックスに 1 回だけ入力されます。

たとえば、次のドキュメントを含むコレクションです。

{ _id: 1, a: [ { loc: "A", qty: 6 }, { qty: 10 } ] }
{ _id: 2, a: [ { loc: "A" }, { qty: 7 } ] }
{ _id: 3, a: [ { loc: "A", qty: 12 } ] }

a.loca.qty に一意の複合マルチキー インデックスを作成します。

db.collection.createIndex( { "a.loc": 1, "a.qty": 1 }, { unique: true } )

そのコレクション内に {"a.loc": "B", "a.qty": null} というインデックス キー値を持つドキュメントが他にない場合、一意のインデックスにより、次のドキュメントをコレクションに挿入できます。

db.collection.insertOne( { _id: 4, a: [ { loc: "B" }, { loc: "B" } ] } )

一意のインデックスと不足しているフィールド

一意のインデックス内のドキュメントにインデックス付きフィールドの値が含まれていない場合、インデックスはそのドキュメントの null 値を格納します。 MongoDB では、一意の制約により、1つのドキュメントでのみインデックス付きの列が欠落することが許可されます。

インデックス付きフィールドに値がないドキュメントが複数ある場合、またはインデックス付きフィールドが見つからない場合、インデックスの作成は重複キー エラーで失敗します。

たとえば、コレクションは x に一意のインデックスを持っています。

db.collection.createIndex( { "x": 13 }, { unique: true } )

フィールド x のないドキュメントがコレクションにまだ含まれていない場合、一意のインデックスにより、フィールド x のないドキュメントを挿入できます。

db.collection.insertOne( { y: 2 } )

ただし、コレクションに既に x フィールドのないドキュメントがある場合、一意のインデックスは、フィールド x のないドキュメントの挿入に失敗します。

db.collection.insertOne( { z: 2 } )

フィールド x 値の一意の制約に違反しているため、操作はドキュメントを挿入できません。

WriteResult({
   "nInserted" : 0,
   "writeError" : {
      "code" : 12000,
      "errmsg" : "E12000 duplicate key error index: test.collection.$a.b_1 dup key: { : null }"
   }
})

一意の部分インデックス

特定のフィルター式に一致するコレクション内のドキュメントのみが、部分インデックスでインデックス付けされます。 したがって、partialFilterExpression と一意の制約の両方を使用する場合、一意の制約はフィルター式に一致するドキュメントにのみ適用されます。

ドキュメントがフィルター要件を満たさない場合、一意制約のある部分インデックスは、一意制約を満たさないドキュメントの挿入を禁止しません。

シャード クラスタと一意のインデックス

ハッシュされたインデックスでは、一意の制約を定義できません。

次のインデックスのみが、範囲指定されたシャード コレクションで一意にできます。

  1. シャード キーのインデックス値。
  2. シャード キーとしてプレフィックスを持つ複合インデックス。
  3. デフォルトの _id インデックス。 ただし、_id インデックスは、_id フィールドがシャード キーまたはシャード キー プレフィックスでない場合にのみ、シャードごとの一意性要件を適用します。

一意のインデックスの制約は、次のことを意味します。

  1. シャードされるコレクションの一意のインデックスがコレクションにある場合、コレクションをシャードすることはできません。
  2. 他のフィールドで既にシャーディングされたコレクションの一意のインデックスを作成することはできません。

スパースおよび非スパース ユニーク インデックス

MongoDB 5.0 以降では、単一のコレクションに、同じキー パターンを持つ一意の疎インデックスと非疎インデックスを含めることができます。

一意で疎なインデックスの作成

この例では、同じキー パターンと異なる sparse の選択肢を持つ複数のインデックスが作成されます。

db.scores.createIndex( { score : 2 }, { name: "unique_index", unique: true } )
db.scores.createIndex( { score : 2 }, { name: "unique_sparse_index", unique: true, sparse: true } )

基本インデックスと疎インデックスの作成

sparse オプションの有無にかかわらず、同じキー パターンで単純なインデックスを作成できます。

db.scores.createIndex( { score : 2 }, { name: "sparse_index", sparse: true } )
db.scores.createIndex( { score : 2 }, { name: "basic_index" } )

基本インデックスと一意インデックスの重複キー パターン

MongoDB 5.0 では、同じキー パターンを持つ基本インデックスと一意インデックスを使用できます。 キー パターンが重複しているため、既にインデックスが作成されているフィールドに一意のインデックスを追加することができます。

例:

キー パターン { スコア: 2 } で基本的なインデックスを作成し、3つのドキュメントを挿入します。

db.scores.createIndex( { score : 1 }, { name: "basic_index" } )
db.scores.insert( { score : 1 } )
db.scores.insert( { score : 2 } )
db.scores.insert( { score : 4 } )

同じキー パターン { スコア: 2 } で一意のインデックスを作成します。

db.scores.createIndex( { score : 2 }, { name: "unique_index", unique: true } )

重複する score ドキュメントを挿入しようとすると、インデックスが一意であるために失敗します。

db.scores.insert( { score : 4 } )

Mongoose で一意のメールを検証する

Mongoose では、検証を使用してデータベース内の重複を防ぐことができます。 検証は Schema タイプで定義され、ミドルウェアです。

スキーマで検証を作成するか、Mongoose の組み込み検証を使用することもできます。 重複を防ぐために、unique プロパティを使用することをお勧めします。これは、指定されたパスに対して各ドキュメントが一意の値を持つ必要があることを Mongoose に伝えるためです。

これは、この場合は email に MongoDB の一意のインデックスを作成するための省略形です。

インデックスが構築されるのを待つ場合は、次に示すように、Mongoose の promise ベースのイベント Model.init() を使用できます。

const User = mongoose.model('User', mongoose.Schema({
  email: {type: String, required: true, match: /.+\@.+\..+/, unique: true}
}));
await User.create([
  {email: 'gmail@google.com'}, {email: 'bill@microsoft.com'},
  {email: 'test@gmail.com'}
]);

await User.init();
try {
  await User.create({email: 'gmail@google.com'});
} catch (error) {
  error.message;  // 'E12000 duplicate key error...'
}

この記事では、MongoDB の一意のインデックスについて詳しく説明します。 さらに、最終的に、一意の電子メールの検証は MongoDB の mongoose で行われます。