Самостоятельное соединение на столе с ActiveRecord - PullRequest
6 голосов
/ 04 сентября 2010

У меня есть ActiveRecord с именем Name, который содержит имена в различных Languages.

class Name < ActiveRecord::Base
  belongs_to :language

class Language < ActiveRecord::Base
  has_many :names

Найти имена на одном языке достаточно просто:

Language.find(1).names.find(whatever)

Но мне нужно найти подходящие пары, в которых и язык 1, и язык 2 имеют одно и то же имя. В SQL это требует простого самостоятельного объединения:

SELECT n1.id,n2.id FROM names AS n1, names AS n2
  WHERE n1.language_id=1 AND n2.language_id=2
    AND n1.normalized=n2.normalized AND n1.id != n2.id;

Как я могу сделать такой запрос с ActiveRecord? Обратите внимание, что мне нужно найти пары имен (= обе стороны совпадения), а не просто список имен на языке 1, который случайно совпадает с чем-либо.

Для бонусных баллов замените n1.normalized=n2.normalized на n1.normalized LIKE n2.normalized, поскольку поле может содержать символы подстановки SQL.

Я также открыт для идей о моделировании данных по-разному, но я бы предпочел избегать иметь отдельные таблицы для каждого языка, если смогу.

Ответы [ 2 ]

7 голосов
/ 05 сентября 2010

Попробуйте это:

ids = [1,2]
Name.all(:select    => "names.id, n2.id AS id2",
         :joins     => "JOIN names AS n2 
                              ON n2.normalized = names.normalized AND 
                                 n2.language_id != names.language_id AND
                                 n2.language_id IN (%s)" % ids.join(','),
         :conditions => ["names.language_id IN (?)", ids]
).each do |name|
  p "id1 : #{name.id}"
  p "id2 : #{name.id2}"
end

PS: убедитесь, что вы дезинфицируете параметры, переданные условию соединения.

1 голос
/ 04 сентября 2010

Звучит так, как будто вы захотите использовать отношение «многие ко многим» между языком и именем вместо has_many / assign_to.

>> Language.create(:name => 'English')
 => #<Language id: 3, name: "English", created_at: "2010-09-04 19:15:11", updated_at: "2010-09-04 19:15:11"> 
>> Language.create(:name => 'French')
 => #<Language id: 4, name: "French", created_at: "2010-09-04 19:15:13", updated_at: "2010-09-04 19:15:13"> 
>> Language.first.names << Name.find_or_create_by_name('Dave')
 => [#<Name id: 3, name: "Dave", language_id: 3, created_at: "2010-09-04 19:16:50", updated_at: "2010-09-04 19:16:50">] 
>> Language.last.names << Name.find_or_create_by_name('Dave')
 => [#<Name id: 3, name: "Dave", language_id: 4, created_at: "2010-09-04 19:16:50", updated_at: "2010-09-04 19:16:50">]
>> Language.first.names.first.languages.map(&:name)
 => ["English", "French"] 

Этот дополнительный уровень нормализации должен упростить то, что вы пытаетесь сделать.

...