mongoDB / mongoose:nullでない場合は一意


103

一意のコレクションエントリを強制する方法はあるのかと思っていましたが、エントリがnullでない場合のみです。eサンプルスキーマ:

var UsersSchema = new Schema({
    name  : {type: String, trim: true, index: true, required: true},
    email : {type: String, trim: true, index: true, unique: true}
});

この場合の「email」は必須ではありませんが、「email」が保存されている場合は、このエントリーが(データベースレベルで)一意であることを確認したいと思います。

空のエントリは値「null」を取得するように見えるため、電子メールがないすべてのエントリは「一意」オプションでクラッシュします(電子メールを持たない別のユーザーがいる場合)。

現在、私はアプリケーションレベルでそれを解決していますが、そのdbクエリを保存したいと思います。

どうも

回答:


168

MongoDB v1.8以降でsparseは、インデックスを定義するときにオプションをtrueに設定することにより、一意の値を確保しながらフィールドなしで複数のドキュメントを許可するという望ましい動作を得ることができます。のように:

email : {type: String, trim: true, index: true, unique: true, sparse: true}

またはシェルで:

db.users.ensureIndex({email: 1}, {unique: true, sparse: true});

一意のスパースインデックスemailでは、がのフィールドを持つ複数のドキュメントは許可され、フィールドのnullない複数のドキュメントのみが許可されることに注意してください。email

http://docs.mongodb.org/manual/core/index-sparse/を参照してください


18
驚くばかり!私のような初心者にとって、1.8以降は間違いなく最良の答えです!注:sparse:trueをスキーマに追加しただけでは、Mongooseは一意のインデックスをスパースに更新しません。インデックスを削除して再度追加する必要があります。それが予想されるかバグであるならば、Dunno。
Adam A

8
「注:インデックスがすでにデータベースに存在する場合、置き換えられません。」- mongoosejs.com/docs/2.7.x/docs/schematypes.html
damphat

特定のフィールドを持たないいくつかのドキュメントは、そのフィールドにnull値を含むいくつかのドキュメント(一意にインデックスを付けることができない)と同じではないので、これが質問に正しく答えるとは思いません。
kako-nawao 2015年

1
@ kako-nawao確かに、emailフィールドがない場合にのみ機能し、実際にが存在する場所では機能しませんnull。更新された回答を参照してください。
JohnnyHK 2015年

2
欠落しているフィールドでは機能しません。後のバージョンのmongodbでは動作が変更された可能性があります。回答を更新する必要があります。
joniba 2016

44

tl; dr

はい、null一意の「実際の」値を適用しながら、フィールドが設定されているか定義されていない複数のドキュメントを持つことが可能です。

要件

  • MongoDB v3.2以降。
  • 事前に具体的な値のタイプを知っている(たとえば、常にa stringかそうでないobject場合null)。

詳細に興味がない場合は、このimplementationセクションに進んでください。

より長いバージョン

@Nolanの回答を補足するために、MongoDB v3.2以降、フィルター式で部分一意インデックスを使用できます。

部分フィルター式には制限があります。以下のみを含めることができます。

  • 等式(フィールド:値または$eq演算子の使用)、
  • $exists: true 式、
  • $gt$gte$lt$lte表情、
  • $type 式、
  • $and トップレベルの演算子のみ

つまり、自明な表現{"yourField"{$ne: null}}は使用できません。

ただし、フィールドで常に同じタイプを使用すると仮定すると、$typeを使用できます。

{ field: { $type: <BSON type number> | <String alias> } }

MongoDB v3.6は、配列として渡すことができる複数の可能なタイプを指定するためのサポートを追加しました。

{ field: { $type: [ <BSON type1> , <BSON type2>, ... ] } }

つまり、そうでない場合、値は複数の複数のタイプのいずれかになることができますnull

したがって、email以下の例のフィールドに値stringまたはbinary data値のいずれかを許可する場合、適切な$type式は次のようになります。

{email: {$type: ["string", "binData"]}}

実装

マングース

あなたはマングーススキーマでそれを指定することができます:

const UsersSchema = new Schema({
  name: {type: String, trim: true, index: true, required: true},
  email: {
    type: String, trim: true, index: {
      unique: true,
      partialFilterExpression: {email: {$type: "string"}}
    }
  }
});

または直接コレクションに追加します(ネイティブのnode.jsドライバーを使用します):

User.collection.createIndex("email", {
  unique: true,
  partialFilterExpression: {
    "email": {
      $type: "string"
    }
  }
});

ネイティブのmongodbドライバー

を使用して collection.createIndex

db.collection('users').createIndex({
    "email": 1
  }, {
    unique: true,
    partialFilterExpression: {
      "email": {
        $type: "string"
      }
    }
  },
  function (err, results) {
    // ...
  }
);

mongodbシェル

を使用してdb.collection.createIndex

db.users.createIndex({
  "email": 1
}, {
  unique: true, 
  partialFilterExpression: {
    "email": {$type: "string"}
  }
})

これにより、null電子メールを使用して、または電子メールフィールドをまったく使用せずに、同じ電子メール文字列を使用せずに複数のレコードを挿入できます。


素晴らしい答え。あなたは救世主です。
r3wt

この答えは私にもできました。
Emmanuel NK

1
この質問で受け入れられる回答のほとんどは、インデックス付きキーに明示的にnull値を設定していないことを確認することです。代わりに未定義で渡されること。私はそれをしていて、まだエラーが発生していました(uniqueおよびを使用している間sparse)。私はこの答えでスキーマを更新し、既存のインデックスを削除しましたが、それは魅力的なように機能しました。
フィル

これは、最も一般的なシナリオに基づいて知識と可能な回答を提供するため、投票されました。詳しい回答ありがとうございます!:+1:
anothercoder

6

このトピックを研究している人への簡単な更新です。

選択した答えは機能しますが、代わりに部分インデックスを使用することを検討してください。

バージョン3.2で変更:MongoDB 3.2以降、MongoDBは部分的なインデックスを作成するオプションを提供します。部分インデックスは、スパースインデックスの機能のスーパーセットを提供します。MongoDB 3.2以降を使用している場合は、部分インデックスがスパースインデックスよりも優先されます。

部分インデックスに関するその他のドコ:https : //docs.mongodb.com/manual/core/index-partial/


2

実際には、フィールドとしての「電子メール」が存在しない最初のドキュメントのみが正常に保存されます。「email」が存在しない後続の保存は、エラーが発生する一方で失敗します(以下のコードスニペットを参照)。理由については、http://www.mongodb.org/display/DOCS/Indexes#Indexes-UniqueIndexesにある一意のインデックスと欠落しているキーに関するMongoDBの公式ドキュメントを参照してください

  // NOTE: Code to executed in mongo console.

  db.things.ensureIndex({firstname: 1}, {unique: true});
  db.things.save({lastname: "Smith"});

  // Next operation will fail because of the unique index on firstname.
  db.things.save({lastname: "Jones"});

定義により、一意のインデックスでは、1つの値のみを1回だけ格納できます。nullをそのような値の1つと考える場合、挿入できるのは1回だけです。アプリケーションレベルで確認および検証することにより、アプローチが正しい。それはそれができる方法です。

また、http://www.mongodb.org/display/DOCS/Querying+and+nullsを読むこともできます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.