Rails 7 adds `#with_all_rich_text` to eager load all rich text associations at o...
source link: https://blog.saeloun.com/2021/03/23/rails-adds-support-for-eager-loading-all-rich-text-associations-at-once
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Rails 7
added
support for eager loading all rich text associations at once.
We can now use #with_all_rich_text
instead of eager loading each rich text association separately with #with_rich_text_{field_name}
.
Before
We could eager load rich text associations using the helpers provided by ActionText.
Let’s take an example of the Post model.
We have 3 rich text fields in the Post
model - :summary
, :body
and :tldr
.
class Post < ApplicationRecord
has_rich_text :summary
has_rich_text :body
has_rich_text :tldr
end
Now, if we have to eager load all the rich_text associations we would end up doing the following:
def show
Post
.with_rich_text_summary
.with_rich_text_body
.with_rich_text_tldr
.find(params[:id])
end
This will still make 4 queries to the database.
- 1 query to load the Post and
- 3 queries to the ActionText table to load the 3 rich_texts associations.
We can see the same in the log below.
Started GET "/posts/1" for ::1 at 2021-03-17 15:25:43 +0530
Processing by PostsController#show as HTML
Parameters: {"id"=>"1"}
(0.1ms) SELECT sqlite_version(*)
↳ app/controllers/posts_controller.rb:17:in 'show'
Post Load (0.1ms) SELECT "posts".* FROM "posts" WHERE "posts"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
↳ app/controllers/posts_controller.rb:17:in 'show'
ActionText::RichText Load (0.2ms) SELECT "action_text_rich_texts".* FROM "action_text_rich_texts" WHERE "action_text_rich_texts"."record_type" = ? AND "action_text_rich_texts"."name" = ? AND "action_text_rich_texts"."record_id" = ? [["record_type", "Post"], ["name", "summary"], ["record_id", 1]]
↳ app/controllers/posts_controller.rb:17:in 'show'
ActionText::RichText Load (0.1ms) SELECT "action_text_rich_texts".* FROM "action_text_rich_texts" WHERE "action_text_rich_texts"."record_type" = ? AND "action_text_rich_texts"."name" = ? AND "action_text_rich_texts"."record_id" = ? [["record_type", "Post"], ["name", "body"], ["record_id", 1]]
↳ app/controllers/posts_controller.rb:17:in 'show'
ActionText::RichText Load (0.1ms) SELECT "action_text_rich_texts".* FROM "action_text_rich_texts" WHERE "action_text_rich_texts"."record_type" = ? AND "action_text_rich_texts"."name" = ? AND "action_text_rich_texts"."record_id" = ? [["record_type", "Post"], ["name", "tldr"], ["record_id", 1]]
After
The new method #with_all_rich_text
allows to load all the rich text associations at once.
With the same Post
model with three action_text associations.
class Post < ApplicationRecord
has_rich_text :summary
has_rich_text :body
has_rich_text :tldr
end
We will now use the newly introduced method #with_all_rich_text
.
def show
Post
.with_all_rich_text
.find(params[:id])
end
The above method will now only fire one query to the database. This is how the query will be generated.
Started GET "/posts/1" for ::1 at 2021-03-17 15:31:53 +0530
Processing by PostsController#show as HTML
Parameters: {"id"=>"1"}
SQL (0.3ms) SELECT "posts"."id" AS t0_r0, "posts"."title" AS t0_r1, "posts"."body" AS t0_r2, "posts"."created_at" AS t0_r3, "posts"."updated_at" AS t0_r4, "posts"."status" AS t0_r5, "posts"."category" AS t0_r6, "action_text_rich_texts"."id" AS t1_r0, "action_text_rich_texts"."name" AS t1_r1, "action_text_rich_texts"."body" AS t1_r2, "action_text_rich_texts"."record_type" AS t1_r3, "action_text_rich_texts"."record_id" AS t1_r4, "action_text_rich_texts"."created_at" AS t1_r5, "action_text_rich_texts"."updated_at" AS t1_r6, "rich_text_bodies_posts"."id" AS t2_r0, "rich_text_bodies_posts"."name" AS t2_r1, "rich_text_bodies_posts"."body" AS t2_r2, "rich_text_bodies_posts"."record_type" AS t2_r3, "rich_text_bodies_posts"."record_id" AS t2_r4, "rich_text_bodies_posts"."created_at" AS t2_r5, "rich_text_bodies_posts"."updated_at" AS t2_r6, "rich_text_tldrs_posts"."id" AS t3_r0, "rich_text_tldrs_posts"."name" AS t3_r1, "rich_text_tldrs_posts"."body" AS t3_r2, "rich_text_tldrs_posts"."record_type" AS t3_r3, "rich_text_tldrs_posts"."record_id" AS t3_r4, "rich_text_tldrs_posts"."created_at" AS t3_r5, "rich_text_tldrs_posts"."updated_at" AS t3_r6 FROM "posts" LEFT OUTER JOIN "action_text_rich_texts" ON "action_text_rich_texts"."record_type" = ? AND "action_text_rich_texts"."name" = ? AND "action_text_rich_texts"."record_id" = "posts"."id" LEFT OUTER JOIN "action_text_rich_texts" "rich_text_bodies_posts" ON "rich_text_bodies_posts"."record_type" = ? AND "rich_text_bodies_posts"."name" = ? AND "rich_text_bodies_posts"."record_id" = "posts"."id" LEFT OUTER JOIN "action_text_rich_texts" "rich_text_tldrs_posts" ON "rich_text_tldrs_posts"."record_type" = ? AND "rich_text_tldrs_posts"."name" = ? AND "rich_text_tldrs_posts"."record_id" = "posts"."id" WHERE "posts"."id" = ? LIMIT ? [["record_type", "Post"], ["name", "summary"], ["record_type", "Post"], ["name", "body"], ["record_type", "Post"], ["name", "tldr"], ["id", 1], ["LIMIT", 1]]
How this works?
The following method is responsible for finding all the rich_text associations for the model.
private
def rich_text_association_names
reflect_on_all_associations(:has_one).collect(&:name).select { |n| n.start_with?("rich_text_") }
end
It is collecting all the has_one
associations for the model starting with rich_text_
and
eager loading all the associations at once.
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK