Question

J'essaie de créer un modèle pour un projet Ruby on rails qui établit des relations entre différents mots. Considérez-le comme un dictionnaire où le lien " Liens " entre deux mots montre qu'ils peuvent être utilisés comme synonymes. Ma base de données ressemble à ceci:

Words
----
id

Links
-----
id
word1_id
word2_id

Comment créer une relation entre deux mots en utilisant la table des liens. J'ai essayé de créer le modèle mais je ne savais pas comment faire jouer la table des liens:

class Word < ActiveRecord::Base
  has_many :synonyms, :class_name => 'Word', :foreign_key => 'word1_id'
end
Était-ce utile?

La solution

En général, si votre association a des suffixes tels que 1 et 2, elle n'est pas configurée correctement. Essayez ceci pour le modèle Word:

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

Modèle de lien:

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

Tableaux:

Words
----
id

Links
-----
id
word_id
synonym_id

Autres conseils

Hmm, c'est une question délicate. En effet, les synonymes peuvent provenir soit de l’id word1, soit de l’id word2, ou des deux.

Quoi qu'il en soit, lorsque vous utilisez un modèle pour la table de liens, vous devez utiliser l'option: through des modèles utilisant la table de liens

.
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

Cela devrait le faire, mais maintenant vous devez vérifier deux endroits pour obtenir tous les synonymes. Je voudrais ajouter une méthode qui a rejoint ces derniers, à l'intérieur de la classe Word.

def synonyms
  return synonyms1 || synonyms2
end

|| Les résultats combinés réuniront les tableaux et élimineront les doublons.

* Ce code n'a pas été testé.

Modèle 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

Définition de : depend = = gt; : destroy sur le has_many: links supprimera tous les liens associés à ce mot avant que détruise le mot record.

Modèle de lien:

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

En supposant que vous utilisez les derniers Rails, vous n'avez pas besoin de spécifier la clé étrangère pour le appartient_à: synonyme . Si je me souviens bien, cela a été introduit en tant que norme dans Rails 2.

Tableau de mots:

name

Table de liens:

word_id
synonym_id

Pour lier un mot existant en tant que synonyme à un autre mot:

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

Pour créer un nouveau mot comme synonyme d'un autre mot:

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

Je le regarderais sous un angle différent; étant donné que tous les mots sont synonymes, vous ne devez en aucun cas promouvoir le mot "meilleur". Essayez quelque chose comme ça:

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

Maintenant, vous pouvez faire

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

En essayant de mettre en œuvre la solution de Sarah, je suis tombé sur deux problèmes:

Premièrement, la solution ne fonctionne pas si vous souhaitez attribuer des synonymes en procédant de la sorte

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

De même, supprimer des synonymes indirectement ne fonctionne pas correctement. En effet, Rails ne déclenche pas les rappels after_save_on_create et after_destroy lorsqu'il crée ou supprime automatiquement les enregistrements Link. Du moins pas dans Rails 2.3.5 où je l’ai essayé.

Ceci peut être corrigé en utilisant: after_add et: after_remove les rappels dans le modèle Word:

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

Où les rappels sont les méthodes de Sarah, légèrement ajustées:

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

Le deuxième problème de la solution de Sarah est que les synonymes que d’autres mots ont déjà lorsqu'ils sont liés à un nouveau mot ne sont pas ajoutés au nouveau mot et inversement. Voici une petite modification qui résout ce problème et garantit que tous les synonymes d'un groupe sont toujours liés à tous les autres synonymes de ce groupe:

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 
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top