I'm trying to eager load a Course model with some associated models, Community and Package. The relationships between them are as follows:
class Community < ApplicationRecord
...
has_many :courses, dependent: :destroy
end
class Course < ApplicationRecord
...
belongs_to :community, optional: true
has_many :package_course_relationships, dependent: :destroy
has_many :packages, through: :package_course_relationships
has_many :available_packages, -> { available }, through: :package_course_relationships, source: :package
end
class Package < ApplicationRecord
...
has_many :package_course_relationships, dependent: :destroy
has_many :courses, through: :package_course_relationships
scope :available, -> { where(archived: false) }
end
class PackageCourseRelationship < ApplicationRecord
belongs_to :package
belongs_to :course
end
The problem I am facing is that when calling
Course.includes(:community, :available_packages)
.where(communities: { is_active: true }, archived: false, is_draft: false, is_completed: false, is_default_template: false, visibility: %i[unlisted public])
.find(3)
two sql queries are made:
SELECT DISTINCT "courses"."id" FROM "courses"
LEFT OUTER JOIN "communities" ON "communities"."id" = "courses"."community_id"
LEFT OUTER JOIN "package_course_relationships" ON "package_course_relationships"."course_id" = "courses"."id"
LEFT OUTER JOIN "packages" ON "packages"."id" = "package_course_relationships"."package_id" AND "packages"."archived" = $1
WHERE "communities"."is_active" = $2 AND "courses"."archived" = $3 AND "courses"."is_draft" = $4 AND "courses"."is_completed" = $5 AND "courses"."is_default_template" = $6 AND "courses"."visibility" IN ($7, $8) AND "courses"."id" = $9
LIMIT $10
[["archived", false], ["is_active", true], ["archived", false], ["is_draft", false], ["is_completed", false], ["is_default_template", false], ["visibility", "unlisted"], ["visibility", "public"], ["id", 3], ["LIMIT", 1]]
SELECT "courses"."id" AS t0_r0, "courses"."name" AS t0_r1, "courses"."description" AS t0_r2, "courses"."meeting_times" AS t0_r3, "courses"."start_date" AS t0_r4, "courses"."end_date" AS t0_r5, "courses"."is_default" AS t0_r6, "courses"."created_at" AS t0_r7, "courses"."updated_at" AS t0_r8, "courses"."community_id" AS t0_r9, "courses"."is_draft" AS t0_r10, "courses"."is_private" AS t0_r11, "courses"."archived" AS t0_r12, "courses"."is_completed" AS t0_r13, "courses"."program_curriculum_id" AS t0_r14, "courses"."is_template" AS t0_r15, "courses"."is_default_template" AS t0_r16, "courses"."golfer_limit" AS t0_r17, "courses"."marketing_video_url" AS t0_r18, "courses"."is_featured" AS t0_r19, "courses"."photo_url" AS t0_r20, "courses"."organization_id" AS t0_r21, "courses"."lesson_plan_id" AS t0_r22, "courses"."payment_method" AS t0_r23, "courses"."registration_starts_at" AS t0_r24, "courses"."registration_ends_at" AS t0_r25, "courses"."course_category_id" AS t0_r26, "courses"."personalized_video_url" AS t0_r27, "courses"."visibility" AS t0_r28, "communities"."id" AS t1_r0, "communities"."name" AS t1_r1, "communities"."description" AS t1_r2, "communities"."address_id" AS t1_r3, "communities"."created_at" AS t1_r4, "communities"."updated_at" AS t1_r5, "communities"."is_active" AS t1_r6, "communities"."email" AS t1_r7, "communities"."phone_number" AS t1_r8, "communities"."bio" AS t1_r9, "communities"."is_public" AS t1_r10, "communities"."points" AS t1_r11, "communities"."booking_url" AS t1_r12, "communities"."monthly_points" AS t1_r13, "communities"."weekly_points" AS t1_r14, "communities"."support_rating" AS t1_r15, "communities"."representative_id" AS t1_r16, "communities"."community_stage_id" AS t1_r17, "communities"."additional_note" AS t1_r18, "communities"."goals_note" AS t1_r19, "communities"."completed_onboarding" AS t1_r20, "communities"."season_start_date" AS t1_r21, "communities"."season_end_date" AS t1_r22, "communities"."mailing_address" AS t1_r23, "communities"."community_type" AS t1_r24, "communities"."network_date" AS t1_r25, "communities"."purchased_upsell_products" AS t1_r26, "communities"."map_filters" AS t1_r27, "communities"."sales_rep_id" AS t1_r28, "communities"."pinned_ads" AS t1_r29, "communities"."level" AS t1_r30, "communities"."onboarding_checklist_url" AS t1_r31, "communities"."resource_folder_url" AS t1_r32, "communities"."show_onboarding_banner" AS t1_r33, "communities"."selected_stripe_connected_account_id" AS t1_r34, "communities"."license_type" AS t1_r35, "communities"."application_fee" AS t1_r36, "communities"."pipedrive_deal_id" AS t1_r37, "communities"."pipedrive_organization_id" AS t1_r38, "communities"."golf_genius_id" AS t1_r39, "communities"."application_fee_type" AS t1_r40, "communities"."currency_code" AS t1_r41, "communities"."timezone" AS t1_r42, "packages"."id" AS t2_r0, "packages"."name" AS t2_r1, "packages"."description" AS t2_r2, "packages"."price" AS t2_r3, "packages"."archived" AS t2_r4, "packages"."creator_id" AS t2_r5, "packages"."community_id" AS t2_r6, "packages"."created_at" AS t2_r7, "packages"."updated_at" AS t2_r8 FROM "courses"
LEFT OUTER JOIN "communities" ON "communities"."id" = "courses"."community_id"
LEFT OUTER JOIN "package_course_relationships" ON "package_course_relationships"."course_id" = "courses"."id"
LEFT OUTER JOIN "packages" ON "packages"."id" = "package_course_relationships"."package_id" AND "packages"."archived" = $1
WHERE "communities"."is_active" = $2 AND "courses"."archived" = $3 AND "courses"."is_draft" = $4 AND "courses"."is_completed" = $5 AND "courses"."is_default_template" = $6 AND "courses"."visibility" IN ($7, $8) AND "courses"."id" = $9 AND "courses"."id" = $10
[["archived", false], ["is_active", true], ["archived", false], ["is_draft", false], ["is_completed", false], ["is_default_template", false], ["visibility", "unlisted"], ["visibility", "public"], ["id", 3], ["id", 3]]
I am not very experienced with Rails and I think I might have written the ActiveRecord call wrong, but I can't guess why this is resulting in two similar SQL queries.
I don't really know what to try, but I am expecting only one query to be made. It is confusing to me why the first query that only selects the id of the course is made.
find(3)so it should just return the course with id=3. Given that, what is the purpose of the rest of the query? Just doCourse.find(3)should give you the same result.find(params[:id])withfind(3)to simplify the example given and to focus on the double-query problem.