简体   繁体   English

Rails 活动记录查询以检索包含 IDS 数组中所有 IDS 的所有记录

[英]Rails active record query to retrive all records that contains ALL IDS in the array of IDS

A video can have multiple categories.一个视频可以有多个类别。

video.rb 
has_many :video_categories
has_many :categories, through: :video_categories

category.rb
has_many :video_categories
has_many :videos, through: :video_categories

I have this simple form that allows the user to select the categories he wants to combine in order to find specific videos.我有一个简单的表单,允许用户选择他想要组合的类别以查找特定视频。 For example, there's a "python" category and "advanced" category.例如,有一个“python”类别和“高级”类别。 If he selected these two categories, it should show videos that has both categories.如果他选择了这两个类别,它应该显示同时具有这两个类别的视频。

Video A - Categories [1,4,7] (this is the categories ids)视频 A - 类别 [1,4,7](这是类别 ID)

Video B - Categories [1,2,7,9]视频 B - 类别 [1,2,7,9]

Video C - Categories [7,9]视频 C - 类别 [7,9]

If the user select the categories [1,7], the output should be video A and B. The current scope that I have its returning ALL videos that has category 1, ALL videos that has category 7 and videos that has BOTH of them.如果用户选择类别 [1,7],则输出应为视频 A 和 B。我返回的当前范围是所有类别为 1 的视频、类别为 7 的所有视频以及同时包含这两个类别的视频。

I want just the videos that has BOTH of them.我只想要同时包含两者的视频。 How do I do that ?我怎么做 ?

Current scope:当前范围:

video.rb
scope :for_categories, -> (category_ids) {
    joins(:video_categories).where(
      video_categories: { category_id: category_ids }
    )
  }

pages_controller.rb
def search
 @results = Video.for_categories(params[:category_ids])
end

My form我的表格

        <%= form_with url: "/search", method: :get do |form| %>
            <%= form.collection_check_boxes(:category_ids, Category.all,
                :id, :title, { prompt: 'None'}, { multiple: true} ) do |cat| %>
              <label class="text-capitalize checkbox-inline mr-2">
                <%= cat.check_box %>
                <%= cat.label %>
              </label>
            <% end %>
          <%= form.submit "Search" %>
        <% end %>

You want apply a GROUP and use HAVING to set a condition on the group to ensure that the number of categories joined matches the number of ids:您想应用一个 GROUP 并使用 HAVING 来设置组的条件,以确保加入的类别数量与 id 数量匹配:

class Video
  def self.for_categories(*category_ids)
     ids = category_ids.flatten # handles a single array as input
     group(:id)
       .joins(:categories)
       .where(categories: { id:  ids })
       .having(
          Category.arel_table[:id]
                  .count
                  .gteq(ids.length)
       )
  end
end

In terms of SQL this will give you something like:就 SQL 而言,这会给你类似的东西:

SELECT videos.*
FROM videos
INNER JOIN video_categories ON video_categories.video_id = videos.id
INNER JOIN categories ON video_categories.category_id = categories.id
WHERE categories.id IN (1, 2, 3)
GROUP BY videos.id
HAVING COUNT(categories.id) >= 3

I was able to find the solution with the following code我能够使用以下代码找到解决方案

video.rb
scope :by_categories, -> (category_ids) {
    joins(:categories).where(categories: [category_ids] )
  }

  scope :by_all_categories, -> (category_ids) {
    by_categories(category_ids).
    group("videos.id").
    having('count(videos.id) >= ?', category_ids.size)
  }
pages_controller.rb
def search
    @results = Video.by_all_categories(params[:category_ids].reject(&:empty?))
end
categories_to_search = [1, 7]

conditions = categories_to_search.map do |c|
  { viedo_categories: { category_id: c } }
do

Video.where conditions
# this is the same as: Video.where viedo_categories: { category_id: 1 }, viedo_categories: { category_id: 7 }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM