渴望只加载活动记录的第一页结果

时间:2013-08-30 08:25:05

标签: ruby-on-rails-3 activerecord eager-loading

我正在研究搜索项目的功能。

我的模型就是这样,反对:

User has many items
User has many addresses
User has one profile
Item has many images
Item has many addresses
Item belongs to category

我想显示按用户分组的结果,例如:

Results for 'Laptop':

Items of Bob:
  Laptop dell (details...)
  Samsung laptop (details...)

Items of Alice:
  Laptop HP (details...)
...

所以我有急切的加载这个大问题:

r = User.includes([{items: [:images, :addresses, :category]}, :addresses, :profile]).
  joins(:items).
  where('query like "%laptop%"').
  where(...).
  limit(80).
  all
# then get the first page of results with kaminary

然后我在erb中用这样的循环显示:

r.each do |u|
  # items of user u
  u.items do |i|
    # item i
  end
end

一切都很好,除了急切的加载需要很长的时间。通过急切加载显示的第一页上的项目,可能会快得多。

可以使用活动记录来急切加载只有条件的记录吗?类似于:User.includes([:table1, table2], conditions: 'users.id in (1,2,3)')

另一种解决方案是使用限制20执行大请求,然后重新执行请求而不需要加载限制80,然后手动合并结果,但这很复杂?

还有其他建议吗?

1 个答案:

答案 0 :(得分:0)

我最终做了两个请求并将结果合并在一起。它并不像听起来那么难:

# per_page is 20
limit = "#{(page.to_i - 1) * 20}, #{(page.to_i * 20)}"
query = Users.where('items.title LIKE "%whatever%"').where('addresses.lat < 50')
return query.includes([{items: [:images, :category]}, :profile, :addresses]).
    joins(:items).          
    limit(limit).all |
    query.select('DISTINCT users.*').joins(:items, :addresses).
    limit(200).all

备注1:

此eager只加载当前页面的20个元素,并添加其他页面上的所有元素而不需要加载。例如,假设您在第3页,第一个请求将加载limit 60, 80,并添加元素#0到#199。数组union(operator |)在ruby中的工作方式是,如果两个数组中都存在相同的元素,则保留第一个数组中的元素。在这种情况下,第一个数组中的元素将是eager加载的元素。因此,如果我们在第3页,它会将[60..80] (eager loaded)[1..200] (not eager loaded)合并,而最终数组中的元素60到80将是第一个数组中的元素。

备注2:

select distinct很重要,因为没有它,限制将限制项目或地址的数量。例如,如果用户#1有198个对象,则第二个查询将返回如下内容:

  user 1 - item 1 of user 1
  user 1 - item 2 of user 1
  ...
  user 1 - item 198 of user 1
  user 2 - item 1 of user 2
  user 3 - item 1 of user 3

这不会正确预加载。合并后我们会有用户[1,2,3,60,61,... 80]。

如果不同,结果将是:

  user 1 - item 1 of user 1
  user 2 - item 1 of user 2
  user 3 - item 1 of user 3
  ...
  user 200 - item 1 of user 200

合并后我们会[1,2,3 ... 200],从第一个查询中取出元素60到80.

备注3:

在第二个查询中,我们必须连接where子句中使用的所有表。