質問

私は、異なる単語間の関係を構築するruby on railsプロジェクトのモデルを作成しようとしています。これは、「リンク」が存在する辞書と考えてください。 2つの単語の間にある同義語として使用できることを示しています。私のDBは次のようになります:

Words
----
id

Links
-----
id
word1_id
word2_id

リンクテーブルを使用して、2つの単語間の関係を作成するにはどうすればよいですか。モデルを作成しようとしましたが、リンクテーブルを再生する方法がわかりませんでした:

class Word < ActiveRecord::Base
  has_many :synonyms, :class_name => 'Word', :foreign_key => 'word1_id'
end
役に立ちましたか?

解決

通常、関連付けに1や2などのサフィックスがある場合、適切に設定されていません。 Wordモデルでこれを試してください:

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 idまたはword2 idのいずれか、または両方に由来する可能性があるためです。

とにかく、リンクテーブルにモデルを使用する場合、リンクテーブルを使用するモデルで: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

それでうまくいきますが、今では2つの場所をチェックしてすべての同義語を取得する必要があります。これらを結合するメソッドをクラスWord内に追加します。

def synonyms
  return synonyms1 || synonyms2
end

||結果を結合すると、配列が結合され、配列間の重複が排除されます。

*このコードはテストされていません。

Wordモデル:

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 =&gt; has_many:links の:destroy は、単語レコードを destroy する前に、その単語に関連付けられているすべてのリンクを削除します。

リンクモデル:

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

最新のRailsを使用している場合、 belongs_to:synonym に外部キーを指定する必要はありません。正しく思い出せば、これはRails 2で標準として導入されました。

Wordテーブル:

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

サラのソリューションを実装しようとすると、2つの問題に遭遇しました:

まず、次の方法で類義語を割り当てたい場合、ソリューションは機能しません

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

また、シノニムを間接的に削除することも正しく機能しません。これは、Railsが自動的にLinkレコードを作成または削除するときにafter_save_on_createおよびafter_destroyコールバックをトリガーしないためです。少なくとも私が試してみたRails 2.3.5ではそうではありません。

これは、Wordモデルで:after_addおよび:after_removeコールバックを使用して修正できます。

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

コールバックがSarahのメソッドである場合、わずかに調整します:

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

サラのソリューションの2番目の問題は、新しい単語とリンクされたときに他の単語がすでに持っている同義語が新しい単語に追加されず、その逆も同じであるということです。 この問題を修正し、グループのすべての同義語がそのグループ内の他のすべての同義語に常にリンクされるようにする小さな変更を次に示します。

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