Mongoose文档模式和验证

7

我有一个类似这样的架构:

class Schemas

  constructor: ->
    @mongoose = require 'mongoose'
    @schema = @mongoose.Schema

    @EmployeeSchema = new @schema
      'firstname': { type: String, required: true }, 
      'lastname': { type: String, required: true }, 
      'email': { type: String, required: true, index: { unique: true }, validate: /\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}\b/ },
      'departmentId': { type: @schema.ObjectId, required: true }
      'enddate': String,
      'active': { type: Boolean, default: true } 

    @EmployeeSchemaModel = @mongoose.model 'employees', @EmployeeSchema

    @DepartmentSchema = new @schema
      'name': { type: String, required: true, index: { unique: true } }
      'employees' : [ @EmployeeSchema ]

    @DepartmentSchemaModel = @mongoose.model 'departments', @DepartmentSchema

为了让我的员工住在一个部门的员工文档数组里,我有几个部门文档,其中存储了一些员工文档。然后我添加了一个新的部门,但它没有包含员工。如果我尝试再次添加没有员工的另一个部门,Mongoose会产生一个“重复键错误”,因为员工的电子邮件是必填的且唯一的。员工的电子邮件字段是必须的和唯一的,它需要这样。有没有什么办法解决这个问题?
3个回答

6

如果您使用咖啡脚本等效的方式启用Mongoose调试日志记录,即mongoose.set('debug', true);,您可以看到发生了什么:

DEBUG: Mongoose: employees.ensureIndex({ email: 1 }) { safe: true, background: true, unique: true }      
DEBUG: Mongoose: departments.ensureIndex({ name: 1 }) { safe: true, background: true, unique: true }      
DEBUG: Mongoose: departments.ensureIndex({ 'employees.email': 1 }) { safe: true, background: true, unique: true }  

通过将完整的EmployeeSchema嵌入到DepartmentSchemaemployees数组中(而不仅仅是它的ObjectId引用),您最终在employees.emaildepartment.employees.email上创建了唯一索引。

因此,当您创建一个没有任何员工的新department时,您会“使用”department.employees.email索引中未定义的电子邮件值作为唯一性。那么当您尝试第二次这样做时,该唯一值已被占用,您会得到Duplicate key error

最好的解决方法可能是将DepartmentSchema.employees更改为对员工的ObjectId引用数组,而不是完整对象。然后,索引保留在应属于employees集合的地方,您不会重复数据或创建不一致的机会。


谢谢Johnny,我之前不知道_debug_命令,这对于未来的调试非常有用。我也不知道Mongoose正在创建两个索引(当我只要求一个时)。我知道我可以创建2个不同的集合,并通过ObjectId使一个集合_refer_到另一个集合,但那样我就回到了关系模型。作为妥协,我想我可以在嵌入式员工对象中引用_email-address_集合并创建唯一索引。这似乎是这个模型的一个很好的解决方案。 - Simon Lomax

1

请查看以下参考资料:

http://docs.mongodb.org/manual/core/indexes/#sparse-indexes

mongoDB/mongoose: 如果非空则唯一 (特别是JohnnyHK的答案)

简单来说,自Mongo 1.8以来,您可以定义所谓的sparse索引,它仅在值不为空时才启动唯一性检查。

在您的情况下,您需要:

@EmployeeSchema = new @schema
  'firstname': { type: String, required: true }, 
  'lastname': { type: String, required: true }, 
  'email': { type: String, required: true, index: { unique: true, sparse: true }, validate: /\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}\b/ },
  'departmentId': { type: @schema.ObjectId, required: true }
  'enddate': String,
  'active': { type: Boolean, default: true }

注意在EmployeeSchema的电子邮件属性上添加了到您的索引。

https://gist.github.com/juanpaco/5124144


0

看起来你不能在子文档的单个字段上创建一个唯一索引。虽然Mongo shell中的db.collection.ensureIndex函数似乎可以让你这样做,但它会将子文档作为整体测试其唯一性而不是单个字段。

你可以在子文档的单个字段上创建索引,只是不能使它们唯一


2
您可以在子文档字段上创建唯一索引,但唯一性是在整个集合中强制执行的,并且“无值”被视为唯一值之一。请参阅我的答案。 - JohnnyHK

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接