指定したユーザがやりとりしたメッセージの中から、相手ユーザごとに最新の1件を取得したい
Ruby on Rails 4.2.0で実装を行なっています。
以下のようなデータ構造をもつMessageモデルがあるとします。
# == Schema Information
#
# Table name: messages
#
# id :integer not null, primary key
# from_user_id :integer not null
# to_user_id :integer not null
# content :text not null
# created_at :datetime not null
# updated_at :datetime not null
ここで、ユーザごとにやりとりしたメッセージの新着1件ずつを取得したいと考えています。
例えば、指定したユーザがやりとりしたメッセージの一覧は以下のようなscope
で定義できると思います。
scope :related_to, ->(user) { where('from_user_id = :user_id OR to_user_id = :user_id', user_id: user.id) }
これだとユーザごとにすべてのメッセージが取得されてしまいます。
実現したいことは、イメージとしては「from_user_id
かto_user_id
のどちらかについて、1ユーザあたり1件しか取得できない」という条件になると思っています。
汚い書き方をするなら、こうなると思います:
related_to(user)
のfrom_user_id
とto_user_id
をマージする(配列化)- 配列から自分のIDを取り除く
uniq
で重複を取り除く(ここで最新メッセージをやりとりしたユーザIDが取得できる)- 3のそれぞれ最新のメッセージを取得する
よい書き方があれば教えていただければと思います。
よろしくお願いします。
追記です。
ざっくりと、以下のようなコードになるかと思います。
よい書き方があれば、ぜひ回答ください。
module Messages
class HeadlinesFinder
def initialize(user)
@user = user
@messages = []
@other_user_ids = []
end
def call
messages = Message.order(created_at: :desc).related_to(@user)
messages.each do |message|
other_user_id = (message.from_user_id == @user.id) ? message.to_user_id : message.from_user_id
unless other_user_id.in?(@other_user_ids)
@messages << message
@other_user_ids << other_user_id
end
end
@messages
end
end
end
使い方:
@headlines = Messages::HeadlinesFinder.new(current_user).call