Самостоятелно присъединяване към маса с ActiveRecord

Имам 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 заместващи знаци.

Също така съм отворен към идеи за моделиране на данните по различен начин, но бих предпочел да избягвам отделни таблици за всеки език, ако мога.


person lambshaanxy    schedule 04.09.2010    source източник


Отговори (2)


Опитайте тази:

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: Уверете се, че сте дезинфекцирали параметрите, предадени на условието за присъединяване.

person Harish Shetty    schedule 05.09.2010
comment
Е, това със сигурност работи (след коригиране на малка правописна грешка, трябва да бъде :joins => "JOIN names as...), но връща само обекти с име на Език 1 (с id2 прикрепено). Извличането на обектите за имена в език 2 изисква извикване на Name.find(name.id2) за всяко съвпадение, което причинява доста голям удар в производителността. Има ли начин да се заобиколи това? - person lambshaanxy; 06.09.2010
comment
Добре, това връща списък с всички съвпадения и на двата езика (след добавяне на AND names.language_id != n2.language_id за филтриране на самосъвпаденията), но това е много по-бавна заявка и връща единичен гигантски списък вместо списък с двойки -- все още трябва да използвам Name.find(name.id2), за да разберете съвпадащата двойка на име. - person lambshaanxy; 07.09.2010
comment
Колко реда се връщат в списъка? В идеалния случай това трябва да върне един ред (ако приемем, че нямате дублиращи се записи за един и същи ключ). Какви данни се опитвате да получите във втората си находка? Можете да актуализирате списъка за избор, за да добавите всички необходими полета от таблицата names. Актуализирах отговора. Може би този път ще се получи. - person Harish Shetty; 07.09.2010

Изглежда, че може да искате да използвате връзка много към много между език и име вместо has_many/belongs_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"] 

Това допълнително ниво на нормализиране трябва да улесни това, което се опитвате да направите.

person Dave Pirotte    schedule 04.09.2010
comment
Ох, интересно. Проблемът е, че напр. Финландското „Joni“ и еврейското „Yoni“ всъщност са различни имена с различни свойства (изписване в оригиналния скрипт и т.н.), които случайно имат едно и също нормализирано поле за име, а не само едно име. - person lambshaanxy; 06.09.2010