【问题标题】:mongoDB/mongoose: unique if not nullmongoDB/mongoose:如果不为空,则为唯一
【发布时间】:2011-12-18 19:05:12
【问题描述】:

我想知道是否有办法强制一个唯一的集合条目但前提是条目不为空。 e 示例架构:

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

在这种情况下不需要“电子邮件”,但如果保存“电子邮件”,我想确保此条目是唯一的(在数据库级别上)。

空条目的值似乎是“null”,因此每个没有电子邮件的条目都会使用“唯一”选项崩溃(如果有其他用户没有电子邮件)。

现在我正在应用程序级别解决它,但很想保存那个数据库查询。

谢谢

【问题讨论】:

    标签: mongodb node.js mongoose


    【解决方案1】:

    从 MongoDB v1.8+ 开始,您可以通过在定义索引时将 sparse 选项设置为 true 来获得确保唯一值但允许多个文档没有字段的所需行为。如:

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

    或者在shell中:

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

    请注意,唯一的稀疏索引仍然不允许具有email 字段的多个文档,null,只有多个文档没有 @ 987654328@字段。

    http://docs.mongodb.org/manual/core/index-sparse/

    【讨论】:

    • 太棒了!绝对是 1.8 之后像我这样的新手的最佳答案!注意:如果您只是将 sparse : true 添加到您的架构,Mongoose 不会将您的唯一索引更新为稀疏。您必须删除并重新添加索引。不知道这是预期的还是错误。
    • "注意:如果数据库上已经存在索引,则不会被替换。" - mongoosejs.com/docs/2.7.x/docs/schematypes.html
    • 我认为这不能正确回答问题,因为一些没有特定字段的文档与一些在该字段上具有空值的文档(不能唯一索引)不同。
    • @kako-nawao 是的,它只适用于没有email 字段的文档,而不是它实际存在的值null。查看更新的答案。
    • 不适用于缺少的字段。也许在更高版本的 mongodb 中行为发生了变化。答案应该更新。
    【解决方案2】:

    tl;博士

    是的,可以有多个文档,其中一个字段设置为 null 或未定义,同时强制执行唯一的“实际”值。

    要求

    • MongoDB v3.2+。
    • 提前了解具体的值类型(例如,始终为 stringobject,而不是 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 字段接受stringbinary 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 电子邮件插入多条记录,或者根本没有电子邮件字段,但不能使用相同的电子邮件字符串。

    【讨论】:

    • 很棒的答案。你是救世主。
    • 这个答案也为我做了。
    • 这个问题的大多数公认答案都涉及确保您没有为索引键显式设置空值。而是将它们传递给未定义的。我正在这样做,但仍然出现错误(使用uniquesparse 时)。我用这个答案更新了我的架构,删除了我现有的索引,它就像一个魅力。
    • 投给了这个,因为它提供了基于最常见场景的知识和可能的答案,人们首先会在这个 SO 答案上结束。感谢您的详细回答! :+1:
    【解决方案3】:

    只是对那些研究这个主题的人的快速更新。

    选择的答案会起作用,但您可能需要考虑使用部分索引。

    3.2 版更改:从 MongoDB 3.2 开始,MongoDB 提供 创建部分索引的选项。部分索引提供了一个超集 稀疏索引的功能。如果您使用的是 MongoDB 3.2 或 以后,部分索引应该优先于稀疏索引。

    关于部分索引的更多文档:https://docs.mongodb.com/manual/core/index-partial/

    【讨论】:

      【解决方案4】:

      实际上,只有“email”字段不存在的第一个文档才能成功保存。不存在“电子邮件”的后续保存将失败,同时给出错误(参见下面的代码 sn-p)。原因请查看关于唯一索引和缺失键的 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

      【讨论】:

        猜你喜欢
        • 2022-01-07
        • 1970-01-01
        • 2012-09-09
        • 1970-01-01
        • 1970-01-01
        • 2021-12-05
        相关资源
        最近更新 更多