如何在mongoid中强制执行独特的嵌入式文档

时间:2010-12-02 19:03:11

标签: ruby-on-rails ruby-on-rails-3 mongodb 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})

7 个答案:

答案 0 :(得分:5)

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

validates :name, :uniqueness => true

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

答案 1 :(得分:1)

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

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

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

答案 2 :(得分:0)

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

显然,唯一性验证是不够的,因为它不能防范竞争条件。我遇到的唯一索引的另一个问题是,如果验证通过并且数据库拒绝接受该文档,则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并相应地创建正确的错误消息。以上适合我的申请,所以我没有走那么远。

答案 3 :(得分:0)

添加验证检查,将嵌入任务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

为我工作。

答案 4 :(得分:0)

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

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

答案 5 :(得分:0)

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

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

并使用rake任务

rake db:mongoid:create_indexes

答案 6 :(得分:-1)

你必须跑:

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

直接在数据库上

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