Ecto: Preloading sorted associations

Viewed 430

I want to sort a query of Campaign based on the number of Conversion in a nested association. And I want to do it in another query while preloading Campaign:

CampaignGroup
|> Repo.all()
|> Repo.preload(campaigns: Campaign |> Campaign.sorted_by_number_of_conversions)

But that raises an Ecto error: ERROR 42803 (grouping_error): column "c4.id" must appear in the GROUP BY clause or be used in an aggregate function

My sort query looks like this:

defmodule MyApp.Campaign
  def sorted_by_number_of_conversions(query) do
    from campaign in query,
    join: company in assoc(campaign, :company),
    join: good in assoc(company, :goods),
    join: conversion in assoc(good, :conversion),
    group_by: [campaign.id, company.id],
    order_by: [desc: count(conversion.id)]
  end
end

The raw SQL query looks like this:

SELECT c0."id", c0."description", c0."start_date", c0."name", c0."end_date", c0."url", c0."logo", c0."company_id", c0."inserted_at", c0."updated_at", c4."id" FROM "campaigns" AS c0 
INNER JOIN "companies" AS c1 ON c1."id" = c0."company_id" 
INNER JOIN "goods" AS g2 ON g2."company_id" = c1."id" 
INNER JOIN "conversions" AS c3 ON c3."good_id" = g2."id" 
INNER JOIN "campaign_groups" AS c4 ON c4."id" = ANY($1) 
INNER JOIN "campaigns_campaign_groups" AS c5 ON c5."campaign_group_id" = c4."id" WHERE (c5."campaign_id" = c0."id") 
GROUP BY c0."id", c1."id" 
ORDER BY c4."id", count(c3."id") DESC

Running the query outside of a preload works just fine:

Campaign 
|> Campaign.sorted_by_number_of_conversions()
|> Repo.all()

But shouldn't this query also work inside of a preload statement?

My schemas look like this:

Conversion:

schema "conversions" do
  belongs_to :good, MyApp.Good
end

Good:

schema "goods" do
  has_one :conversion, MyApp.Conversion
  belongs_to :company, MyApp.Company
end

Company:

schema "companies" do
  has_many :goods, MyApp.Good
  has_many :campaigns, MyApp.Campaign
end

Campaign:

schema "campaigns" do
  belongs_to :company, MyApp.Company
  many_to_many :campaign_groups, MyApp.CampaignGroup, join_through: "campaigns_campaign_groups"
end

CampaignGroup:

schema "campaign_groups" do
  many_to_many :campaigns, MyApp.Campaign, join_through: "campaigns_campaign_groups"
end
0 Answers
Related