首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >实例依赖作用域解析N+1

实例依赖作用域解析N+1
EN

Stack Overflow用户
提问于 2019-10-20 19:41:14
回答 1查看 488关注 0票数 4

所以,我遇到了麻烦

代码语言:javascript
复制
Preloading instance dependent scopes are not supported.

我有三个型号

代码语言:javascript
复制
class P < ApplicationRecord
  has_many :as
  has_many :cs
end
代码语言:javascript
复制
class C < ApplicationRecord
  belongs_to :p
end
代码语言:javascript
复制
class A < ApplicationRecord
  belongs_to :p
  has_one :c, -> (a) { where(feature: a.feature) }, through: :p, source: :cs
end

我有三个fast_jsonapi序列化程序

代码语言:javascript
复制
class PSerializer
  include FastJsonapi::ObjectSerializer
  has_many :as
end
代码语言:javascript
复制
class CSerializer
  include FastJsonapi::ObjectSerializer
  belongs_to :p
end
代码语言:javascript
复制
class ASerializer
  include FastJsonapi::ObjectSerializer
  belongs_to :p
  has_one :c
end

这是一个种子文件

代码语言:javascript
复制
p_model = P.create(title: 'PTitle')
4.times do |i|
  A.create(title: "aTitle-#{i}", feature: "feature-#{i}", p: p_model)
  C.create(title: "cTitle-#{i}", feature: "feature-#{i}", p: p_model)
end

我想用他的A和A的C来表示P,但是当我尝试这样做时

代码语言:javascript
复制
PSerializer.new(P.first, { include: [:as, :'as.c'] }).serialized_json

我得到了

代码语言:javascript
复制
  P Load (0.1ms)  SELECT  "ps".* FROM "ps" ORDER BY "ps"."id" ASC LIMIT ?  [["LIMIT", 1]]
   (0.1ms)  SELECT "as"."id" FROM "as" WHERE "as"."p_id" = ?  [["p_id", 1]]
  A Load (0.1ms)  SELECT "as".* FROM "as" WHERE "as"."p_id" = ?  [["p_id", 1]]
  C Load (0.2ms)  SELECT  "cs".* FROM "cs" INNER JOIN "ps" ON "cs"."p_id" = "ps"."id" WHERE "ps"."id" = ? AND "cs"."feature" = ? LIMIT ?  [["id", 1], ["feature", "feature-0"], ["LIMIT", 1]]
  C Load (0.1ms)  SELECT  "cs".* FROM "cs" INNER JOIN "ps" ON "cs"."p_id" = "ps"."id" WHERE "ps"."id" = ? AND "cs"."feature" = ? LIMIT ?  [["id", 1], ["feature", "feature-1"], ["LIMIT", 1]]
  C Load (0.1ms)  SELECT  "cs".* FROM "cs" INNER JOIN "ps" ON "cs"."p_id" = "ps"."id" WHERE "ps"."id" = ? AND "cs"."feature" = ? LIMIT ?  [["id", 1], ["feature", "feature-2"], ["LIMIT", 1]]
  C Load (0.1ms)  SELECT  "cs".* FROM "cs" INNER JOIN "ps" ON "cs"."p_id" = "ps"."id" WHERE "ps"."id" = ? AND "cs"."feature" = ? LIMIT ?  [["id", 1], ["feature", "feature-3"], ["LIMIT", 1]]

所以,看起来像N+ 1。但是我知道我可以用includes来解决它。

代码语言:javascript
复制
PSerializer.new(P.includes({ as: :c }).first, { include: [:as, :'as.c'] }).serialized_json

糟了:

代码语言:javascript
复制
irb(main):010:0> PSerializer.new(P.includes({ as: :c }).first, { include: [:as, :'as.c'] }).serialized_json
  P Load (0.1ms)  SELECT  "ps".* FROM "ps" ORDER BY "ps"."id" ASC LIMIT ?  [["LIMIT", 1]]
  A Load (0.1ms)  SELECT "as".* FROM "as" WHERE "as"."p_id" = ?  [["p_id", 1]]
Traceback (most recent call last):
        1: from (irb):10
ArgumentError (The association scope 'c' is instance dependent (the scope block takes an argument). Preloading instance dependent scopes is not supported.)

我可以试试left_joins

