Создать, если запись не существует

У меня есть 3 модели в моем приложении rails

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. Что я могу сделать, чтобы ускорить это и сделать его более эффективным?

Ответы

Ответ 1

Вы должны сначала добавить индекс базы данных, чтобы принудительно выполнить это условие на самом нижнем уровне:

add_index :contacts, [:voter_id, :survey_id], unique: true

Затем вы должны добавить проверку уникальности на уровне ActiveRecord:

validates_uniqueness_of :voter_id, scope: [:survey_id]

Затем contact.save вернет false, если контакт существует для указанного избирателя и опроса.

UPDATE: если вы создадите индекс, то проверка уникальности будет выполняться довольно быстро.

Ответ 2

Вы можете сделать следующее:

Contact.where(survey_id: опрос, voter_id: избиратель).first_or_create

Ответ 3

Посмотрите, могут ли эти ссылки помочь вам.

Эти ссылки предназначены для рельсов 4.0.2, но вы можете изменить их в api docks

Из apidock: first_or_create, find_or_create_by
Из руководства Rails: find-or-create-by

Ответ 4

Было бы лучше, если бы вы позволили MySQL справиться с этим.

Создайте перенос и добавьте составной уникальный ключ в survey_id, voter_id

add_index :contact, [:survey_id, :voter_id], :unique=> true

Теперь

Contact.create(:survey_id=>survey, :voter_id=>voter_id) 

Создает новую запись только в том случае, если дубликатов нет.