2009-03-08 5 views
1

Ich versuche, ein Modell für ein Ruby on Rails-Projekt zu erstellen, das Beziehungen zwischen verschiedenen Wörtern erstellt. Betrachten Sie es als ein Wörterbuch, in dem die "Links" zwischen zwei Wörtern zeigen, dass sie synonym verwendet werden können. Meine DB sieht ungefähr so ​​aus:Rails-Modell mit Foreign_key und Verknüpfungstabelle

Words 
---- 
id 

Links 
----- 
id 
word1_id 
word2_id 

Wie erstelle ich eine Beziehung zwischen zwei Wörtern, die Verbindungstabelle verwendend. Ich habe versucht, das Modell zu erstellen, war aber nicht sicher, wie die Link-Tabelle ins Spiel zu bekommen:

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

Antwort

4

Im Allgemeinen tun, wenn Ihr Verein Suffixe wie 1 und 2 hat, ist es nicht richtig eingerichtet. Versuchen Sie, diese für das Modell Wort:

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

Link-Modell:

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 

Tables:

Words 
---- 
id 

Links 
----- 
id 
word_id 
synonym_id 
+0

Die automatische Erstellung der komplementären Verbindung ist sehr nützlich, um die Verbindungen gültig zu halten. Obwohl ich zustimme, dass es kein guter Stil ist, Zahlen als Suffixe zu verwenden, ersetzen sie sie durch "word_id" & "synyn_word_id" nur durch Semantik, nein? Danke für Ihre gründliche Antwort. – sdfx

+0

Sie können es als Word_1 und Word_2 in der Links-Tabelle behalten, wenn Sie wollen - was ich meinte, wenn Sie zwei * Assoziationen * mit numerischen Suffixen haben, bedeutet es normalerweise, dass Sie refactorieren sollten. Mein Code führt Sie von zwei Gruppen von Assoziationen zu einer. –

+0

has_many: Synonyme funktionieren nicht, da Sie in Ihrem Link-Modell keine Assoziation definieren, die als Synonym bezeichnet wird. Bitte zerlegen Sie auch verschiedene Modelle in verschiedene Codeabschnitte. –

2

Hmm, das ist eine schwierige Sache. Das liegt daran, dass Synonyme entweder von der ID word1 oder der ID word2 oder beiden stammen können.

Wie auch immer, wenn ein Modell für die Verknüpfungstabelle verwenden, müssen Sie die verwenden: durch Option auf die Modelle, die die Link-Tabelle

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 

verwenden, die es tun sollte, aber jetzt müssen Sie zwei Orte überprüfen bekomme alle Synonyme. Ich würde hinzufügen, eine Methode, die diese, innerhalb der Klasse Wort verbunden.

|| Wenn die Ergebnisse zusammengefügt werden, werden die Arrays verbunden und Duplikate zwischen ihnen eliminiert.

* Dieser Code wurde nicht getestet.

+0

Danke, das ist wirklich eine elegante Lösung für das Synonym-Problem. – sdfx

+0

Sie sind herzlich willkommen :) – Tilendor

+0

Die Eleganz ist auf mich verloren ... Sie erstellen zwei Links, wenn Sie nur eine benötigen. Überprüfe meine Antwort. –

1

Ich würde es aus einem anderen Blickwinkel betrachten; Da alle Wörter synonym sind, solltest du keinen von ihnen zum "Besten" machen. Probieren Sie etwas wie folgt aus:

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 

Jetzt können Sie

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

Während dies die konzeptionell sauberste Art ist, dies zu tun, stoßen Sie auf Probleme, wenn Sie vorhandene Wörter verbinden. Z.B. wenn wordA & B synonym sind und auch wordC & wordD, und jetzt willst du wordA & wordD verbinden. Sie haben zwei verschiedene "Konzepte", die Sie kombinieren müssen. – sdfx

+0

Wenn A == B, C == D und A = D, dann ist A == B == C == D. Gleichheit ist transitiv, also sollten sie alle ein gemeinsames Konzept haben. Dies ist ein idealisiertes Beispiel, da Wörter mehr als eine Bedeutung haben können, aber das bedeutet nur, dass Sie eine Viele-zu-Viele-Beziehung zwischen Wörtern und Konzepten benötigen. –

+0

Ich vermute, dass der wechselseitige Link-Ansatz für die meisten Situationen besser ist, aber dies ist ein interessanter Ansatz, danke für die Veröffentlichung. –

2

Wortmodell:

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 

Einstellung :dependent => :destroy auf der has_many :links löscht alle Links, die mit diesem Wort verknüpft sind, bevor der Wortsatz eingegeben wird.

Link-Modell:

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

Vorausgesetzt, dass Sie die neuesten Rails verwenden, sollten Sie die Fremdschlüssel für die belongs_to :synonym angeben. Wenn ich mich richtig erinnere, wurde dies als Standard in Rails 2 eingeführt.

Word-Tabelle:

name 

Link-Tabelle:

word_id 
synonym_id 

ein bestehendes Wort als Synonym für ein anderes Wort, um eine Verknüpfung:

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

Um ein neues Wort als zu erstellen Synonym zu einem anderen Wort:

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

Ich denke, dass Ihr Problem ein Problem hat: Wort = Word.find_by_name ("Katze") word.synonyms << Wort.find_by_name ("Katze") Macht Katze ein Synonym für Katzen, aber nicht umgekehrt. Sie müssen entweder beide Spalten in der Linktabelle überprüfen, wie meine, oder ein Kompliment wie Sarah erstellen. – Tilendor

+0

@Tilendor sah das auch. Wenn Sie die Erstellung des Komplements hinzufügen, sollte diese Lösung genauso wie die korrigierte Version von Sarah sein. – sdfx

+0

Um die beiden Wörter miteinander zu verknüpfen, verwenden Sie die link_to-Methode, die im Wortmodell definiert ist. –

0

Der Versuch, Sarah-Lösung stieß ich auf zwei Fragen zu implementieren:

Zum einen die Lösung nicht arbeiten zu wollen, wenn Synonyme zuweisen von

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

Auch das Löschen Synonyme indirekt nicht richtig funktioniert . Dies liegt daran, dass Rails die Rückrufe nach_save_on_create und after_destroy nicht auslöst, wenn die Link-Datensätze automatisch erstellt oder gelöscht werden. Zumindest nicht in Rails 2.3.5, wo ich es ausprobiert habe.

Dies kann durch Verwendung festgelegt werden: after_add und: after_remove Rückrufe im Modell Wort:

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

Wo die Rückrufe Sarah Methoden sind leicht angepasst:

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 

Die zweite Ausgabe von Sarah Lösung ist, dass Synonyme, die andere Wörter bereits haben, wenn sie mit einem neuen Wort verbunden sind, nicht zu dem neuen Wort hinzugefügt werden und umgekehrt. Hier ist eine kleine Änderung, die dieses Problem behebt und stellt sicher, dass alle Synonyme einer Gruppe werden immer auf alle anderen Synonyme in dieser Gruppe verknüpft:

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