How to use Includes in rails
Includes is a handy method to do eager loading on your associations as stated in the rails documentation - "With includes, Active Record ensures that all of the specified associations are loaded using the minimum possible number of queries."
Lets look at some of the ways to use includes
- Just plain
includes
statement
users = User.includes(:url_shortner_logs)
User Load (0.8ms) SELECT "users".* FROM "users"
UrlShortnerLog Load (1.2ms) SELECT "url_shortner_logs".* FROM "url_shortner_logs" WHERE "url_shortner_logs"."user_id" IN (2, 3, 4, 1)
It fires two queries and loads User and UrlShortnerLog tables in memory.
users.first.url_shortner_logs
The above command does not make a DB call instead loads the data from memory
-
Includes
withwhere
UrlShortner.includes(:url_shortner_logs).where(url_shortner_logs: {created_at: Date.today})
SQL (1.2ms) SELECT "url_shortners"."id" AS t0_r0, "url_shortners"."short_url" AS t0_r1, "url_shortners"."original_url" AS t0_r2, "url_shortners"."sanitized_url" AS t0_r3, "url_shortners"."created_at" AS t0_r4, "url_shortners"."updated_at" AS t0_r5, "url_shortner_logs"."id" AS t1_r0, "url_shortner_logs"."url_shortner_id" AS t1_r1, "url_shortner_logs"."user_id" AS t1_r2, "url_shortner_logs"."browser" AS t1_r3, "url_shortner_logs"."version" AS t1_r4, "url_shortner_logs"."platform" AS t1_r5, "url_shortner_logs"."created_at" AS t1_r6, "url_shortner_logs"."updated_at" AS t1_r7 FROM "url_shortners" LEFT OUTER JOIN "url_shortner_logs" ON "url_shortner_logs"."url_shortner_id" = "url_shortners"."id" WHERE "url_shortner_logs"."created_at" = $1 [["created_at", "2017-07-30"]]
The above query makes a single DB call and does left outer join on the where conditions.
-
Includes
withwhere
andreferences
UrlShortner.includes(:url_shortner_logs).where("url_shortner_logs.created_at= '2017–07–30'")
This query will throw ActiveRecord::StatementInvalid: PG::UndefinedTable: ERROR. It does not have reference to the table UrlShortnerLogs To solve this, rails provides a method called references and it works only in conjunction with includes
UrlShortner.includes(:url_shortner_logs).where("url_shortner_logs.created_at= '2017-07-30'").references(:url_shortner_logs)
SQL (0.5ms) SELECT "url_shortners"."id" AS t0_r0, "url_shortners"."short_url" AS t0_r1, "url_shortners"."original_url" AS t0_r2, "url_shortners"."sanitized_url" AS t0_r3, "url_shortners"."created_at" AS t0_r4, "url_shortners"."updated_at" AS t0_r5, "url_shortner_logs"."id" AS t1_r0, "url_shortner_logs"."url_shortner_id" AS t1_r1, "url_shortner_logs"."user_id" AS t1_r2, "url_shortner_logs"."browser" AS t1_r3, "url_shortner_logs"."version" AS t1_r4, "url_shortner_logs"."platform" AS t1_r5, "url_shortner_logs"."created_at" AS t1_r6, "url_shortner_logs"."updated_at" AS t1_r7 FROM "url_shortners" LEFT OUTER JOIN "url_shortner_logs" ON "url_shortner_logs"."url_shortner_id" = "url_shortners"."id" WHERE (url_shortner_logs.created_at= '2017-07-30')
We can also observe that includes makes two DB calls in basic query condition and makes a single DB call when where clause is being used.