我的 Rails 应用程序中有 3 个模型
class Contact < ActiveRecord::Base
belongs_to :survey, counter_cache: :contact_count
belongs_to :voter
has_many :contact_attempts
end
class Survey < ActiveRecord::Base
has_many :questions
has_many :contacts
end
class Voter < ActiveRecord::Base
has_many :contacts
end
联系人由 voter_id 和 Survey_id 组成。我的应用程序的逻辑是,在任何给定的调查中,选民只能有一个联系人。
现在我正在使用以下代码来强制执行此逻辑。我在联系人表中查询与给定 voter_id 和 Survey_id 匹配的记录。如果不存在则创建它。否则它什么也不做。
if !Contact.exists?(:survey_id => survey, :voter_id => voter)
c = Contact.new
c.survey_id = survey
c.voter_id = voter
c.save
end
显然这需要一个选择和一个插入查询来创建 1 个潜在联系人。当我一次添加可能数千个联系人时。
现在我正在使用 Resque 来允许它在后台运行并且远离 ui 线程。我可以做些什么来加快速度并提高效率?
您可以执行以下操作:
Contact.where(survey_id: survey,voter_id: voter).first_or_create
您应该首先添加一个数据库索引,以尽可能将这种情况强制在最低级别:
add_index :contacts, [:voter_id, :survey_id], unique: true
然后您应该在 ActiveRecord 级别添加唯一性验证:
validates_uniqueness_of :voter_id, scope: [:survey_id]
如果指定选民和调查存在联系人,
contact.save
将返回 false
。
更新:如果您创建索引,那么唯一性验证将运行得非常快。
看看这些链接是否可以帮助您。
这些链接适用于 Rails 4.0.2,但您可以在 api 码头中进行更改
来自 apidock:first_or_create、find_or_create_by
来自 Rails 指南:查找或创建者
如果让MySQL来处理会更好。
创建迁移并将复合唯一键添加到
survey_id, voter_id
add_index :contact, [:survey_id, :voter_id], :unique=> true
现在
Contact.create(:survey_id=>survey, :voter_id=>voter_id)
仅当没有重复项时才会创建新记录。
您可以使用
find_or_create_by
方法来实现此目的。如果您只想检查某些字段,则可以选择将块传递给此方法。仅当没有找到记录时才会执行此块。
文档说:
只有在创建客户时才会执行该块。我们第二次运行此代码时,该块将被忽略。
它的工作原理如下:
Contact.find_or_create_by({survey_id: survey,voter_id: voter})
或者像这样的块:
Contact.find_or_create_by({survey_id: survey}) do |contact|
contact.voter_id = voter
end