5

Facebook のグループ ページに似たグループ ページを作成するアプリケーションを作成しています。誰かがページに投稿することができ、その投稿には返信があります。これらは非常に類似したプロパティを持っているため、投稿と返信は同じ STI テーブルに結合されます。

class Page < ActiveRecord::Base
  has_many :posts
  has_many :replies, through: :posts
end

class BasePost < ActiveRecord::Base
  ...
end

class Post < BasePost
  belongs_to :page
  ...
end

class Reply < BasePost
  belongs_to :post
  ...
end

手に入れたいですpage.posts_and_replies

ページの「最も気に入った投稿または返信」などを見つけるには、投稿と返信を組み合わせて、次のような結果セットを取得する必要があります。

top_messages = page.posts_and_replies.order_by('total_likes DESC').limit(10)

投稿と返信を一緒に出すのは難しい

単一の結果セットとして並べ替えるposts_and_repliesには、通常は単一の結合に基づいてクエリを実行する必要があります。

class Page < ActiveRecord::Base
  has_many :posts
  has_many :replies, through: :posts
  has_many :posts_and_replies, class_name: 'BasePost'
end

# ideally we could then do queries such as
most_recent_messages = @page.posts_and_replies.order('created_at DESC').limit(10)

page_idにしか存在しないため、これを行うことはできませんposts。「返信」は、それらが属する投稿を通じてのみページを参照します。したがって、この結合は投稿を提供しますが、返信は提供しません。


可能な解決策:

データを非正規化します...

Reply と Post にを複製することもできpage_idますが、非正規化されたデータは涙で終わる傾向があるため、可能であればそれを避けたいと思います。

カスタムを使用するfinder_sql

カスタム finder sql を使用して、投稿と返信を別々に取得し、それらを結合することができます。

class Page < ActiveRecord::Base
  has_many :posts
  has_many :replies
  has_many :posts_and_replies, class_name: 'BasePost', finder_sql: Proc.new{ 
    "select base_posts.* from base_posts 
      where page_id = #{self.id}
    UNION
      select 
      base_posts.*
      from base_posts left join base_posts as head_posts
      on base_posts.post_id = head_posts.id
      where head_posts.page_id = #{self.id}"
  }
end

カスタム finder_sql は機能しますが、関連付け拡張では機能しません

上記の finder_sql は実際には機能しますが、関連拡張機能はどれも機能しません。関連拡張機能 ( など.where) を使用しようとすると、組み込みの finder_sql にフォールバックします。

これは機能します

 Page.find(8).posts_and_replies
 => select base_posts.* from base_posts 
      where page_id = 8
    UNION
      select 
      base_posts.*
      from base_posts left join base_posts as head_posts
      on base_posts.post_id = head_posts.id
      where head_posts.page_id = 8

*ただし、これは誤った finder_sql にフォールバックします*

  Page.find(8).posts_and_replies.where('total_likes > 1')
  => select "base_posts".* from "base_posts" where "base_posts"."page_id" = 8 
     and (total_likes > 12)

何らかの理由で、関連付け拡張機能が正しいfinder_sql

アソシエーション拡張機能が指定された finder_sql を尊重するようにするにはどうすればよいですか?

問題は、finder_sql を適切に使用していない関連付け拡張機能に要約されているようです。これを強制する方法はありますか?

4

1 に答える 1