mongoDB 3.2中的唯一索引忽略空值

Posted

技术标签:

【中文标题】mongoDB 3.2中的唯一索引忽略空值【英文标题】:Unique index in mongoDB 3.2 ignoring null values 【发布时间】:2016-06-15 19:15:11 【问题描述】:

我想将唯一索引添加到忽略唯一索引字段中的空值并忽略基于 partialFilterExpression 过滤的文档的字段。

问题是稀疏索引不能与部分索引一起使用。

此外,添加唯一索引会将空值添加到索引键字段,因此不能根据 PartialFilterExpression 中的 $exist 条件忽略文档。

在 MongoDB 3.2 中是否可以解决这种情况?

【问题讨论】:

空值在您的情况下有什么特殊含义吗?如果没有,您可以随时 $unset 为所有空值设置该字段,从而可以使用 $exist 运算符。 添加唯一索引自动添加空字段,因此我们不能拥有没有唯一索引键字段的文档。 【参考方案1】:

我正在添加此答案,因为我正在寻找解决方案但没有找到。这可能不能完全回答这个问题,或者可能是,但会帮助很多像我这样的人。

示例。如果null的字段是houseName,并且是string类型,解决办法可以是这样的

db.collectionName.createIndex(
   name: 1, houseName: 1,
   unique: true, partialFilterExpression: houseName: $type: "string"
);

这将忽略字段houseName 中的null 值,并且仍然是唯一的。

【讨论】:

只是我的测试中的一个注释,供其他任何寻求相同解决方案的人使用:虽然这可以作为一种在允许空值的同时强制唯一性的机制,但 Mongo 并未使用索引字段使用索引进行查找查询并使用 hint() 强制它导致性能非常缓慢 @PeteS 我看到了同样的行为,你找到可行的解决方案了吗? @MattOakley 不完全是。我们采用了一种解决方法,即在索引上设置 partialFilterExpression: fieldToIndex: $exists: true ,然后使用 [BsonIgnoreIfNull] (C#) 在应用程序代码中装饰我们类中的相应属性。如果保存时应用程序中的字段为空,Mongo 驱动程序将不会序列化该字段。 如果部分索引包含$type: string,则应将其添加到查询中以使用索引(如.find( houseName: $type: "string", $eq: "some value" ) @PeteS 和@oryol 是正确的,在默认请求中使用$type: "string",不会提高性能,必须使用 $exists: true 【参考方案2】:

是的,您可以在 MongoDB 3.2 中创建部分索引

请看https://docs.mongodb.org/manual/core/index-partial/#index-type-partial

MongoDB 建议使用部分索引而不是稀疏索引。我会建议你放弃你的稀疏索引以支持部分索引。

【讨论】:

我必须使用 PartialFilterExpression 过滤数据以进行索引,并且必须使用唯一索引。 唯一的事情是我希望某些文档没有唯一的索引键字段,这似乎是不可能的,或者我可能不知道。【参考方案3】:

您可以在 mongo:3.2 中创建部分索引。 例如,如果 ipaddress 可以是“”,但“127.0.0.1”应该是唯一的。解决办法可以是这样的:

db.collectionName.createIndex(
 "ipaddress":1,
 "unique":true, "partialIndexExpression":"ipaddress":"$gt":"")

这将忽略 ipaddress 字段中的“”值,并且仍然是唯一的

【讨论】:

【参考方案4】:

“你的领域”: “$exists”:是的, "$gt" : "0", “$类型”:“字符串”

【讨论】:

如果您想要一个索引对于所有不等于空字符串或 null 的值都是唯一的,那么这个答案是完美的。【参考方案5】:

要在 mongodbCompass 中创建,您必须将其编写为 JSON:

要查找支持的其他类型,请参阅this link。

【讨论】:

【参考方案6】:

是的,这可能是部分过滤器表达式不能包含任何“非”过滤器的问题。

对于那些可能对此类索引的 C# 解决方案感兴趣的人,这里有一个示例。

我们有一个“用户”实体,它与“医生”实体具有一对一的“关系”。 此关系由“用户”实体中的非必需、可为空的字段“DoctorId”表示。换句话说,要求给定的“医生”一次只能链接到单个“用户”。

所以我们需要一个唯一的索引,当某些东西试图将 DoctorId 设置为已经为任何其他“用户”实体设置的相同 Guid 时,它可以触发异常。同时,“DoctorId”字段必须允许多个“null”条目,因为许多用户没有附加任何医生。

构建这种索引的解决方案如下:

var uniqueDoctorIdIndexDefinition = new IndexKeysDefinitionBuilder<User>()
    .Ascending(o => o.DoctorId);
var existsFilter = Builders<User>.Filter.Exists(o => o.DoctorId);
var notNullFilter = Builders<User>.Filter.Type(o => o.DoctorId, BsonType.String);
var andFilter = Builders<User>.Filter.And(existsFilter, notNullFilter);
var createIndexOptions = new CreateIndexOptions<User>

    Unique = true,
    Name = UniqueDoctorIdIndexName,
    PartialFilterExpression = andFilter,
;
var uniqueDoctorIdIndex = new CreateIndexModel<User>(
    uniqueDoctorIdIndexDefinition,
    createIndexOptions);

users.Indexes.CreateOne(uniqueDoctorIdIndex);

可能在您对“用户”实体的描述中,您必须通过使用属性直接指定“DoctorId”字段的 BsonType,例如在我们的例子中是:

[BsonRepresentation(BsonType.String)]
public Guid? DoctorId  get; set; 

我非常确定对于这个问题有一个更精通和紧凑的解决方案,所以如果有人在这里提出建议,我会很高兴。

【讨论】:

以上是关于mongoDB 3.2中的唯一索引忽略空值的主要内容,如果未能解决你的问题,请参考以下文章

Mongodb 唯一索引

MongoDB——索引属性之唯一索引(Unique Indexes)

MongoDB——索引属性之唯一索引(Unique Indexes)

在 NodeJS 中使用 Mongoose + Mockgoose 更新时忽略唯一索引

添加索引迁移中的唯一性被忽略

MongoDb在并非所有文档中的字段上创建唯一索引[重复]