MongoDB/mongoose: уникальный, если не null
Мне было интересно, есть ли способ принудительно ввести уникальную запись коллекции , но только если запись не является нулевой.
е
Пример схемы:
var UsersSchema = new Schema({
name : {type: String, trim: true, index: true, required: true},
email : {type: String, trim: true, index: true, unique: true}
});
"email" в этом случае не требуется, но если "электронная почта" сохраняется, я хочу убедиться, что эта запись уникальна (на уровне базы данных).
Пустые записи, похоже, получают значение "null", поэтому каждая запись без сбоев электронной почты с "уникальной" опцией (если есть другой пользователь без электронной почты).
Сейчас я решаю его на уровне приложения, но хотел бы сохранить этот запрос db.
ТНХ
Ответы
Ответ 1
Начиная с 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/
Ответ 2
ТЛ; др
Да, возможно иметь несколько документов с полем, равным null
или не определенным, с одновременным применением уникальных "фактических" значений.
требования:
- MongoDB v3. 2+.
- Знание ваших конкретных типов значений заранее (например, всегда
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
значения 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 Shell
используя db.collection.createIndex
:
db.users.createIndex({
"email": 1
}, {
unique: true,
partialFilterExpression: {
"email": {$type: "string"}
}
})
Это позволит вставлять несколько записей с null
адресом электронной почты или вообще без поля электронной почты, но не с одной и той же строкой электронной почты.
Ответ 3
Простое обновление для тех, кто исследует эту тему.
Выбранный ответ будет работать, но вы можете захотеть вместо этого использовать частичные индексы.
Изменено в версии 3.2: Начиная с MongoDB 3.2, MongoDB предоставляет возможность создания частичных индексов. Частичные индексы предлагают надмножество функциональность разреженных индексов. Если вы используете MongoDB 3.2 или позже, частичные индексы должны быть предпочтительнее более разреженных индексов.
Больше doco по частичным индексам: https://docs.mongodb.com/manual/core/index-partial/
Ответ 4
Фактически, только первый документ, в котором не существует поля "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