Convert ActiveRecord habtm query to Arel

前端 未结 1 1249
孤街浪徒
孤街浪徒 2020-12-10 17:35

I have a pretty common habtm relationship:

Photo has_and_belongs_to_many :tags
Tag has_and_belongs_to_many :photos

In my Photo

相关标签:
1条回答
  • 2020-12-10 17:53

    It's hard to find good Arel documentation, but @Philip C has put together some useful slides, referenced in his answer to this question.

    The following should be what you're looking for:

    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))
    )
    

    This results in an Arel::Nodes::In instance that you should be able to use directly as in Photo.where(q).


    UPDATE:

    After looking through the documentation and some of the source for ransack, there doesn't seem to be any natural way to define a custom predicate involving a subquery, which is necessary in your case (because predicates must fit into a where clause). One way to work around this might be to take advantage of the :formatter that your predicate uses as follows:

    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
    

    You can define tags_subquery(tag_ids) as a method that generates the arel node as above but replaces array with tag_ids and calls .to_sql on it before returning it (the formatter needs to return a string, not a node).

    I haven't tried this, so I'll be thrilled if it works!

    0 讨论(0)
提交回复
热议问题