0

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.

  • Why do you even try to eager load translations when they are already loaded in the same query with join? – Dieter Pisarewski Aug 28 '22 at 07:21
  • I don't understand, @DieterPisarewski could you please explain what is the solution you suggest? The code above shows that when eager loading is not used, two queries are executed (`Content Load` then `Content::Translation Load`). – Sébastien Dubois Aug 29 '22 at 08:12

0 Answers0