我如何包含类别("父母")和子类别("儿童")以摆脱N + 1查询
class Category < ApplicationRecord
belongs_to :parent, class_name: 'Category', foreign_key: :parent_id
has_many :children, class_name: 'Category', foreign_key: :parent_id
has_many :article_categories
has_many :articles, through: :article_categories
scope :roots, -> { where(parent_id: 0) }
scope :children, -> { where.not(parent_id: 0) }
end
带有parent_id 0的类别是&#34;类别/父级&#34;
带有parent_id!= 0的类别是&#34;子类别/儿童&#34;
我在控制器中声明了实例:
@articles = articles.includes(:categories)
在视图中:
@articles.each do |article|
article.categories.roots #N+1 query solved using "includes(:categories)"
article.categories.children.first #N+1 query need to solve
..............
问题是,由于article.categories.children.first
,每个新周期都会对数据库产生新请求N + 1请求是:
Category Load (0.8ms) SELECT "categories".* FROM "categories" INNER JOIN "article_categories" ON "categories"."id" = "article_categories"."category_id" WHERE "article_categories"."article_id" = $1 AND ("categories"."parent_id" != $2) ORDER BY "categories"."id" ASC LIMIT $3 [["article_id", 450], ["parent_id", 0], ["LIMIT", 1]]
我需要包含&#34; parent / Categories&#34;
还包括&#34; children / Subcategories&#34;从...&#34; children.first&#34;
中删除N + 1查询更多详情:
articles.includes(:categories) =>
Article Load (3.9ms) SELECT "articles".* FROM "articles" WHERE "articles"."customer_type" = $1 AND "articles"."aasm_state" IN ('published', 'unpublished') ORDER BY "articles"."title" ASC [["customer_type", 0]]
ArticleCategory Load (3.6ms) SELECT "article_categories".* FROM "article_categories" WHERE "article_categories"."article_id" IN (1, 2, ...)
Category Load (0.7ms) SELECT "categories".* FROM "categories" WHERE "categories"."id" IN (1, 15, ...)
答案 0 :(得分:0)
我总是使用Bullet gem摆脱N + 1问题
答案 1 :(得分:0)
由于您要预加载文章的子类别,您应该在Article
模型中定义此关联:
class Article < ApplicationRecord
has_many :article_categories
# '-> { children }' executes 'children' scope defined in 'Category' model
has_many :children_categories, -> { children }, through: :article_categories, source: :category, class_name: "Category"
end
现在,您可以使用此关联预加载和获取子类别:
# in controller
@articles = articles.includes(:children_categories)
# in view
@articles.each do |article|
article.children_categories.first
修改强>
您可能还想将default_scope
添加到类别模型中,以便始终仅提取子类别:
class Category < ApplicationRecord
default_scope { children }
end
如果您这样做,则不必向关联添加-> { children }
条件。