2015-11-01 54 views
2

我有以下的數據庫架構和模型,構成了自我指涉的關聯,其中一個Phrase具有其他Phrase S的許多翻譯,通過Translation協會:Rails的自我指涉協會加盟條件

模式

ActiveRecord::Schema.define(version: 20151003213732) do 

    enable_extension "plpgsql" 

    create_table "phrases", force: :cascade do |t| 
    t.integer "language" 
    t.string "text" 
    end 

    create_table "translations", force: :cascade do |t| 
    t.integer "source_id" 
    t.integer "destination_id" 
    end 
end 

短語

class Phrase < ActiveRecord::Base 

    has_many :translations, 
       class_name: "Translation", 
       foreign_key: "source_id" 
    has_many :destination_phrases, 
       through: :translations 
    has_many :inverse_translations, 
       class_name: "Translation", 
       foreign_key: "destination_id" 
    has_many :source_phrases, 
       through: :inverse_translations 

    enum language: [:eng, :spa, ...] 
end 

翻譯

class Translation < ActiveRecord::Base 
    belongs_to :source_phrase, 
       class_name: "Phrase", 
       foreign_key: "source_id" 
    belongs_to :destination_phrase, 
       class_name: "Phrase", 
       foreign_key: "destination_id" 
end 

現在,我想基於語言和目的地語言運行查詢。例如,我想查找英文短語及其各自的西班牙語翻譯。目前,我正在根據來源查詢短語,但之後我必須使用目標語言的select方法過濾出結果。我有如下所示:

@translations = Translation.includes(:source_phrase, :destination_phrase) 
          .where(phrases: {language: @source_language}) 

# Select only destination phrases where the language matches 
@translations = @translations.select { 
    |t| t.destination_phrase.language == @destination_language 
} 

我想消除select調用,因爲這絕對應該在ActiveRecord的可能。 select將被替換爲模型的where查詢中的其他參數,但我無法弄清楚如何指定它。

它應該是這個樣子:

@translations = 
    Translation.includes(:source_phrase, :destination_phrase) 
       .where([source_phrase: {language: @source_language}, 
         destination_phrase: {language: @destination_language}]) 

然而,ActiveRecord的認爲(理所當然),該where子句中source_phrasedestination_phrase是表名。所以表名仍然是phrases,但是當我不能指定的連接條件時,兩個連接,只是第一個。

如何在自我參照關聯上指定兩個單獨的聯接條件,它們都訪問同一模型上的相同屬性(Phrase模型上的language)?

+0

是否可以查詢短語而不是翻譯? (短語:「目的地短語」:{語言:「西班牙語」}})' –

+0

@LannyBose的確我可以查詢短語模型,但不幸的是不是那種我正在尋找的反應 - 我最終需要的模型是一個「翻譯」。在玩了大約兩個小時後,並使用類似於您所建議的「加入」代碼的東西后,我終於提出瞭解決方案。這也表明我的'短語'模型關聯不正確(不奇怪)。我自我回答,如果你想看看! –

回答

3

我的Phrase模型原來定義不正確,這是第一個問題。我訂正,像這樣:

短語

class Phrase < ActiveRecord::Base 

    has_many :translations, 
       class_name: "Translation", 
       foreign_key: "source_id" 
    has_many :source_phrases,   # this changed 
       through: :translations 
    has_many :inverse_translations, 
       class_name: "Translation", 
       foreign_key: "destination_id" 
    has_many :destination_phrases,  # this changed 
       through: :inverse_translations 

    enum language: [:eng, :spa, ...] 
end 

翻譯一直未變:

class Translation < ActiveRecord::Base 
    belongs_to :source_phrase, 
       class_name: "Phrase", 
       foreign_key: "source_id" 
    belongs_to :destination_phrase, 
       class_name: "Phrase", 
       foreign_key: "destination_id" 
end 

現在我能執行下述查詢來得到正確的結果:

Translation 
    .includes(:source_phrase => :source_phrases, :destination_phrase => :destination_phrases) 
    .where(source_phrases_phrases: {language: 0}, destination_phrases_phrases: {language: 2}) 

Translation 
    .joins(:source_phrase => :source_phrases, :destination_phrase => :destination_phrases) 
    .where(source_phrases_phrases: {language: 0}, destination_phrases_phrases: {language: 2}) 
    .distinct 

答案是在指定(在本例中Phrasesource_phrasesdestination_phrases),其然後可以被用作where子句中不同的表名join編關聯。

我要指出,我用source_phrases_phrasesdestination_phrases_phraseswhere子句中由於Rails似乎期望表名(phrases)被附加到協會(source_phrasesdestination_phrases)。它使一些醜陋的查詢,但也許我能說出我在Phrase模型更好地組織...

它也似乎從EXPLAIN輸出(太長的方式放在這裏),該版本joins約25使用6,000個短語和12,000個翻譯,速度更快。這些表越大,它的指數級越快。

經驗教訓?自我指涉性協會是一種痛苦。我當然希望這能幫助未來的人。