2009-03-08 3 views
1

Я пытаюсь создать модель для проекта ruby ​​on rails, который строит отношения между разными словами. Подумайте об этом как о словаре, где «Связи» между двумя словами показывают, что они могут использоваться синонимом. Мой DB выглядит примерно так:Rails model with foreign_key и таблица ссылок

Words 
---- 
id 

Links 
----- 
id 
word1_id 
word2_id 

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

class Word < ActiveRecord::Base 
    has_many :synonyms, :class_name => 'Word', :foreign_key => 'word1_id' 
end 

ответ

4

В общем, если ваша ассоциация имеет суффиксы, такие как 1 и 2, он не настроен должным образом. Попробуйте это для модели Слово:

class Word < ActiveRecord::Base 
    has_many :links, :dependent => :destroy 
    has_many :synonyms, :through => :links 
end 

Link Модель:

class Link < ActiveRecord::Base 
    belongs_to :word 
    belongs_to :synonym, :class_name => 'Word' 

    # Creates the complementary link automatically - this means all synonymous 
    # relationships are represented in @word.synonyms 
    def after_save_on_create 
    if find_complement.nil? 
     Link.new(:word => synonym, :synonym => word).save 
    end 
    end 

    # Deletes the complementary link automatically. 
    def after_destroy 
    if complement = find_complement 
     complement.destroy 
    end 
    end 

    protected 

    def find_complement 
    Link.find(:first, :conditions => 
     ["word_id = ? and synonym_id = ?", synonym.id, word.id]) 
    end 
end 

Таблицы:

Words 
---- 
id 

Links 
----- 
id 
word_id 
synonym_id 
+0

Автоматическое создание дополнительной ссылки действительно полезно для поддержания правильности соединений. Хотя я согласен с тем, что не использовать стиль в качестве суффиксов, заменив их словами «word_id» и «synonymous_word_id» - это просто семантика, нет? Благодарим вас за подробный ответ. – sdfx

+0

Вы можете сохранить его как word_1 и word_2 в таблице ссылок, если хотите - я имел в виду, если у вас есть две ассоциации * с числовыми суффиксами, это обычно означает, что вы должны реорганизовать. Мой код выводит вас из двух наборов ассоциаций в один. –

+0

has_many: синонимы не будут работать, потому что в вашей модели ссылок вы не определяете какую-либо ассоциацию: синоним. Пожалуйста, также разбейте различные модели на разные разделы кода. –

2

Хм, это хитрый. Это связано с тем, что синонимы могут быть либо с идентификатором word1, либо с идентификатором word2, либо с обоими.

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

class Word < ActiveRecord::Base 
    has_many :links1, :class_name => 'Link', :foreign_key => 'word1_id' 
    has_many :synonyms1, :through => :links1, :source => :word 
    has_many :links2, :class_name => 'Link', :foreign_key => 'word2_id' 
    has_many :synonyms2, :through => :links2, :source => :word 
end 

Это должно сделать это, но теперь вы должны проверить два места получить все синонимы. Я бы добавил метод, который присоединился к ним, внутри класса Word.

def synonyms 
    return synonyms1 || synonyms2 
end 

Результаты, полученные вместе, объединяются в массивы и устраняют дубликаты между ними.

* Этот код не проверен.

+0

Спасибо, это действительно элегантное решение синонима-проблему. – sdfx

+0

Добро пожаловать :) – Tilendor

+0

Элегантность потеряна для меня ... Вы создаете две ссылки, когда вам нужен только один. Проверьте мой ответ. –

1

Я бы просмотрел его под другим углом; поскольку все слова являются синонимами, вы не должны продвигать ни одного из них, чтобы быть «лучшим». Попробуйте что-то вроде этого:

class Concept < ActiveRecord::Base 
    has_many :words 
end 

class Word < ActiveRecord::Base 
    belongs_to :concept 

    validates_presence_of :text 
    validates_uniqueness_of :text, :scope => :concept_id 

    # A sophisticated association would be better than this. 
    def synonyms 
    concept.words - [self] 
    end 
end 

Теперь вы можете сделать

word = Word.find_by_text("epiphany") 
word.synonyms 
+0

