mongoDB / mongoose: уникальный, если не нулевой - PullRequest
81 голосов
/ 31 октября 2011

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

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

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

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

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

thx

Ответы [ 4 ]

133 голосов
/ 14 марта 2012

Начиная с 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/

28 голосов
/ 05 мая 2017

tl; dr

Да, возможно иметь несколько документов с полем, установленным на null или не определенным, при одновременном применении уникальных «фактических» значений.

требования :

  • MongoDB v3.2 +.
  • Знание ваших конкретных типов значений заранее (например, всегда string или object, когда не null).

Если вы не заинтересованыне стесняйтесь переходить к разделу implementation.

более длинная версия

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

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

  • выражения равенства (т. Е. Field: value или с использованием оператора $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"]}}

реализация

mongoose

Вы можете сpecify его в схеме mongoose:

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

Thisпозволит вставлять несколько записей с null электронной почтой или без поля электронной почты вообще, но не с одной и той же строкой электронной почты.

4 голосов
/ 27 марта 2017

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

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

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

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

2 голосов
/ 31 октября 2011

На самом деле, только первый документ, в котором "email" как поле не существует, будет успешно сохранен. Последующее сохранение, где отсутствует «электронная почта», завершится с ошибкой (см. Фрагмент кода ниже). По этой причине посмотрите официальную документацию 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+and+nulls

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