查询部分可以通过范围非常简单地完成:
class Category
scope :grouped_entries, lambda { |user, date|
select(['categories.*', 'COUNT(entries.id) as count_entries'])
.joins(:entries)
.where('entries.user_id = ?', user.id)
.where('MONTH(entries.created_at) = ?', date.month)
.group('categories.id')
}
end
然后可以循环:
<% Category.grouped_entries(current_user, Date.today).each do |category| %>
<%= category.name %> with <%= category.count_entries %> entries this month.
<% end %>
当然,缓存这需要您在本月创建条目时刷新缓存。例如,您可以像这样缓存查询:
@categories = Rails.cache.fetch("/grouped_entries/#{current_user.id}/#{Date.today.month}") do
Category.grouped_entries(current_user, Date.today).all
end
然后在使用 user_id 和条目 created_at 月份创建新条目时简单地将其过期。我想说您应该先使用这种方法,然后再尝试单独缓存每个类别的条目。此查询应该执行得很快,因此您不必深入研究单独缓存每一行。它还将执行单个查询,而不是为每个类别执行一个查询。
这就是为什么我不会单独缓存每一行的原因:
- 您仍然必须查询数据库以获取用户的类别列表或类别 ID,因此无论如何您都必须执行一个查询。
- 缓存过期更复杂,因为在更多情况下您必须使两个缓存过期,例如,当条目的类别更改时,您必须使旧类别缓存和新类别缓存过期。
- 您最终可能会针对您的数据库运行更多查询以获取过期的缓存信息,并且数据库的延迟最终可能会比实际查询花费更长的时间。
- 您不需要缓存每一行,因为查询很简单并且使用索引。您应该有一个关于 user_id 和 category_id 条目的索引。