mongoDB/mongoose: уникальный, если не null

мне было интересно, есть ли способ заставить уникальную запись коллекции но только если запись не null. ми Пример схемы:

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

"электронная почта" в этом случае не требуется, но если "электронная почта" сохранена, я хочу убедиться, что эта запись уникальна (на уровне базы данных).

пустые записи, похоже, получают значение "null", поэтому каждая запись без сбоев электронной почты с опцией "unique" (если есть другой пользователь без электронной почты).

сейчас я решение его на уровне приложения, но хотелось бы сохранить этот запрос db.

thx

4 ответов


по состоянию на MongoDB v1.8+ вы можете получить желаемое поведение обеспечения уникальных значений, но разрешить несколько документов без поля, установив true при определении индекса. Как в:

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

или в оболочке:

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

обратите внимание, что уникальный, разреженный индекс по-прежнему не позволяет несколько документов с


tl; dr

Да, это можно сделать.
Несколько документов с полем null или не определен, применяя уникальные "фактические" значения.

требования:

  • MongoDB v3.2+.
  • когда поле имеет фактическое значение, оно должно быть определенного типа (e.g, всегда строка).

как? Перейдите к деталям реализации ниже.

больше версия

чтобы дополнить ответ @Nolan, начиная с MongoDB v3.2 можно использовать частичный уникальный индекс с выражением фильтра.

выражение частичного фильтра имеет ограничения. Он может включать только следующее:

  • выражения равенства (т. е. поле: Значение или с помощью $eq оператор),
  • $exists: true выражение
  • $gt, $gte, $lt, $lte выражения
  • $type выражения
  • $and оператор только верхнего уровня

это означает, что тривиальное выражение {"yourField"{$ne: null}} не может быть использован.

однако, предполагая, что ваше поле всегда использует один и тот же тип, вы можете использовать $type выражение.

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

реализация

мангуста

вы можете указать его в мангуста схема:

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'}}
    }
  }
});

или непосредственно добавьте его в коллекцию (которая использует собственный узел.водитель с 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 электронная почта или вообще без поля электронной почты, но не с той же строкой электронной почты.


просто быстрое обновление для тех, кто исследует эту тему.

выбранный ответ будет работать, но вы, возможно, захотите рассмотреть возможность использования частичных индексов.

изменено в версии 3.2: начиная с MongoDB 3.2, MongoDB предоставляет возможность создания частичных индексов. Частичные индексы предлагают надмножество функциональность разреженных индексов. Если вы используете MongoDB 3.2 или позже, частичные индексы должны быть предпочтительнее, чем редкие индексы.

больше doco по частичным индексам:https://docs.mongodb.com/manual/core/index-partial/


на самом деле, только первый документ, где "электронная почта" как поле не существует, будет успешно сохранен. Последующие сохранения, где "электронная почта" отсутствует, завершатся ошибкой ( см. фрагмент кода ниже). По этой причине посмотрите официальную документацию MongoDB в отношении уникальных индексов и отсутствующих ключей здесь http://www.mongodb.org/display/DOCS/Indexes#Indexes-UniqueIndexes.

  // 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"});

по определению уникальный индекс может позволить хранить только одно значение однажды. Если вы считаете null одним из таких значений, его можно вставить только один раз! Вы правы в своем подходе, обеспечивая и проверяя его на уровне приложения. Вот как это можно сделать.

вы также можете прочитать это http://www.mongodb.org/display/DOCS/Querying+и + нули