Eager loading model instances using single SQL statement with Mobility table backend

32 Views Asked by At

Using Rails 7 and Mobility 1.2.9:

# config/initializers/mobility.rb
Mobility.configure do |config|
  config.plugins do
    backend :table
    active_record
    reader
    writer
    backend_reader
    query
    cache
    dirty
    presence
  end
end

# app/models/content.rb
class Content < ApplicationRecord
  extend Mobility
  translates :slug, :title
end

How to find contents by slug, instantiate them into Content objects and query their translated attributes using one single SQL statement (for an unchanging locale)? The examples below with and without eager_loading run 2 SQL statements.

> content = Content.i18n.find_by(slug: "foo")
  Content Load (0.7ms)  SELECT "contents".* FROM "contents" INNER JOIN "content_translations" "content_translations_en" ON "content_translations_en"."content_id" = "contents"."id" AND "content_translations_en"."locale" = 'en' WHERE "content_translations_en"."slug" = 'foo' LIMIT $1  [["LIMIT", 1]]
> content.title
  Content::Translation Load (0.3ms)  SELECT "content_translations".* FROM "content_translations" WHERE "content_translations"."content_id" = $1  [["content_id", "..."]]
> content = Content.i18n.eager_load(:translations).find_by(slug: "foo")
  SQL (0.6ms)  SELECT DISTINCT "contents"."id" FROM "contents" LEFT OUTER JOIN "content_translations" ON "content_translations"."content_id" = "contents"."id" INNER JOIN "content_translations" "content_translations_en" ON "content_translations_en"."content_id" = "contents"."id" AND "content_translations_en"."locale" = 'en' WHERE "content_translations_en"."slug" = 'blog-1-en' LIMIT $1  [["LIMIT", 1]]
  SQL (0.8ms)  SELECT "contents"."id" AS t0_r0, "contents"."created_at" AS t0_r1, "contents"."updated_at" AS t0_r2, ..., "content_translations"."id" AS t1_r0, "content_translations"."locale" AS t1_r1, "content_translations"."created_at" AS t1_r2, "content_translations"."updated_at" AS t1_r3, "content_translations"."slug" AS t1_r4, "content_translations"."title" AS t1_r5, "content_translations"."content_id" AS t1_r6 FROM "contents" LEFT OUTER JOIN "content_translations" ON "content_translations"."content_id" = "contents"."id" INNER JOIN "content_translations" "content_translations_en" ON "content_translations_en"."content_id" = "contents"."id" AND "content_translations_en"."locale" = 'en' WHERE "content_translations_en"."slug" = 'blog-1-en' AND "contents"."id" = $1  [["id", "..."]]
> content.title
# no DB statement, but there were 2 statements at instantiation

NB: I do not want to pluck attributes but instead to create model instances.

0

There are 0 best solutions below