Вопрос

Я пытаюсь создать модель для проекта ruby on rails, которая строит отношения между разными словами.Думайте об этом как о словаре, где "Связи" между двумя словами показывают, что они могут использоваться как синонимы.Моя база данных выглядит примерно так:

Words
----
id

Links
-----
id
word1_id
word2_id

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

class Word < ActiveRecord::Base
  has_many :synonyms, :class_name => 'Word', :foreign_key => 'word1_id'
end
Это было полезно?

Решение

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

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

Модель связи:

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

Другие советы

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

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

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

|| объединение результатов воедино объединит массивы и устранит дубликаты между ними.

* Этот код не тестировался.

Модель слова:

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вводя слово "запись".

Модель связи:

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"))

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

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

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

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

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

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

Это можно исправить, используя обратные вызовы:after_add и:after_remove в Word model:

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 
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top