Pregunta

Estoy tratando de crear un modelo para un proyecto de ruby ??on rails que construye relaciones entre diferentes palabras. Piense en ello como un diccionario en el que " Enlaces " entre dos palabras muestra que se pueden usar como sinónimos. Mi DB se ve algo como esto:

Words
----
id

Links
-----
id
word1_id
word2_id

¿Cómo creo una relación entre dos palabras, usando la tabla de enlaces? He intentado crear el modelo pero no estaba seguro de cómo poner en funcionamiento la tabla de enlaces:

class Word < ActiveRecord::Base
  has_many :synonyms, :class_name => 'Word', :foreign_key => 'word1_id'
end
¿Fue útil?

Solución

En general, si su asociación tiene sufijos como 1 y 2, no está configurada correctamente. Prueba esto para el modelo de Word:

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

Modelo de enlace:

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

Tablas:

Words
----
id

Links
-----
id
word_id
synonym_id

Otros consejos

Hmm, esta es una pregunta difícil. Esto se debe a que los sinónimos pueden ser del ID de word1 o del ID de word2 o ambos.

De todos modos, cuando use un modelo para la tabla de enlaces, debe usar la opción: through en los modelos que usan la tabla de enlaces

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

Eso debería hacerlo, pero ahora debes marcar dos lugares para obtener todos los sinónimos. Yo agregaría un método que se uniera a estos, dentro de la clase Word.

def synonyms
  return synonyms1 || synonyms2
end

|| juntando los resultados se unirán a las matrices y eliminarán los duplicados entre ellos.

* Este código no está probado.

Modelo de palabra:

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

Configuración : dependiente = > : destroy en el has_many: links eliminará todos los enlaces asociados con esa palabra antes de que destroy ingrese el registro de la palabra.

Modelo de enlace:

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

Suponiendo que está utilizando los últimos Rails, no tendrá que especificar la clave foránea para el belongs_to: synonym . Si recuerdo correctamente, esto se introdujo como estándar en Rails 2.

Tabla de palabras:

name

Tabla de enlaces:

word_id
synonym_id

Para vincular una palabra existente como sinónimo de otra palabra:

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

Para crear una nueva palabra como sinónimo de otra palabra:

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

Lo vería desde un ángulo diferente; ya que todas las palabras son sinónimas, no debes promocionar ninguna de ellas para que sea la "mejor". Intenta algo como esto:

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

Ahora puedes hacerlo

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

Tratando de implementar la solución de Sarah, encontré 2 problemas:

En primer lugar, la solución no funciona cuando se desean asignar sinónimos haciendo

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

También eliminar sinónimos indirectamente no funciona correctamente. Esto se debe a que Rails no activa las devoluciones de llamada after_save_on_create y after_destroy cuando crea o elimina automáticamente los registros de enlace. Al menos no en Rails 2.3.5 donde lo probé.

Esto se puede solucionar usando: after_add y: after_remove callbacks en el modelo de Word:

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

Donde las devoluciones de llamada son los métodos de Sarah, ligeramente ajustados:

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

El segundo problema de la solución de Sarah es que los sinónimos que otras palabras ya tienen cuando están vinculados con una nueva palabra no se agregan a la nueva palabra y viceversa. Aquí hay una pequeña modificación que soluciona este problema y garantiza que todos los sinónimos de un grupo estén siempre vinculados a todos los demás sinónimos de ese grupo:

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 
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top