在我的RequestsController#index
我有一个简单的查询返回47行(实际查询包含一堆其他相关和条件,这里没有相关性):
requests = current_contact.requests.includes(:hub_post).limit(20)
我有以下型号:
class HubPost < ApplicationRecord
belongs_to :contact
has_many :requests, dependent: :destroy
end
class Request < ApplicationRecord
belongs_to :contact
belongs_to :hub_post, optional: true
end
在HubPost中,我有以下方法:
def shared_by(count=2, excluded=nil)
ids = Request.where(hub_post_id: self.id, completed: true).pluck(:contact_id)
ids = ids.keep_if { |id| id != excluded.id } if excluded.present?
ActiveModel::Serializer::CollectionSerializer.new(Contact.where(id: ids).limit(count), serializer: PortalContactSerializer).as_json.shuffle
end
def shared_by_count(excluded=nil)
ids = Request.where(hub_post_id: self.id, completed: true).pluck(:contact_id)
ids = ids.keep_if { |id| id != excluded.id } if excluded.present?
Contact.where(id: ids).count
end
在目前的形式中,此代码代表了一个非常严重的N + 1问题。此端点平均需要2,000毫秒才能响应。现在,我可以通过将.includes(:hub_post)
更改为.includes(hub_post: [requests: [:contact]])
来轻松解决N + 1问题。现在的问题是这个查询从数据库中提取了数千个请求行,并将它们作为ActiveRecord对象在内存中实例化,这是一个更糟糕的问题。
理想情况下,我只想包含completed = true
和contact_id != current_contact.id
的请求,并且我希望limit
{包含的行数>到2或count
定义使我不会抓取数千条记录。
为了使事情变得更复杂,我不想join
关联,因为我放弃了一些请求。我想获取所有current_contact的请求,即使该请求没有hub_post或者它有一个但hub_post没有任何其他请求。
我尝试将includes
与where
语句和references
语句一起使用,但这似乎只是在进行连接,如下所示。当我希望它返回全部47行时,此查询仅返回18行。
requests.includes(hub_post: [:requests]).where(hub_post: { requests: { completed: true } }).references(hub_post: [:requests])
据我所知,includes
无法通过我在该主题上看到的其他问题有条件地判断一个关联。我想知道的是,是否有可能以高效的方式解决N + 1问题而不会导致内存消耗问题。