ruby-on-rails - 将 ActiveRecord habtm 查询转换为 Arel

标签 ruby-on-rails activerecord arel

我有一个很普通的habtm关系:

Photo has_and_belongs_to_many :tags
Tag has_and_belongs_to_many :photos

在我的照片模型中,我有一个“带标签”的方法,我用它来查找用一组给定的 tag_id 标记的照片。此查询只需要匹配具有所有给定标签的照片,但不考虑是否存在任何其他标签。这是我的方法:
def self.with_terms( array )
  select('distinct photos.*').joins(:tags).where('tags.id' => array).group("photos." + self.column_names.join(', photos.')).having("count(*) = #{array.size}")
end

这按预期工作。

现在,为了将它与我正在使用的其他一些库更好地集成,我需要在 Arel 中重新编写它。 (使它成为 Arel 节点?,不确定您通常称之为什么)。

我一直在试验这个,但老实说我以前从未尝试过使用 Arel,所以我有点迷茫。我一直在控制台中进行试验并尝试过:
t = Photo.arel_table
q = t.join(:tags).on(t[:tags_id].in(array))
Photo.where(q)

但是,(1) 我不认为 q首先是正确的查询,并且(2)它创建了一个 Arel::SelectManager ,当传递给 where 调用时会引发 Cannot visit Arel::SelectManager .所以,显然我做错了。

更新:为了更具体一点,我希望返回一个 Arel 节点,因为我正在使用一个 gem(ransack),它希望您将 Arel 节点传递给搜索方法。 Ransack 会将这个 Arel 节点与其他节点链接起来,以生成复杂的搜索查询。

Arel 大师能告诉我如何正确地做到这一点吗?

最佳答案

很难找到好的 Arel 文档,但是 @ Philip C 已经整理了一些有用的 slides ,在他对这个 question 的回答中引用了。

以下应该是您要查找的内容:

photos = Arel::Table.new(:photos)
tags = Arel::Table.new(:tags)
photo_tags = Arel::Table.new(:photo_tags)

q = photos[:id].in(
   photos.project(photos[:id])
  .join(photo_tags).on(photos[:id].eql(photo_tags[:photo_id]))
  .join(tags).on(photo_tags[:tag_id].eql(tags[:id]))
  .where(tags[:id].in(array))
  .group(photos.columns)
  .having(tags[:id].count.eq(array.length))
)

这会产生一个 Arel::Nodes::In 实例,您应该可以像在 Photo.where(q) 中一样直接使用它。

更新:

在查看文档和一些 Ransack 的源代码后,似乎没有任何自然的方法来定义涉及子查询的自定义谓词,这在您的情况下是必需的(因为谓词必须适合 where 子句)。解决此问题的一种方法可能是利用谓词使用的 :formatter,如下所示:
Ransack.configure do |config|
  config.add_predicate 'with_tag_ids',
                   :arel_predicate => 'in',
                   :formatter => proc {|tag_ids| tags_subquery(tag_ids) },
                   :validator => proc {|v| v.present?},
                   :compounds => true
end

您可以将 tags_subquery(tag_ids) 定义为一种生成 arel 节点的方法,但将 array 替换为 tag_ids 并在返回之前对其调用 .to_sql (格式化程序需要返回一个字符串,而不是一个节点)。

我还没有尝试过这个,所以如果它有效,我会很高兴!

关于ruby-on-rails - 将 ActiveRecord habtm 查询转换为 Arel,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13420773/

相关文章:

php - Codeigniter Active Record - 选择字符串作为列

ruby-on-rails - 使用 ActiveRecord 进行条件链接

ruby-on-rails - 模型中的虚拟柱

ruby-on-rails - ARel 按字符串长度选择行

ruby-on-rails - 组合两个 ActiveRecord::Relation 对象

ruby-on-rails - rspec 测试 Controller 后将我的参数从符号更改为字符串并破坏我的测试

ruby-on-rails - 路由错误 - 没有路由匹配 [GET] "/games"

javascript - Rails 4.2 - 链接以在 302 中使用 Javascript 结果创建操作

ruby-on-rails - yarn 安装总是失败

ruby-on-rails - Rails多态关联: Restrict allowed classes