如何在mongoid中强制使用独特的嵌入式文档

我有以下型号

class Person include Mongoid::Document embeds_many :tasks end class Task include Mongoid::Document embedded_in :commit, :inverse_of => :tasks field :name end 

我该如何确保以下内容?

 person.tasks.create :name => "create facebook killer" person.tasks.create :name => "create facebook killer" person.tasks.count == 1 different_person.tasks.create :name => "create facebook killer" person.tasks.count == 1 different_person.tasks.count == 1 

即任务名称在特定人员中是唯一的


检查了索引上的文档后,我认为以下内容可能有效:

 class Person include Mongoid::Document embeds_many :tasks index [ ["tasks.name", Mongo::ASCENDING], ["_id", Mongo::ASCENDING] ], :unique => true end 

 person.tasks.create :name => "create facebook killer" person.tasks.create :name => "create facebook killer" 

仍然会产生重复。


上面显示在Person中的索引配置将转换为mongodb

 db.things.ensureIndex({firstname : 1, 'tasks.name' : 1}, {unique : true}) 

默认情况下,索引不是唯一的。 如果你看看Mongo Docs ,那么唯一性是一个额外的标志。

我不知道确切的Mongoid翻译,但你正在寻找这样的东西:

db.things.ensureIndex({firstname : 1}, {unique : true, dropDups : true})

难道你不能在任务上放一个validation器吗?

 validates :name, :uniqueness => true 

这应该确保父文档中的唯一性。

我不相信嵌入式文档可以实现这一点。 我遇到了与您相同的问题,我发现的唯一解决方法是使用引用的文档,而不是嵌入的文档,然后在引用的文档上创建复合索引。

显然,唯一性validation是不够的,因为它不能防范竞争条件。 我遇到的唯一索引的另一个问题是,如果validation通过并且数据库拒绝接受该文档,则mongoid的默认行为是不会引发任何错误。 我不得不在mongoid.yml中更改以下配置选项:

 persist_in_safe_mode: true 

这在http://mongoid.org/docs/installation/configuration.html中有记录

最后,在进行此更改后,如果数据库拒绝存储文档,则save / create方法将开始抛出错误。 所以,你需要这样的东西才能告诉用户发生的事情:

 alias_method :explosive_save, :save def save begin explosive_save rescue Exception => e logger.warn("Unable to save record: #{self.to_yaml}. Error: #{e}") errors[:base] << "Please correct the errors in your form" false end end 

即使这不是一个很好的选择,因为你还在猜测哪些字段确实导致了错误(以及为什么)。 更好的解决方案是查看MongoidError并相应地创建正确的错误消息。 以上适合我的应用,所以我没有走那么远。

添加validation检查,将嵌入任务ID的数组计数与具有唯一ID的另一个数组的计数进行比较。

 validates_each :tasks do |record, attr, tasks| ids = tasks.map { |t| t._id } record.errors.add :tasks, "Cannot have the same task more than once." unless ids.count == ids.uniq.count end 

为我工作。

您可以在任务模型上定义validates_uniqueness_of以确保这一点,根据http://mongoid.org/docs/validation.html上的Mongoid文档,此validation适用于父文档的范围,并且应该执行您想要的操作。

您的索引技术也应该有效,但您必须在它们生效之前生成索引。 使用Rails,您可以使用rake任务执行此操作(在当前版本的Mongoid中称为db:mongoid:create_indexes)。 请注意,保存违反索引约束的内容时不会出现错误,因为Mongoid(有关详细信息,请参阅http://mongoid.org/docs/persistence/safe_mode.html )。

您还可以在模型类中指定索引:

 index({ 'firstname' => 1, 'tasks.name' => 1}, {unique : true, drop_dups: true }) 

并使用rake任务

 rake db:mongoid:create_indexes 

你必须运行:

 db.things.ensureIndex({firstname : 1, 'tasks.name' : 1}, {unique : true}) 

直接在数据库上

您似乎在“活动记录”(即类人员)中包含“创建索引命令”