Хотя это может быть концептуальный самый чистый способ сделать это, у вас возникают проблемы, когда вы связываете существующие слова. Например. если wordA & B являются синонимами и wordC & wordD также, и теперь вы хотите подключить wordA & wordD. У вас есть два разных понятия, которые нужно объединить. – sdfx

+0

Если 'A == B',' C == D' и 'A == D', то' A == B == C == D'. Равенство транзитивно, поэтому все они должны иметь одну концепцию. Это идеализированный пример, поскольку слова могут иметь более одного значения, но это просто означает, что вам нужно отношение «многие ко многим» между словами и понятиями. –

+0

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

2

Слово модели:

class Word < ActiveRecord::Base 
    has_many :links, :dependent => :destroy 
    has_many :synonyms, :through => :links 

    def link_to(word) 
    synonyms << word 
    word.synonyms << self 
    end 
end 

Установка :dependent => :destroy на has_many :links удалит все ссылки, связанные с этим словом, до destroy записи слова.

Link Модель:

class Link < ActiveRecord::Base 
    belongs_to :word 
    belongs_to :synonym, :class_name => "Word" 
end 

Предполагая, что вы используете последние Rails, вы не должны будете указать внешний ключ для belongs_to :synonym. Если я правильно помню, это было введено как стандарт в Rails 2.

Таблица слов:

name 

Ссылка стол:

word_id 
synonym_id 

Чтобы связать существующее слово как синоним другого слова:

word = Word.find_by_name("feline") 
word.link_to(Word.find_by_name("cat")) 

Чтобы создать новое слово в виде синоним к другому слову:

word = Word.find_by_name("canine") 
word.link_to(Word.create(:name => "dog")) 
+0

Я думаю, что ваша проблема есть одна проблема: слово = Word.find_by_name («кошачье») word.synonyms << Word.find_by_name («кошка») Делает кошки синонимом кошачьих, но не наоборот. Вам нужно либо проверить оба столбца в таблице ссылок, как и мой, либо создать комплимент, подобный Саре. – Tilendor

+0

@Tilendor видел это тоже. Если вы добавите создание комплимента, это решение должно быть точно таким же, как исправленная версия Sarah – sdfx

+0

Теперь, чтобы связать эти два слова, используйте метод link_to, определенный в слове model. –

0

Попытка реализовать решение Сары я наткнулся на 2 вопроса:

Во-первых, решение не работает, когда требуется назначить синонимы, делая

word.synonyms << s1 or word.synonyms = [s1,s2] 

Также удаление синонимов косвенно не работает должным образом , Это связано с тем, что Rails не запускает обратные вызовы after_save_on_create и after_destroy, когда он автоматически создает или удаляет записи Link. По крайней мере, не в Rails 2.3.5, где я его пробовал.

Это может быть исправлено с помощью: after_add и: after_remove обратных вызовов в модели Word:

has_many :synonyms, :through => :links, 
        :after_add => :after_add_synonym, 
        :after_remove => :after_remove_synonym 

Где обратные вызовы методы Сары, слегка скорректированные:

def after_add_synonym synonym 
    if find_synonym_complement(synonym).nil? 
    Link.new(:word => synonym, :synonym => self).save 
    end 
end 

def after_remove_synonym synonym 
    if complement = find_synonym_complement(synonym) 
    complement.destroy 
    end 
end 

protected 

def find_synonym_complement synonym 
    Link.find(:first, :conditions => ["word_id = ? and synonym_id = ?", synonym.id, self.id]) 
end 

Второй выпуск Сары решение состоит в том, что синонимы, которые другие слова уже имеют при соединении с новым словом, не добавляются к новому слову и наоборот. Вот небольшая модификация, которая устраняет эту проблему и гарантирует, что все синонимы группы всегда связаны со всеми другими синонимами в этой группе:

def after_add_synonym synonym 
    for other_synonym in self.synonyms 
    synonym.synonyms << other_synonym if other_synonym != synonym and !synonym.synonyms.include?(other_synonym) 
    end 
    if find_synonym_complement(synonym).nil? 
    Link.new(:word => synonym, :synonym => self).save 
    end 
end 
Смежные вопросы