代码语言:javascript
复制
irb(main):011:0> PSerializer.new(P.left_joins({ as: :c }).first, { include: [:as, :'as.c'] }).serialized_json
Traceback (most recent call last):
        1: from (irb):11
ArgumentError (The association scope 'c' is instance dependent (the scope block takes an argument). Preloading instance dependent scopes is not supported.)

实际上是一样的。如何解决此N+1问题?

我使用这些模型创建了rails代码库,因此您可以自己尝试。Rails 5.2.3 https://github.com/X1ting/reproduce_preload_bug 2.5.1

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-10-20 20:37:59

非常感谢@igor-khodyrev

他建议我使用组合键,它起作用了!所以,解决方案:

将gem添加到Gemfile

代码语言:javascript
复制
gem 'composite_primary_keys', '=11'

并将模型中的关联更改为

代码语言:javascript
复制
  has_one :c, foreign_key: [:p_id, :feature], primary_key: [:p_id, :feature]

N+1已解决:

代码语言:javascript
复制
irb(main):005:0> PSerializer.new(P.includes({ as: :c }).first, { include: [:as, :'as.c'] }).serialized_json
  P Load (0.2ms)  SELECT  "ps".* FROM "ps" ORDER BY "ps"."id" ASC LIMIT ?  [["LIMIT", 1]]
  A Load (0.1ms)  SELECT "as".* FROM "as" WHERE "as"."p_id" = ?  [["p_id", 1]]
  C Load (0.1ms)  SELECT "cs".* FROM "cs" WHERE ("cs"."p_id" = 1 AND "cs"."feature" = 'feature-0' OR "cs"."p_id" = 1 AND "cs"."feature" = 'feature-1' OR "cs"."p_id" = 1 AND "cs"."feature" = 'feature-2' OR "cs"."p_id" = 1 AND "cs"."feature" = 'feature-3' OR "cs"."p_id" = 1 AND "cs"."feature" = 'feature-kek')
=> "{\"data\":{\"id\":\"1\",\"type\":\"p\",\"relationships\":{\"as\":{\"data\":[{\"id\":\"1\",\"type\":\"a\"},{\"id\":\"2\",\"type\":\"a\"},{\"id\":\"3\",\"type\":\"a\"},{\"id\":\"4\",\"type\":\"a\"},{\"id\":\"5\",\"type\":\"a\"}]}}},\"included\":[{\"id\":\"1\",\"type\":\"a\",\"relationships\":{\"p\":{\"data\":{\"id\":\"1\",\"type\":\"p\"}},\"c\":{\"data\":{\"id\":\"1\",\"type\":\"c\"}}}},{\"id\":\"2\",\"type\":\"a\",\"relationships\":{\"p\":{\"data\":{\"id\":\"1\",\"type\":\"p\"}},\"c\":{\"data\":{\"id\":\"2\",\"type\":\"c\"}}}},{\"id\":\"3\",\"type\":\"a\",\"relationships\":{\"p\":{\"data\":{\"id\":\"1\",\"type\":\"p\"}},\"c\":{\"data\":{\"id\":\"3\",\"type\":\"c\"}}}},{\"id\":\"4\",\"type\":\"a\",\"relationships\":{\"p\":{\"data\":{\"id\":\"1\",\"type\":\"p\"}},\"c\":{\"data\":{\"id\":\"4\",\"type\":\"c\"}}}},{\"id\":\"5\",\"type\":\"a\",\"relationships\":{\"p\":{\"data\":{\"id\":\"1\",\"type\":\"p\"}},\"c\":{\"data\":null}}},{\"id\":\"1\",\"type\":\"c\",\"relationships\":{\"p\":{\"data\":{\"id\":\"1\",\"type\":\"p\"}}}},{\"id\":\"2\",\"type\":\"c\",\"relationships\":{\"p\":{\"data\":{\"id\":\"1\",\"type\":\"p\"}}}},{\"id\":\"3\",\"type\":\"c\",\"relationships\":{\"p\":{\"data\":{\"id\":\"1\",\"type\":\"p\"}}}},{\"id\":\"4\",\"type\":\"c\",\"relationships\":{\"p\":{\"data\":{\"id\":\"1\",\"type\":\"p\"}}}}]}"

我还将解决方案PR添加到我的存储库中。https://github.com/X1ting/reproduce_preload_bug/pull/1

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/58472642

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档