使用acts_as_votable的缓存计数

时间:2015-07-16 20:18:26

标签: ruby-on-rails caching activerecord

我有一个视图正在迭代一系列帖子,并且正在计算每个帖子的投票数。我知道如何一般地进行急切加载以及如何使用缓存计数器。但是我无法弄清楚如何使用acts_as_votable附带的缓存计数器(或者我做错了什么。)

查看:

        <span class="votes">
        <% if current_user.voted_for? link %>
            <%= link_to like_link_path(link), class: "likes active", id: "link-#{link.id}", remote: true, method: :put do %>
            <i class="fa fa-heart"></i> <%= link.cached_votes_total.to_s %>
            <% end %>
        <% else %>
            <%= link_to like_link_path(link), class: "likes", id: "link-#{link.id}", remote: true, method: :put do %>
            <i class="fa fa-heart-o"></i> <%= link.cached_votes_total.to_s %>
            <% end %>
        <% end %>
        </span>

终端:

Link Load (0.4ms)  SELECT  "links".* FROM "links"  ORDER BY "links"."score" DESC LIMIT 10 OFFSET 0
User Load (0.4ms)  SELECT "users".* FROM "users" WHERE "users"."id" IN (1)
User Load (0.4ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1  ORDER BY "users"."id" ASC LIMIT 1  [["id", 1]]
(0.2ms)  SELECT COUNT(*) FROM "votes" WHERE "votes"."voter_id" = $1 AND "votes"."voter_type" = $2 AND "votes"."votable_id" = $3 AND "votes"."votable_type" = $4 AND "votes"."vote_scope" IS NULL  [["voter_id", 1], ["voter_type", "User"], ["votable_id", 1], ["votable_type", "Link"]]
(0.2ms)  SELECT COUNT(*) FROM "votes" WHERE "votes"."voter_id" = $1 AND "votes"."voter_type" = $2 AND "votes"."votable_id" = $3 AND "votes"."votable_type" = $4 AND "votes"."vote_scope" IS NULL  [["voter_id", 1], ["voter_type", "User"], ["votable_id", 2], ["votable_type", "Link"]]
Rendered home/index.html.erb within layouts/application (24.6ms)
Completed 200 OK in 60ms (Views: 56.9ms | ActiveRecord: 1.6ms)

谢谢!

2 个答案:

答案 0 :(得分:2)

对我来说,这看起来没什么异常。 voted_for?方法是voted_on?的别名。继续阅读您将发现的代码,此方法将调用votes.size > 0,这将触发对数据库的以下查询。 (我猜你的页面上有2个链接,对吗?)所以它与缓存的计数字段无关。

(0.2ms)  SELECT COUNT(*) FROM "votes" WHERE "votes"."voter_id" = $1 AND "votes"."voter_type" = $2 AND "votes"."votable_id" = $3 AND "votes"."votable_type" = $4 AND "votes"."vote_scope" IS NULL  [["voter_id", 1], ["voter_type", "User"], ["votable_id", 1], ["votable_type", "Link"]]
(0.2ms)  SELECT COUNT(*) FROM "votes" WHERE "votes"."voter_id" = $1 AND "votes"."voter_type" = $2 AND "votes"."votable_id" = $3 AND "votes"."votable_type" = $4 AND "votes"."vote_scope" IS NULL  [["voter_id", 1], ["voter_type", "User"], ["votable_id", 2], ["votable_type", "Link"]]

P.S。代码在这里:github: voter.rb

答案 1 :(得分:2)

可能这不是最好的方法,但它可以帮助预加载用户对单个数据库命中链接的投票:

  1. 在后端某处(例如用户模型),您可以定义类似

    的方法
    # votable - records, which were voted by user, links in the case
    def preloaded_votes(votable)
      @preloaded_votes ||= Vote.where(voter: self, votable: votable).group(:votable_id).count
    end
    

    此方法将返回类似{votable_id1 => count1, votable_id2 => count2 }的哈希值,其中votable_idN表示方法的votable参数的元素的ID。

  2. 将当前通话current_user.voted_for? link更改为预加载的计数current_user.preloaded_votes.key?(link.id)
  3. 此解决方案增加了一个数据库命中,但允许避免N+1个查询。此外,其他SQL不包含任何表连接。一种可能的改进方法 - 为这类查询添加特殊索引。