ActiveRecordでユーザの最新の発言を取得する

id:NeoCat:20080525に引き続き monologue (Twitterもどき)を作っていきます。


特定のユーザの最新の発言を取得するにはどうすれば良いでしょうか。
まず思いつくのは、そのまま「あるユーザのステータスを日付順に並べたときに、一番最初のもの」と記述する方法です。
findで書くと下のような感じでしょうか。

      new_status = Status.find(:first,:conditions => [ "id = ?", @user.id], :order => "updated_at DESC")

もしくはfind_byで

      new_status = Status.find_by_user_id(@user.id, :order => "updated_at DESC", :limit => 1)

など。

しかし、例えばフォロワーの一覧に最新ステータスを並べたい場合など、ユーザごとにこの検索を行っていると、とても遅そうです。
インデックスの作成などDB側の工夫でもある程度は早くなるでしょうが、最新の1件(もしくはn件)だけ表示できれば良い場合、発言時にusersテーブルの中にフィールドを追加し、そこに最新のStatusのIDを記録してしまうという方法が考えられます。
ただし発言の削除時は、気をつけないと、存在しないIDを指し示すことになってしまいます。これを回避するには、削除時に最新の発言を改めて検索して、フィールドを更新すれば良さそうです。

というわけで、実装してみたのが以下のコントローラ。

  def new
    if logged_in? && request.post?
      @status = Status.new(params[:status])
<snip>
          current_user.statuses << @status
          current_user.new_status = @status.id                   #追加
          current_user.save!                                     #追加
          flash[:notice] = "更新しました!"
<snip>
    redirect_to(:controller => :user, :action => 'index')
  end
  
  def delete
    @status = params[:id] && Status.find(params[:id])
    if @status && @status.user == current_user
      @status.destroy
      flash[:notice] = "削除しました!"
      new_status = Status.find_by_user_id(current_user.id,
                               :limit => 1, :order => "updated_at DESC")  #追加
      current_user.new_status = new_status ? new_status.id : nil          #追加
      current_user.save!                                                  #追加
<snip>
end

以前作ったStatusコントローラに上記のコメント部分で示したを追加しました。


なお、あらかじめMigrationなどを使ってusersテーブルにはあらかじめnew_statusフィールドを追加しておきます。やり方はこの記事などを参照。


こうしておけば、Status.find(@user.new_status) で素早く最新のStatusが取得できます。