我有chat
型号:
class Chat < ApplicationRecord
# Associations
has_many :chat_recipients
has_many :recipients, through: :chat_recipients, source: :person
has_many :messages, dependent: :destroy
has_many :latest_5_messages, -> { order(created_at: :desc).limit(5) }, class_name: Message.name
# Associations
end
和message
模型:
class Message < ApplicationRecord
# Associations
belongs_to :person
belongs_to :chat
# Associations
end
这两个协会:
has_many :messages, dependent: :destroy
has_many :latest_5_messages, -> { order(created_at: :desc).limit(5) }, class_name: Message.name
指向同一个模型/类。
我期望的是,应该只获取5条消息来减少服务器负载。我已经开发了超过4年的Rails应用程序了,我发现了一些我觉得有些奇怪的东西。当我在rails console
上运行此命令时:
2.3.1 :080 > chat = current_person.chats.includes(:recipients, :latest_5_messages).find(1)
这是输出:
Chat Load (0.8ms) SELECT "chats".* FROM "chats" INNER JOIN "chat_recipients" ON "chats"."id" = "chat_recipients"."chat_id" WHERE "chat_recipients"."person_id" = $1 AND "chats"."id" = $2 LIMIT $3 [["person_id", 1], ["id", 1], ["LIMIT", 1]]
ChatRecipient Load (0.4ms) SELECT "chat_recipients".* FROM "chat_recipients" WHERE "chat_recipients"."chat_id" = 1
Person Load (0.7ms) SELECT "people".* FROM "people" WHERE "people"."id" IN (1, 2)
Message Load (1.0ms) SELECT "messages".* FROM "messages" WHERE "messages"."chat_id" = 1 ORDER BY "messages"."created_at" DESC
可以清楚地看到,提取了获取所有消息的查询。但是我想只获取5条记录,对吧?如果无论如何取得所有记录,那么对has_many关联应用限制的重点是什么。但这不是现在,我跑了:
chat.latest_5_messages.count
这是输出:
2.3.1 :082 > chat.latest_5_messages.count
(0.9ms) SELECT COUNT(count_column) FROM (SELECT 1 AS count_column FROM "messages" WHERE "messages"."chat_id" = $1 LIMIT $2) subquery_for_count [["chat_id", 1], ["LIMIT", 5]]
=> 5
我是说发生了什么?我喜欢Rails,我绝对喜欢,但我认为在开发超过4年之后,我仍然在追踪ActiveRecord的工作。为什么另一个查询当我chat.latest_5_messages
时,我在Associations::CollectionProxy
中看到了5条记录,但事情就是当我在这个集合上循环时,有80多次迭代(它提取了与提供的限制(5)相反的所有相关记录)。看看:
2.3.1 :087 > chat.latest_5_messages.map(&:id)
=> [190, 189, 188, 187, 186, 185, 184, 183, 182, 181, 180, 179, 178, 177, 176, 175, 174, 172, 170, 169, 167, 166, 165, 164, 163, 162, 161, 160, 159, 158, 157, 156, 155, 152, 151, 150, 149, 148, 147, 146, 145, 144, 143, 142, 141, 140, 139, 138, 137, 136, 135, 134, 133, 132, 131, 130, 129, 128, 127, 126, 125, 124, 123, 122, 121, 120, 119, 118, 117, 116, 115, 114, 113, 112, 111, 110, 109, 108, 107, 106, 105, 104, 103, 102]
并且我必须chat.latest_5_messages.all
来获取我想要的5条记录,但这是以我想避免的另一个数据库查询为代价的。
对不起如果这个问题听起来像是某些人,我可能不知道ActiveRecord的某些部分,但如果有人解释,我们将非常感激。
P.S:这只能使用原始SQL查询来实现,还是有办法以Rails方式实现。
答案 0 :(得分:0)
听起来您正在尝试加载具有指定限制的关联,而ActiveRecord不支持该关联。见http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#module-ActiveRecord::Associations::ClassMethods-label-Eager+loading+of+associations
特别是:
如果您急切加载与指定的:limit选项的关联,它将被忽略,返回所有关联的对象
这也在很多其他地方讨论,例如Rails 4 Eager load limit subquery,Rails Eager Load and Limit
我无法解释为什么map
会给你所有的记录 - 这肯定是奇怪的。您是否有机会在map
上覆盖ApplicationRecord
的定义?升级到Rails / ActiveRecord的最新次要版本会对它产生影响吗?
答案 1 :(得分:0)
您的想法的最佳实现是范围
class Chat < ApplicationRecord
has_many :chat_recipients
has_many :recipients, through: :chat_recipients, source: :person
has_many :messages, dependent: :destroy
scope :latest_5_messages, -> { order(created_at: :desc).limit(5) }
end