我有以下数据库架构和模型,它们构成了一个自引用关联,其中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_phrase
和where
是表名。所以表名仍然必须是phrases
,但是当它不能指定两个连接的连接条件时,只是第一个。
如何在自引用关联上指定2个单独的连接条件,这两个条件都访问同一模型上的相同属性(language
模型上的Phrase
)?
答案 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
子句中使用where
和source_phrases_phrases
,因为Rails似乎希望将表名(destination_phrases_phrases
)附加到关联( where
和phrases
)。它会产生一些难看的查询,但也许我可以更好地在source_phrases
模型中命名我的关联......
似乎从destination_phrases
输出(这里太长了),Phrase
版本的速度提高了约25%,包含6,000个短语和12,000个翻译。随着这些表的增长越大,它也会以指数方式变得越来越快。
经验教训?自我指涉协会是一种痛苦。我当然希望这能帮助将来的某个人。