ActiveRecord left_joins 命名

问题描述 投票:0回答:1

我正在使用 acts-as-taggable-on 并且我想搜索模型或其关联中可能存在的标签。


class Event < ApplicationRecord
  acts_as_taggable_on :mentions

  belongs_to :establishment
end

class Establishment < ApplicationRecord
  acts_as_taggable_on :hashtags
end

我正在尝试使用标签值上的

LIKE
进行搜索。我对连接的
ActiveRecord
命名感到困惑。

Event.left_joins([:mentions]).to_sql
#=> SELECT "events".* FROM "events" LEFT OUTER JOIN "taggings" ON "taggings"."taggable_type" = 'Event' AND "taggings"."context" = 'mentions' AND "taggings"."taggable_id" = "events"."id" LEFT OUTER JOIN "tags" ON "tags"."id" = "taggings"."tag_id"
# name is "tags" for "mentions"

Event.left_joins([:mentions], {:establishment=>[:hashtags]}).to_sql
#=> SELECT "events".* FROM "events" LEFT OUTER JOIN "taggings" ON "taggings"."taggable_type" = 'Event' AND "taggings"."context" = 'mentions' AND "taggings"."taggable_id" = "events"."id" LEFT OUTER JOIN "tags" ON "tags"."id" = "taggings"."tag_id" LEFT OUTER JOIN "establishments" ON "establishments"."id" = "events"."establishment_id" LEFT OUTER JOIN "taggings" "hashtag_taggings_establishments_join" ON "hashtag_taggings_establishments_join"."taggable_type" = 'Establishment' AND "hashtag_taggings_establishments_join"."context" = 'hashtags' AND "hashtag_taggings_establishments_join"."taggable_id" = "establishments"."id" LEFT OUTER JOIN "tags" "hashtags_establishments" ON "hashtags_establishments"."id" = "hashtag_taggings_establishments_join"."tag_id"
# name is "tags" for "mentions" and "hashtags_establishments" for hashtags in establishment

Event.left_joins({:establishment=>[:hashtags]}).to_sql
#=> SELECT "events".* FROM "events" LEFT OUTER JOIN "establishments" ON "establishments"."id" = "events"."establishment_id" LEFT OUTER JOIN "taggings" ON "taggings"."taggable_type" = 'Establishment' AND "taggings"."context" = 'hashtags' AND "taggings"."taggable_id" = "establishments"."id" LEFT OUTER JOIN "tags" ON "tags"."id" = "taggings"."tag_id"
# name is "tags" for "hashtags" in establishment

如果只有一个连接,则文本值在

tags.name
中,但如果有多个连接,就我而言,它是
"#{tag_association}_#{model_name.pluralize}.name"

我想强制一个名称 100% 确定我正在搜索的位置,而不必计算我必须猜测该名称的连接数。

ruby-on-rails nested left-join where-clause acts-as-taggable-on
1个回答
0
投票

我喜欢这篇文章,它解释了连接的左外部 https://www.ananunesdasilva.com/posts/understanding-activerecord-left-outer-joins

了解左外连接及其应用

在本文中,我们将深入研究左外连接(或简称左连接)的概念并探讨其用例。左外连接允许我们根据相关表中的匹配和不匹配条目来查询表。与仅返回匹配条目的内连接不同,左外连接始终包含左表中的所有条目,即使右表中没有相应的匹配项也是如此。

为了说明这一点,让我们参考一个涉及两个类的域模型:住宿和预订。一个住宿可以有多个预订,每个预订都属于一个住宿。

以下是两个表中的示例记录:

住宿 |身份证 |名称 | |----|----------------| | 1 | “海滨别墅”| | 2 | “迷人的房子”| | 3 | “里斯本公寓”| | 4 | “小木屋”| | 5 | “农舍”|

预订 |身份证 |住宿 ID |入住 |退房 | |----|--------------------|-------------|------------ --| | 1 | 1 | 2023 年 6 月 20 日 | 2023 年 6 月 23 日 | | 2 | 1 | 2023 年 8 月 7 日 | 2023 年 8 月 8 日 | | 3 | 2 | 2023 年 5 月 22 日 | 2023 年 5 月 28 日 | | 4 | 4 | 2023 年 4 月 1 日 | 2023 年 4 月 2 日 |

获取所有住宿及其预订数

要检索住宿列表及其预订数量,我们需要在两个表之间执行联接。最初,使用 ActiveRecord 的

joins
方法,相应的 SQL 查询会产生 INNER JOIN:

Accommodation.joins(:bookings)

等效的SQL:

SELECT "accommodations".* FROM "accommodations" INNER JOIN "bookings" ON "bookings"."id" = "accommodations"."booking_id"

此 INNER JOIN 仅返回具有匹配预订的住宿。因此,“里斯本公寓”和“农舍”被排除在结果之外,因为它们没有关联的预订。

即使没有预订,也要包含住宿,我们可以采用

left_joins
(或
left_outer_joins
)方法:

Accommodation.left_joins(:bookings)

这会生成以下 SQL:

SELECT "accommodations".* FROM "accommodations" LEFT OUTER JOIN "bookings" ON "bookings"."id" = "accommodations"."booking_id"

带有左外连接的结果表将如下所示:

住宿.id 住宿.名称 预订.id bookings.accommodation_id 预订.check_in 预订.check_out
1 “海滨别墅” 1 1 2021 年 6 月 20 日 2021 年 6 月 23 日
1 “海滨别墅” 2 1 2021 年 8 月 7 日 2021 年 8 月 8 日
2 “迷人的房子” 3 2 2021 年 5 月 22 日 2021 年 5 月 28 日
3 '里斯本公寓'
4 “小木屋” 4 4 2021 年 4 月 1 日 2021 年 4 月 2 日
5 “农舍”

如上所示,左外连接包括所有住宿,甚至包括那些没有预订的住宿。住宿“Lisbon Flat”和“Farm House”出现在结果中,与预订相关的属性设置为“null”。

计算每个住宿的预订量

要获取每个住宿的预订数量,我们可以将 ActiveRecord 的

group
count
方法与左外连接结合使用:

Accommodation.left_joins(:bookings).group(:name).count('bookings.id')

此 SQL 查询计算所需的输出,提供一个哈希值,其中住宿名称作为键,相应的预订计数作为值。即使没有预订的住宿也会出现在结果中,显示计数为 0。

{"Beach House"=>2, "Charming House"=>1, "Lisbon Flat"=>0, "Le Petit Chalet"=>1, "Farm House"=>0}

使用 SQL 字符串自定义联接

left_joins
left_outer_joins
方法从 Rails 版本 5.2.3 开始可用。对于早期的 Rails 版本,需要使用 SQL 字符串自定义联接。例如:

Accommodation.joins('LEFT OUTER JOIN bookings ON bookings.accommodation_id = accommodations.id')

无需预订即可获得住宿

要仅检索没有预订的住宿,我们可以使用

where
方法根据预订表中的空值过滤左外连接的结果:

Accommodation.left_joins(:bookings).where(bookings: { id: nil })

SQL:

SELECT COUNT(*) FROM "accommodations" LEFT OUTER JOIN "bookings" ON "bookings"."accommodation_id" = "accommodations"."id" WHERE "bookings"."id" IS NULL

输出:

身份证 姓名
3 '里斯本公寓'
5 “农舍”

对于 Rails 6 及更高版本,可以使用 ActiveRecord 的

missing
方法实现同一解决方案的更多语义版本:

Accommodation.where.missing(:bookings)

在本系列的下一部分中,我们将探索在单个查询中组合多个联接。

© www.soinside.com 2019 - 2024. All rights reserved.