在我的Rails 4 / Postgres 9.1应用程序中,用户可以标记短语。通过连接表 phrase_tags 与标记表关联的词组表格,并在相应的模型中通过 has_many:通过关联。
我希望用户能够搜索具有所有他们指定为输入的标签的短语。例如,找到用"对象"标记的短语。和"颜色"。
我有一个看起来像这样的工作解决方案(在phrase.rb中)。 tags_array是一个字符串数组:
scope :tagged_with_all, -> (tags_array) {
joins(:tags)
.where("tags.name in (?)", tags_array)
.group("phrases.id").having("count(*) = ?", tags_array.count)
}
ActiveRecord转换为:
SELECT "phrases".* FROM "phrases"
INNER JOIN "phrase_tags" ON "phrase_tags"."phrase_id" = "phrases"."id"
INNER JOIN "tags" ON "tags"."id" = "phrase_tags"."tag_id"
WHERE (tags.name in ('objects','colors'))
GROUP BY phrases.id HAVING count(*) = 1
这很好用,GROUP BY / HAVING
只选择那些有多个关联标签等于tags_array长度的短语。
问题在于:当我将此范围与其他具有另一个JOIN的内容组合时,Postgres会抱怨该连接表的主键必须包含在GROUP BY子句中或聚合(参见下面的示例)。有没有其他方法可以使这个工作(可能使用递归子选择?)不使用GROUP BY?
示例错误:在这里,我想获取所有标记的短语,并使用给定语言的翻译加入:
Phrase.joins("LEFT JOIN translations t on t.phrase_id = phrases.id and
t.language_id = #{lang_id}")
.select("phrases.*, t.id AS translation_id, t.native_text as
translated_text")
.tagged_with_all(tag_items)
生成的SQL:
SELECT phrases.*, t.id AS translation_id, t.native_text as translated_text
FROM "phrases" INNER JOIN "phrase_tags"
ON "phrase_tags"."phrase_id" = "phrases"."id"
INNER JOIN "tags" ON "tags"."id" = "phrase_tags"."tag_id"
LEFT JOIN translations t on t.phrase_id = phrases.id and t.language_id = 11
WHERE (tags.name in ('objects','colors'))
GROUP BY phrases.id HAVING count(*) = 2
例外:
ActiveRecord::StatementInvalid: PG::GroupingError: ERROR: column "t.id"
must appear in the GROUP BY clause or be used in an aggregate function