Rails 6中通过has_many关联查询当前用户所在房间的全部消息
ActiveRecord查询所属房间全量消息实现方案
首先补全User模型的反向关联,让关联逻辑闭环,后续查询可以直接复用:
class User < ApplicationRecord has_many :messages # 新增和Room的反向多对多关联,通过messages表做中间表 has_many :rooms, through: :messages end
你可以根据性能需求从以下几种实现方式中选择,都不需要手写循环,不会产生N+1查询问题:
方式1:最易读的常规写法
Message.where(room_id: current_user.rooms.ids)
- 逻辑:先查出当前用户所属的所有房间ID,再查询所有room_id命中这个ID列表的消息,和房间内消息的发送者无关,完全匹配需求:只要是用户所在房间的消息,不管发送者是谁都会被返回。
- 执行逻辑:共生成2条简单SQL,常规数据量级下性能足够。
方式2:单次JOIN查询(最高性能)
如果消息、房间数据量很大,可以用内连接写法,仅执行1条SQL完成查询:
Message.joins(room: :users).where(users: { id: current_user.id }).distinct
- 逻辑:通过内连接关联消息所属房间、房间下的关联用户,筛选出关联用户为当前用户的所有消息,末尾的
distinct用来避免同一条消息被重复返回。 - 执行逻辑:单条多表JOIN查询,不需要提前查询房间ID,大数据量下性能最优。
方式3:可复用关联写法
如果你需要在多处调用这个查询,可以直接在User模型里加一层关联,后续调用不需要重复写查询逻辑:
class User < ApplicationRecord has_many :messages has_many :rooms, through: :messages # 新增关联:用户所属房间下的所有消息 has_many :room_messages, through: :rooms, source: :messages end
定义完成后直接调用即可拿到结果:
current_user.room_messages
后续要做排序、分页、筛选都可以直接链式调用,比如按创建时间倒序加分页:
current_user.room_messages.order(created_at: :desc).page(params[:page])
注意:不要使用循环遍历每个房间再取消息的写法,该写法会产生N+1查询问题,房间数量越多查询次数越高,性能会随数据量上涨快速下降。
内容的提问来源于stack exchange,提问作者Глеб Гутник
相关产品推荐
相关产品推荐

