The documentation for this method is incorrect - the joins to the comments
table would only be aliased if the table were previously joined in a different context, like User.joins(:comments, posts: :comments)
. It should instead read:
# Nested joins:
#
# User.joins(posts: [:comments])
# # SELECT "users".*
# # FROM "users"
# # INNER JOIN "posts" ON "posts"."user_id" = "users"."id"
# # INNER JOIN "comments" ON "comments"."post_id" = "posts"."id"
I coincidentally found this while trying to find some way to “ask” a AR relation what join aliases are currently being used and how I can tie those back to the joins in my query. Using a similar example:
# Joining the posts table twice causes the second join to be aliased with "posts_comments".
puts User(:posts, comments: :post).to_sql
=> SELECT "users".* FROM "users" INNER JOIN "posts" ON "posts"."user_id" = "users"."id" INNER JOIN "comments" ON "comments"."user_id" = "users"."id" INNER JOIN "posts" "posts_comments" ON "posts_comments"."id" = "comments"."post_id"
# Filtering on the first join is easy, filtering on the second join is not, notice below that "posts" table is incorrectly referenced twice:
puts User.joins(:posts, comments: :post).where(posts: { title: "dogs" }, comments: { posts: { title: "dogs" } }).to_sql
=> SELECT "users".* FROM "users" INNER JOIN "posts" ON "posts"."user_id" = "users"."id" INNER JOIN "comments" ON "comments"."user_id" = "users"."id" INNER JOIN "posts" "posts_comments" ON "posts_comments"."id" = "comments"."post_id" WHERE "posts"."title" = 'dogs' AND "posts"."title" = 'dogs'
I see that a relation has an alias_tracker
method that you can call aliases
on, but it would be very handy to be able to do the following (I would open a PR for this myself, but the documentation for building joins is all undocumented and confusing to me):
relation = User.joins(:posts, comments: :post)
alias = relation.join_alias_for(comments: :post)
relation.where(alias => { title: "foo" }) # correctly uses posts_comments table alias