我正在研究搜索项目的功能。
我的模型就是这样,反对:
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,然后手动合并结果,但这很复杂?
还有其他建议吗?
答案 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子句中使用的所有表。