I have the following models:
Moon → Planet → Star → Galaxy → Universe
Moon belongs_to
Planet, Planet belongs_to
Star, etc .:
class Moon < ActiveRecord::Base
belongs_to :planet, inverse_of: :moons
has_one :star, through: :planet
has_one :galaxy, through: :star
has_one :universe, through: :galaxy
end
class Planet < ActiveRecord::Base
belongs_to :star, inverse_of: :planets
has_many :moons, inverse_of: :planet, dependent: :destroy
end
Problem
I am trying to get Rails to load a hierarchy of these objects into memory. I am trying to achieve two things:
- Download everything using one effective query
- Be able to use calls such as
moon.galaxy
, and not moon.planet.star.galaxy
without, additional calls to the database.
. (m1
) includes
Moon
, . , m1.galaxy
. (m2
) includes
. , m2.galaxy
, .
?
m1 - , m2 -
irb(main):152:0* m1 = Moon.includes(:planet, :star, :galaxy).where(galaxies: {name: 'The Milky Way'}).first
SQL (1.9ms) SELECT "moons"."id" AS t0_r0, "moons"."name" AS t0_r1, "moons"."planet_id" AS t0_r2, "moons"."created_at" AS t0_r3, "moons"."updated_at" AS t0_r4, "planets"."id" AS t1_r0, "planets"."name" AS t1_r1, "planets"."star_id" AS t1_r2, "planets"."created_at" AS t1_r3, "planets"."updated_at" AS t1_r4, "stars"."id" AS t2_r0, "stars"."name" AS t2_r1, "stars"."galaxy_id" AS t2_r2, "stars"."created_at" AS t2_r3, "stars"."updated_at" AS t2_r4, "galaxies"."id" AS t3_r0, "galaxies"."name" AS t3_r1, "galaxies"."universe_id" AS t3_r2, "galaxies"."created_at" AS t3_r3, "galaxies"."updated_at" AS t3_r4 FROM "moons" LEFT OUTER JOIN "planets" ON "planets"."id" = "moons"."planet_id" LEFT OUTER JOIN "planets" "planets_moons_join" ON "planets_moons_join"."id" = "moons"."planet_id" LEFT OUTER JOIN "stars" ON "stars"."id" = "planets_moons_join"."star_id" LEFT OUTER JOIN "planets" "planets_moons_join_2" ON "planets_moons_join_2"."id" = "moons"."planet_id" LEFT OUTER JOIN "stars" "stars_moons_join" ON "stars_moons_join"."id" = "planets_moons_join_2"."star_id" LEFT OUTER JOIN "galaxies" ON "galaxies"."id" = "stars_moons_join"."galaxy_id" WHERE "galaxies"."name" = $1 ORDER BY "moons"."id" ASC LIMIT 1 [["name", "The Milky Way"]]
=>
irb(main):153:0> m2 = Moon.includes(planet: {star: :galaxy}).where(galaxies: {name: 'The Milky Way'}).first
SQL (0.5ms) SELECT "moons"."id" AS t0_r0, "moons"."name" AS t0_r1, "moons"."planet_id" AS t0_r2, "moons"."created_at" AS t0_r3, "moons"."updated_at" AS t0_r4, "planets"."id" AS t1_r0, "planets"."name" AS t1_r1, "planets"."star_id" AS t1_r2, "planets"."created_at" AS t1_r3, "planets"."updated_at" AS t1_r4, "stars"."id" AS t2_r0, "stars"."name" AS t2_r1, "stars"."galaxy_id" AS t2_r2, "stars"."created_at" AS t2_r3, "stars"."updated_at" AS t2_r4, "galaxies"."id" AS t3_r0, "galaxies"."name" AS t3_r1, "galaxies"."universe_id" AS t3_r2, "galaxies"."created_at" AS t3_r3, "galaxies"."updated_at" AS t3_r4 FROM "moons" LEFT OUTER JOIN "planets" ON "planets"."id" = "moons"."planet_id" LEFT OUTER JOIN "stars" ON "stars"."id" = "planets"."star_id" LEFT OUTER JOIN "galaxies" ON "galaxies"."id" = "stars"."galaxy_id" WHERE "galaxies"."name" = $1 ORDER BY "moons"."id" ASC LIMIT 1 [["name", "The Milky Way"]]
=>
m1 - moon.galaxy.name , m2 - moon.galaxy.name DB
irb(main):154:0> m1.galaxy.name
=> "The Milky Way"
irb(main):155:0> m2.galaxy.name
Galaxy Load (0.5ms) SELECT "galaxies".* FROM "galaxies" INNER JOIN "stars" ON "galaxies"."id" = "stars"."galaxy_id" INNER JOIN "planets" ON "stars"."id" = "planets"."star_id" WHERE "planets"."id" = $1 LIMIT 1 [["id", 1]]
=> "The Milky Way"
m1 - moon.planet.star.galaxy.name DB, m2 - moon.planet.star.galaxy.name
irb(main):156:0> m1.planet.star.galaxy.name
Star Load (3.3ms) SELECT "stars".* FROM "stars" WHERE "stars"."id" = $1 LIMIT 1 [["id", 1]]
Galaxy Load (0.3ms) SELECT "galaxies".* FROM "galaxies" WHERE "galaxies"."id" = $1 LIMIT 1 [["id", 1]]
=> "The Milky Way"
irb(main):157:0> m2.planet.star.galaxy.name
=> "The Milky Way"
A Diff