Rails,Ransack:如何在 HABTM 关系中搜索“所有”匹配项而不是“任何”匹配项
Posted
技术标签:
【中文标题】Rails,Ransack:如何在 HABTM 关系中搜索“所有”匹配项而不是“任何”匹配项【英文标题】:Rails, Ransack: How to search HABTM relationship for "all" matches instead of "any" 【发布时间】:2012-11-04 18:25:06 【问题描述】:我想知道是否有人有使用 Ransack 与 HABTM 关系的经验。我的应用程序有photos
与terms
有一个habtm 关系(术语就像标签)。以下是对我遇到的情况的简化说明:
我有两张照片:照片 1 和照片 2。它们具有以下术语:
照片 1:A、B、C
照片 2:A、B、D
我构建了一个搜索表单,并在搜索表单中为所有术语添加了复选框,如下所示:
- terms.each do |t|
= check_box_tag 'q[terms_id_in][]', t.id
如果我使用:q[terms_id_in][]
并检查“A,C”,我的结果是照片 1 和照片 2。我只想要照片 1,因为我要求 A 和 C,在这个查询中我不关心B 或 D,但我希望 A 和 C 同时出现在给定的结果中。
如果我使用q[terms_id_in_all][]
,我的结果是 nil,因为两张照片都只包含 A 和 C。或者,也许因为每次连接只有一个术语,所以没有连接匹配 A 和 C。无论如何,我只想要照片1个被退回。
如果我使用任何种类的q[terms_id_eq][]
,我永远不会得到任何结果,所以我认为这在这种情况下不起作用。
那么,给定一个 habtm 连接,如何搜索与给定值匹配的模型,同时忽略未给定的值?
或者,对于任何不熟悉 Ransack 的 rails/sql 专家,您还可以如何创建一个搜索表单,就像我为具有 habtm 连接的模型所描述的那样?
更新:根据the answer to related question,我现在已经构建了一个正确匹配的 Arel 查询。不知何故,您应该能够将 Arel 节点用作洗劫者,或者如 cdesrosiers 指出的那样,用作自定义谓词,但到目前为止,我还没有做到这一点。
根据这个答案,我设置了以下 ransack 初始化程序:
Ransack.configure do |config|
config.add_predicate 'has_terms',
:arel_predicate => 'in',
:formatter => proc |term_ids| Photo.terms_subquery(term_ids),
:validator => proc |v| v.present?,
:compounds => true
end
...然后在Photo上设置如下方法:
def self.terms_subquery(term_ids)
photos = Arel::Table.new(:photos)
terms = Arel::Table.new(:terms)
photos_terms = Arel::Table.new(:photos_terms)
photos[:id].in(
photos.project(photos[:id])
.join(photos_terms).on(photos[:id].eq(photos_terms[:photo_id]))
.join(terms).on(photos_terms[:term_id].eq(terms[:id]))
.where(terms[:id].in(term_ids))
.group(photos.columns)
.having(terms[:id].count.eq(term_ids.length))
).to_sql
end
不幸的是,这似乎不起作用。虽然terms_subquery
产生正确的SQL,但Photo.search(:has_terms => [2,5]).result.to_sql
的结果只是"SELECT \"photos\".* FROM \"photos\" "
【问题讨论】:
(ransack 真是太棒了!!)我没有尝试过这个,它可能不适用于你的情况,所以我不会把它作为答案,但 ransack 会解析_or_
之间字段,例如 q.user_name_or_user_email_cont
允许您对不同的字段进行部分匹配。复选框可能并不那么容易,但也许它会引导您找到解决方案...
@tharrison 谢谢,我会调查一下。
@Andrew 你解决了这个问题吗?正如许多地方所说,包括 ransack repo - ransack 摆脱了使用范围,并且解决方案似乎将范围包裹在自定义谓词中。你搞定了吗?
@prikha 不,没有运气。请参阅下面答案中的 cmets,它们到今天仍然有效。
【参考方案1】:
使用在我的answer 中定义的自定义搜查谓词到您的相关问题,这应该与您的标记的简单更改一起工作:
- terms.each do |t|
= check_box_tag 'q[id_has_terms][]', t.id
更新
:formatter
并没有像我想的那样做,并且看到 Ransack 回购没有提到“子查询”,你可能无法将它用于你想做的事情,之后全部。所有可用的选项似乎都用尽了,所以除了猴子补丁之外别无他法。
为什么不像通常使用活动记录(或者甚至使用您现在拥有的 Arel 查询)那样跳过搜索并查询“照片”表?您已经知道查询有效。您希望从使用 Ransack 中获得什么特定的好处?
【讨论】:
如相关问题所述,自定义谓词运行时不会引发错误,但由于某种原因不会导致正确匹配。老实说,我不明白 ransack 是如何工作的,而且没有错误消息,我不知道这种情况下的问题是什么。 您可以检查日志并发布执行搜索时实际执行的查询吗? 大声笑,它似乎根本没有抓住查询。如果我运行Photo.search(:has_terms=>[2,5]).result.to_sql
,则值为:"SELECT \"photos\".* FROM \"photos\" "
注意,我会将其余设置添加到问题中。
嗯,从 ransack 的explanation of predicates 来看,似乎给search
的选项哈希必须是:column_name_predicate
的形式才能构建SQL WHERE
子句。因此,既然您正在搜索 id
列,并且您已将谓词称为 has_terms
,请尝试使用 Photo.search(:id_has_terms => [2,5])
。
另外,我认为您需要从 arel 查询中删除包装器 photos[:id].in()
,只留下 photos.project(photos[:id])...
,因为该部分查询似乎已经在自定义谓词定义中编码.以上是关于Rails,Ransack:如何在 HABTM 关系中搜索“所有”匹配项而不是“任何”匹配项的主要内容,如果未能解决你的问题,请参考以下文章