ActiveRecord에서 merge에서 joins를 실행하면 거동이 바뀝니다.

4148 단어 RailsActiveRecord

상황



(Rails 5.0.0에서 확인)
class A
  belongs_to :B
end

class B
  has_one :A 
  has_one :C 

  scope :with_c, -> { joins(:c) }
  scope :without_c, -> { left_outer_joins(:c).merge(C.where(id: nil)) }
end

class C
  belong_to :B 
end
> B.with_c.to_sql
SELECT "bs".* FROM "bs" INNER JOIN "cs" ON "cs"."b_id" = "bs"."id"

> A.joins(:b).merge(B.with_c).to_sql
SELECT "as".* FROM "as" INNER JOIN "bs" ON "bs"."id" = "as"."b_id" LEFT OUTER JOIN "cs" ON "cs"."b_id" = "bs"."id"


> B.without_c.to_sql
SELECT "bs".* FROM "bs" INNER JOIN "cs" ON "cs"."b_id" = "bs"."id" WHERE "cs"."id" IS NULL

> A.joins(:b).merge(B.without_c).to_sql
ActiveRecord::ConfigurationError: Can't join 'A' to association named 'c'; perhaps you misspelled it?
from /path/to/activerecord-5.0.0/lib/active_record/associations/join_dependency.rb:231:in `find_reflection'



질문



INNER JOIN인가 OUTER JOIN인가로 치명적으로 거동이 바뀌어 버리는 로직을 쓰려고 합니다만, 그러한 쿼리 내용을 안정시키고 싶은 경우 여러분 어떻게 하고 있을까요?

지금 생각하고 있는 해결안


  • 원소로 사용할 때와 merge에서 사용할 때 사용하는 scope를 구분
  • 구별하는 뻔뻔스럽고 사고 할 것

  • 먼저 B 집합을 찾은 다음 C를 찾습니다 (쿼리 분할)
  • 케이스에 따라서는 (이번은 둘 다 has_one이므로) B의 집합이 작기 때문에 그래도 좋은 느낌

  • scope 내의 쿼리를 직접 작성
  • scope :without_c, -> { joins("LEFT OUTER JOIN cs ON cs.b_id = bs.id").merge(C.where(id: nil)) }
  • 동작의 안정성에서는 제일의 생각이 들지만, 리팩토링이라고 칭해 left_outer_joins(:c)에 깔끔하게 바뀔 것 같다. 코멘트 필수. 그리고 eager_load와 여러가지 섞여 복잡한 쿼리가 되면 안될지도.

  • B 기준으로 쿼리를 다시 조립합니다.
  • 요구 사항에 따라, JOIN의 계층이 깊어지지 않고 끝나기 때문에 솔직한 쿼리를 할 수 있고 거동의 변화에 ​​시달리지 않는다. B의 리스트를 취할 수 있지만, A의 리스트를 원하는 경우는 B의 리스트로부터 취합시다고 하는 것으로.


  • 정답?



    h tps : // 게임 m 없음 m. 코 m / 게 ms / 아 c 치오 레코 rd / ぇ r 시온 s / 4.0.0에서
    The solution is to build JoinAssociation when two relations with join information are being merged. And later while building the Arel use the previously built JoinAssociation record in JoinDependency#graft to build the right from clause. Fixes #3002.
    

    라고 쓰여져 있습니다만, how to build JoinAssociation 를 몰랐다

    좋은 웹페이지 즐겨찾기