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 关系的经验。我的应用程序有photosterms 有一个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 关系中搜索“所有”匹配项而不是“任何”匹配项的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Rails 3 中将范围与 Ransack 一起使用?

在 Ransack 上为 Rails 设置默认搜索参数

Ransack,搜索多列,一个字段,rails 3

rails gem ransack 之模糊搜索

Rails Ransack gem:使用一个条件搜索多个值

在rails应用程序中使用Ransack搜索表单中的关联多对多关联