This is the query I've got at present:
SELECT t1.discipline_id AS discipline1,
t2.discipline_id AS discipline2,
COUNT(DISTINCT t1.product_id) as product_count
FROM (SELECT "product_disciplines".* FROM "product_disciplines") t1
INNER JOIN (SELECT "product_disciplines".* FROM "product_disciplines") t2
ON t1.product_id = t2.product_id
WHERE (t1.discipline_id < t2.discipline_id)
GROUP BY t1.discipline_id, t2.discipline_id
ORDER BY "product_count" DESC
Basically, I've got a list of Products and Disciplines, and each Product may be associated with one or more Disciplines. This query lets me figure out, for each possible (distinct) pair of disciplines, how many products are associated with them. I'll use this as input to a dependency wheel in Highcharts .
The problem arises when I involve Active Model Serializers. This is my controller:
class StatsController < ApplicationController
before_action :get_relationships, only: [:relationships]
def relationships
x = @relationships
.select('t1.discipline_id AS discipline1, t2.discipline_id AS discipline2, COUNT(DISTINCT t1.product_id) as product_count')
.order(product_count: :DESC)
.group('t1.discipline_id, t2.discipline_id')
render json: x, each_serializer: RelationshipSerializer
end
private
def get_relationships
query = ProductDiscipline.all
@relationships = ProductDiscipline
.from(query, :t1)
.joins("INNER JOIN (#{query.to_sql}) t2 on t1.product_id = t2.product_id")
.where('t1.discipline_id < t2.discipline_id')
end
end
each_serializer
points to this class:
class RelationshipSerializer < ApplicationSerializer
has_many :disciplines do
Discipline.where(id: [object.discipline1, object.discipline2])
end
attribute :product_count
end
When I query the database, there are ~1300 possible pairs, which translates my single query in ~1300 Discipline lookups.
Is there a way to avoid the N+1 queries problem with this structure?
From which model this RelationshipSerializer is called? I don't understand why do you need to use object.disciplne1
and object.discipline2
when you could just have a column in the Discipline model pointing out to that model. And in the serializer you would use:
has_many :disciplines, class_name: "::Discipline"
And before you call this serializer make sure that your activerecord query uses includes(:disciplines) for eager loading .
I ended up splitting this in two separate API queries. RelationshipSerializer
saves just the discipline IDs,
class RelationshipSerializer < ApplicationSerializer
# has_many :disciplines do
# # Discipline.where(id: [object.discipline1, object.discipline2])
# [object.discipline1, object.discipline2].to_json
# end
attributes :discipline1, :discipline2
attribute :product_count
end
Since in my app I already need the list of available disciplines, I chose to correlate them client-side.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.