From 7052280d1c9d625ff83d86e68b665e934a537ef1 Mon Sep 17 00:00:00 2001 From: Martin Meyerhoff Date: Wed, 19 Jul 2023 17:28:56 +0200 Subject: [PATCH] Define #preload_relations on Ingredients In order to be able to preload ingredients' related object graphs, we need to be able to tell the rails preloader how those graphs look. This adds a method that can be used on for example an element like this: ``` def preload_ingredient_relations(element) element.ingredients.group_by(&:preload_relations).each do |preload_relations, ingredients| preload(records: ingredients.map(&:related_object).compact, associations: preload_relations) end element end def preload(records:, associations:) if Rails::VERSION::MAJOR >= 7 ActiveRecord::Associations::Preloader.new(records: records, associations: associations).call else ActiveRecord::Associations::Preloader.new.preload(records, associations) end end ``` Most notorious for n+1 problems is the picture ingredient that tends to have a lot of thumbs that we actually need. --- app/models/alchemy/ingredient.rb | 6 ++++++ app/models/alchemy/ingredients/picture.rb | 4 ++++ spec/models/alchemy/ingredient_spec.rb | 8 ++++++++ spec/models/alchemy/ingredients/picture_spec.rb | 8 ++++++++ 4 files changed, 26 insertions(+) diff --git a/app/models/alchemy/ingredient.rb b/app/models/alchemy/ingredient.rb index 7228abe8e0..6ef01ae1bf 100644 --- a/app/models/alchemy/ingredient.rb +++ b/app/models/alchemy/ingredient.rb @@ -90,6 +90,12 @@ def allowed_settings end end + # This method can be overwritten in individual ingredients to fetch objects that belong to the related object in some form. + # This takes an Array of things that can be passed to the Rails preloader. + def preload_relations + [] + end + # The value or the related object if present def value related_object || self[:value] diff --git a/app/models/alchemy/ingredients/picture.rb b/app/models/alchemy/ingredients/picture.rb index 378dd694f5..9bbdbd7773 100644 --- a/app/models/alchemy/ingredients/picture.rb +++ b/app/models/alchemy/ingredients/picture.rb @@ -38,6 +38,10 @@ class Picture < Alchemy::Ingredient upsample ] + def preload_relations + [:thumbs] + end + # The first 30 characters of the pictures name # # Used by the Element#preview_text method. diff --git a/spec/models/alchemy/ingredient_spec.rb b/spec/models/alchemy/ingredient_spec.rb index 364b04a1ad..5b883fe370 100644 --- a/spec/models/alchemy/ingredient_spec.rb +++ b/spec/models/alchemy/ingredient_spec.rb @@ -213,4 +213,12 @@ ) end end + + describe "#preload_relations" do + let(:ingredient) { Alchemy::Ingredients::Text.new(role: "intro", element: element) } + + subject { ingredient.preload_relations } + + it { is_expected.to eq([]) } + end end diff --git a/spec/models/alchemy/ingredients/picture_spec.rb b/spec/models/alchemy/ingredients/picture_spec.rb index da1b4182b0..98b63bb318 100644 --- a/spec/models/alchemy/ingredients/picture_spec.rb +++ b/spec/models/alchemy/ingredients/picture_spec.rb @@ -125,4 +125,12 @@ ) end end + + describe "#preload_relations" do + let(:ingredient) { described_class.new } + + subject { ingredient.preload_relations } + + it { is_expected.to eq([:thumbs]) } + end end