tl;博士
是的,可以有多个文档,其中一个字段设置为 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 expression。
{ 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 电子邮件插入多条记录,或者根本没有电子邮件字段,但不能使用相同的电子邮件字符串。