【问题标题】:Looking to make this Active Record Query more performant. Ruby on Rails希望让这个 Active Record Query 更高效。 Ruby on Rails
【发布时间】:2018-07-12 00:47:35
【问题描述】:

嘿,所以我是活动记录的新手,我在这里有这两个地图功能,我想知道是否有更好/更高效的方法来进行以下查询:

现在,当我执行查询时,我的终端中有数百行,如果很多人同时加载这些对话,恐怕会占用大量内存?

第 1 步获取所有相关对话

第 2 步制作一个对话哈希,其中包含有关发件人、收件人和链接到该对话的消息的所有信息。

第 3 步将所有这些对话按正确的顺序排序,即从对话中对它们进行排序,最新消息位于顶部。

编辑 1:Rails 版本 5.0.1

 def index
      i = 1
      @messages = []
      @user = User.find_by(email: params[:email])
      @message = Conversation.where("sender_id = ? OR recipient_id = ?", @user.id, @user.id)
      @message.map { |conversation|
        @messages << conversation if Message.where(conversation_id: conversation.id).count > 0
      }



      render json: @messages.map { |conversation|
        {
          date: conversation.messages.last.created_at,
          sender: User.find(conversation.sender_id),
          recipient: User.find(conversation.recipient_id),
          conversation: {
            id: conversation.id,
            messages: Message.where(conversation_id: conversation.id).sort_by{|e| e[:created_at]}.reverse.map {|message| {
              sender: {
                email: User.find(message.user_id).email,
                first_name: User.find(message.user_id).first_name,
                last_name: User.find(message.user_id).last_nam },
              body: message.body,
              created_at: message.created_at
              }
            }
          }}
      }.sort_by { |hsh| hsh[:date] }.reverse
 end

【问题讨论】:

  • 是的,这效率不高,因为在此操作中执行了多个 N+1 查询。关注模型之间的关系以避免这种情况。似乎特定用户将通过消息进行许多对话。在您的用户模型中定义此关系,您应该能够调用@user.conversations,ActiveRecord 将执行有限数量的查询。

标签: ruby-on-rails json ruby activerecord


【解决方案1】:
class Message < ApplicationRecord
  belongs_to :conversation, touch: true
end

class Conversation < ApplicationRecord
  has_many :messages, -> { order(created_at: :desc) }
  belongs_to :sender, foreign_key: :sender_id, class_name: 'User'
  belongs_to :recipient, foreign_key: :recipient_id, class_name: 'User'
end

class User < ApplicationRecord
  has_many :conversations, -> (user) { unscope(where: :user_id).where('conversations.sender_id = ? OR conversations.recipient_id = ?', user.id, user.id) }
end

class ConversationsController < ApplicationController
  def index
    @user = User.find_by(email: params[:email])

    # "joins(:messages)" allows only retrieving conversations having at least one message, and does not include conversation with 0 message
    # now ordering by `updated_at: :asc` because `Message belongs_to :conversation, touch: true`, in which Conversation's updated_at will be automatically "touched"/updated whenever the associated Messages are updated/created.
    @conversations = @user.conversations.joins(:messages).order(updated_at: :asc).distinct

    json_response = @conversations.as_json(
      only: [:id, :updated_at],
      include: {
        sender: {
          only: [:id, :first_name, :last_name, :email]
        },
        recipient: {
          only: [:id, :first_name, :last_name, :email]
        },
        messages: {
          only: [:body, :created_at]
        }
      }
    )

    render json: json_response
  end
end

示例请求

Started GET "/conversations?email=foobar@example.com"

示例响应

[{
  "id": 2,
  "updated_at": "2018-02-02T11:17:45.376Z",
  "sender": {
    "id": 4,
    "first_name": "Lorem",
    "last_name": "Ipsum",
    "email": "loremipsum@example.com"
  },
  "recipient": {
    "id": 1,
    "first_name": "Foo",
    "last_name": "Bar",
    "email": "foobar@example.com"
  },
  "messages": [{
    "body": "Hello there",
    "created_at": "2018-02-02T11:17:45.367Z"
  }, {
    "body": "Whatcha doin'?",
    "created_at": "2018-02-02T11:17:36.451Z"
  }, {
    "body": "hahaha :)",
    "created_at": "2018-02-02T11:03:29.843Z"
  }]
}, {
  "id": 1,
  "updated_at": "2018-02-02T11:36:14.275Z",
  "sender": {
    "id": 1,
    "first_name": "Foo",
    "last_name": "Bar",
    "email": "foobar@example.com"
  },
  "recipient": {
    "id": 5,
    "first_name": "Ruby",
    "last_name": "Rails",
    "email": "rubyonrails@example.com"
  },
  "messages": [{
    "body": "hello Ruby-on-Rails! :)",
    "created_at": "2018-02-02T11:36:14.267Z"
  }]
}]

您可以查看如何使用.as_jsonhere

您可以查看如何使用touch: truehere

测试工作

【讨论】:

  • 非常感谢您的详细回答。我一直在努力完成这项工作,但我遇到了以下问题:“未初始化的常量 User::ConversationsWithMessage”
  • 对话模型中的 .order 方法似乎也不是正确的语法。如果这可能相关,我在 Rails 5.0 上。抱歉忘了提。
  • @Hugo 嗨,对于不正确的 order 语法和 conversations_with_messages,我们深表歉意。我刚刚更新了这个。你能再试一次吗。如果这不起作用,我会尝试复制您的场景,以便验证:)
  • 好的,所以我仍然无法使@对话查询工作。 .joins(:messages) 将@conversations 转换为一个消息列表,每个消息都引用一个不适合我的对话。我确实设法使用了您代码的其他部分,非常感谢,但我仍在使用自己的方式来检索 @message 变量中的对话。也许这有点误导我在我的 var 中使用了@messages 名称,而它应该被称为@conversations。
  • 我仍然无法使@user.conversations 工作。我得到了关于对话错误的无列用户。
猜你喜欢
  • 1970-01-01
  • 2011-10-25
  • 1970-01-01
  • 1970-01-01
  • 2018-05-08
  • 1970-01-01
  • 2019-11-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多