使用activerecord和arel(sql)过滤链问题

jjhzyzn0  于 2021-07-26  发布在  Java
关注(0)|答案(1)|浏览(315)

设置如下:

class Title
  has_many :managed_rights
end
class ManagedRight
  belongs_to :title
  has_many :managed_right_countries
  enumerize :territory_rights, in: [:all, :include, :exclude], default: :all
end
class ManagedRightCountry
  belongs_to :managed_right
  belongs_to :country
end
class Country; end

我想按国家筛选标题,在哪里 country_ids = [3, 5, 8, 9] .
所有情况如果 title.managed_rights[0].territory_rights == :all 选择此标题
如果包含大小写,则仅当 managed_right_countries 是在 country_ids ,否则跳过。

title.managed_rights[0].territory_rights == :include 
AND title.managed_rights[0].managed_right_countries IN country_ids

如果排除大小写,则选择所有标题,除非 managed_right_countries 是在 country_ids .

title.managed_rights[0].territory_rights == :exclude
AND title.managed_rights[0].managed_right_countries ALL NOT IN country_ids

这就是我对阿瑞尔的尝试:

managed_rights = ManagedRight.arel_table
managed_right_countries = ManagedRightCountry.arel_table

Title.left_outer_joins(managed_rights: :managed_right_countries)
  .where(
    # some other filters not related to "managed_rights"
    managed_rights[:territory_rights].eq(:all)
      .or(
        managed_rights[:territory_rights].eq(:include)
        .and(managed_right_countries[:country_id].in(country_ids))
        .or(
          managed_rights[:territory_rights].eq(:exclude)
          .and(managed_right_countries[:country_id].not_in(country_ids))
        )
      )
  ).distinct

它适用于大多数情况,除非 managed_right_countries.country_id 有一些ID不在 country_ids 数组。上面的问题有什么意义 :exclude 仅当所有国家都在阵列中时有效。但我需要排除标题的结果,如果任何国家是在数组中。
我怎样才能使它正常工作?
最好使用arel,但是如果没有办法只使用arel,那么原始sql(postgresql)也可以工作。
例子:

title_01 has `managed_rights[0].territory_rights = :all`
title_02 has `managed_rights[0].territory_rights = :include`
         and `managed_rights[0].managed_right_countries = [1, 2]`
title_03 has `managed_rights[0].territory_rights = :exclude`
         and `managed_rights[0].managed_right_countries = [2, 3]`

按国家筛选[1]时,应选择所有3个标题(标题\u 02包含1个,标题\u 03不排除1个)
按国家[2]时,只应选择标题\u 01和标题\u 02(因为排除了三分之一)
当按国家[3]排序时,只应选择标题\u 01,因为标题\u 02不包括3,标题\u 03不包括3
如果按空国家/地区[]筛选,则应显示所有三个国家/地区

9q78igpj

9q78igpj1#

通过创建嵌套子查询解决。
sql解决方案:

SELECT titles.* FROM titles
LEFT OUTER JOIN managed_rights ON managed_rights.title_id = titles.id
LEFT OUTER JOIN managed_right_countries ON managed_right_countries.managed_right_id = managed_rights.id
WHERE (titles.rights = 'all' OR managed_rights.title_id is NULL)
OR managed_rights.territory_rights = 'all'
OR (
    managed_rights.territory_rights = 'include' AND managed_right_countries.country_id in (country_ids)
)
OR (
    managed_rights.territory_rights = 'exclude' 
        AND NOT titles.id in (
            SELECT titles.id FROM titles
            LEFT OUTER JOIN managed_rights ON managed_rights.title_id = titles.id
            LEFT OUTER JOIN managed_right_countries ON managed_right_countries.managed_right_id = managed_rights.id
            WHERE managed_rights.territory_rights = 'exclude' 
                AND managed_right_countries.country_id in (country_ids)
        )
)

arel溶液:

titles = Title.arel_table
managed_rights = ManagedRight.arel_table
managed_right_countries = ManagedRightCountry.arel_table

excluded_title_ids = titles.project('titles.id')
  .join(managed_rights, Arel::Nodes::OuterJoin)
  .on(managed_rights[:title_id].eq(titles[:id]))
  .join(managed_right_countries, Arel::Nodes::OuterJoin)
  .on(managed_right_countries[:managed_right_id].eq(managed_rights[:id]))
  .where(
    managed_rights[:territory_rights].eq(:exclude)
      .and(managed_right_countries[:country_id].in(available_countries))
  )

Title.left_outer_joins(managed_rights: :managed_right_countries)
  .where(
    managed_rights[:territory_rights].eq(:all)
    .or(
      managed_rights[:territory_rights].eq(:include)
        .and(managed_right_countries[:country_id].in(available_countries))
        .or(
          managed_rights[:territory_rights].eq(:exclude)
          .and(titles[:id].not_in(excluded_title_ids))
        )
    )
  )

相关问题