Ruby on Rails のモデルのDBリクエストに悩んでいます。

User, Itemモデルと、両者をつなぐ Bookmark (user, item) 、Follow(user, user)の4モデルがあるのですが、
「過去X時間で最もブックマークされたItem X件」とか、「最近友達がブックマークしたItem X件」といった、ちょっと複雑なリクエストの仕方がイマイチよくわかりません。

こういう風にやれば取得できる!!という定番はあるのでしょうか?

回答の条件
  • 1人5回まで
  • 登録:
  • 終了:2012/07/20 15:20:02

回答3件)

id:masuidrive No.1

回答回数10ベストアンサー獲得回数2

「過去X時間で最もブックマークされたItem X件」は、

bookmarks = Bookmark.group("item_id").select("*, count('item_id') AS cnt").order("cnt DESC")

これで、cntで取り出せます。

ex) p bookmakrs.map(&:cnt)


実は、MySQL/SQLiteなどのSQL DBではタイムライン処理のようなモノが難しいです。

http://labs.cybozu.co.jp/blog/kazuho/archives/2008/06/friends_timeline.php

id:fladdict

うわー、ぱっとやり方が見つからないと思ったら、やっぱりゴニャゴニャと色々やらなきゃならないんですね。 初期のTwitterのfollow上限が1000だったりしたのは、この辺が原因なのかしらですね。

2012/07/13 15:46:57
id:highhill No.2

回答回数1ベストアンサー獲得回数0

サーバーはmySQLですかね?そうだったら、mySQL側で、SQLでガリガリ組んで、viewを作ったらどうでしょうかね?
MySQL :: MySQL 5.1 リファレンスマニュアル :: 20.2 CREATE VIEW 構文
Ruby on Railsは知らないんですが、viewだったら、テーブルとして定義されるので、普通にマッピング出来るはず。

それで、肝心のSQLですが、サブクエリで、fromの中にいろいろごにょごにょ入れられるので、それでやってみる事が出来ると思いますよ。

Rubyでも、直接SQLの発行が出来ると思うので、
Ruby on Rails - Rails で、SQL分を直接実行する場合 - gendosuの企画開発室

サブクエリを使ってごにょごにょSQLを組み立てるのもいいかも。
これは僕がcakePHPで作ったサンプルのスクリーンショット。
no title

ORMで複雑なクエリをいろいろすると凄く遅いので、SQLで解決するのが一番パフォーマンスいいと思います。

id:labocho No.3

回答回数1ベストアンサー獲得回数0

class User < ActiveRecord::Base
  has_many :follows, foreign_key: :follower_id
end

class Item < ActiveRecord::Base
  has_many :bookmarks
  
  scope :bookmarked,
    select("items.*, COUNT(bookmarks.id) AS bookmark_count").
    joins(:bookmarks).
    group("bookmarks.item_id")

  scope :recently_bookmarked, proc {|n|
    t = n.hours.ago
    bookmarked.
      where("bookmarks.created_at > ?", t).
      order("bookmark_count DESC")
  }
  
  scope :recently_bookmarked_by_followee, proc{|n, follower|
    followee_ids = follower.follows.map{|f| f.followee_id }
    recently_bookmarked(n).
      where("bookmarks.user_id IN (?)", followee_ids)
  }
end

# 過去 x 時間以内に最もブックマークされた Item を n 件
Item.recently_bookmarked(x).limit(n)

# user がフォローしてるユーザに、過去 x 時間以内に最もブックマークされた Item を n 件
Item.recently_bookmarked_by_followee(x, user).limit(n)

定番かどうかわかりませんが、こんな感じでしょうか。ほとんど SQL ですが、scope で抽象化すると使いやすいです。

後者は JOIN のみでも可能ですが、これくらいが複雑になりすぎず好みです。実際には follower.followee_ids みたいなメソッドを作ってキャッシュするなどしてます。

データベースの VIEW を作るのも良いですが、Rails だとテーブルと対応したモデルと別のクラスになってしまうのが悩みどころですね。

コメントはまだありません

この質問への反応(ブックマークコメント)

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

回答リクエストを送信したユーザーはいません