Rails自引用关联连接条件

时间:2015-11-01 22:19:41

标签: ruby-on-rails rails-activerecord

我有以下数据库架构和模型,它们构成了一个自引用关联,其中Phrase通过Phrase关联有许多其他Translation的翻译:

模式

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

现在,我想基于语言和目标语言运行查询。例如,我想找到英语短语及其各自的西班牙语翻译。目前我正在查询基于 source 语言的短语,但后来我必须使用目标语言上的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(正确地)认为source_phrase子句中的destination_phrasewhere是表名。所以表名仍然必须是phrases,但是当它不能指定两个连接的连接条件时,只是第一个。

如何在自引用关联上指定2个单独的连接条件,这两个条件都访问同一模型上的相同属性(language模型上的Phrase)?

1 个答案:

答案 0 :(得分:3)

我的KeyMapping模型被证明是错误定义的,这是第一个问题。我已将其修改为:

<强>短语

return entityMaps.SelectMany(entityMap =>
entityMap.Value
    .Where(entityInfo => entityInfo.EntityState == EntityState.Added && (entityInfo.AutoGeneratedKey != null && entityInfo.AutoGeneratedKey.AutoGeneratedKeyType == AutoGeneratedKeyType.Identity))
    .Select(entityInfo => new KeyMapping
    {
        EntityTypeName = entityInfo.Entity.GetType().FullName,
        RealValue = entityInfo.AutoGeneratedKey.Property.GetValue(entityInfo.Entity),
        TempValue = entityInfo.AutoGeneratedKey.TempValue
    }));

翻译保持不变:

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}) ed关联(在本例中为Translation .joins(:source_phrase => :source_phrases, :destination_phrase => :destination_phrases) .where(source_phrases_phrases: {language: 0}, destination_phrases_phrases: {language: 2}) .distinct join中的source_phrases),然后可以将其用作不同的表名称。 destination_phrases条款。

我应该注意,我在Phrase子句中使用wheresource_phrases_phrases,因为Rails似乎希望将表名(destination_phrases_phrases)附加到关联( wherephrases)。它会产生一些难看的查询,但也许我可以更好地在source_phrases模型中命名我的关联......

似乎从destination_phrases输出(这里太长了),Phrase版本的速度提高了约25%,包含6,000个短语和12,000个翻译。随着这些表的增长越大,它也会以指数方式变得越来越快。

经验教训?自我指涉协会是一种痛苦。我当然希望这能帮助将来的某个人。