将current_user的速率加入每首歌曲

时间:2016-01-28 21:15:44

标签: ruby-on-rails activerecord rabl sqlperformance

我需要在索引中显示当前用户的歌曲率。

我有相关的模型:

class Song < ActiveRecord::Base
  has_many :votes, dependent: :destroy

  def user_rate(user)
    votes.find_by(user_id: user).try(:rate)
  end
end

class Vote < ActiveRecord::Base
  belongs_to :song, counter_cache: true
  belongs_to :user
end

投票表:

  create_table "votes", force: :cascade do |t|
    t.integer  "rate"
    t.integer  "user_id"
    t.integer  "song_id
  end

SongsController中的索引操作:

def index
  respond_with @songs = Song.includes(:votes).all
end

在songs / index.json.rabl中,我查找当前用户投票(每个用户只能投票一次)

collection @songs
extends "songs/base"
attributes :thumb_cover
node(:current_user_rate){ |song| song.user_rate(current_user) }

但是song.user_rate(current_user)会生成 n + 1 个查询:(

  User Load (0.9ms)  SELECT  "users".* FROM "users"  ORDER BY "users"."id" ASC LIMIT 1
  Song Load (0.6ms)  SELECT "songs".* FROM "songs"
  Vote Load (0.8ms)  SELECT  "votes".* FROM "votes" WHERE "votes"."song_id" = ? AND "votes"."user_id" = 1 LIMIT 1  [["song_id", 1]]
  Vote Load (0.3ms)  SELECT  "votes".* FROM "votes" WHERE "votes"."song_id" = ? AND "votes"."user_id" = 1 LIMIT 1  [["song_id", 2]]

有没有办法可以在一个查询中为每首歌加入当前用户的vote.rate列?

2 个答案:

答案 0 :(得分:1)

你最好潜入ActiveRecord Association Extensions

#app/models/song.rb
class Song < ActiveRecord::Base
  has_many :votes, dependent: :destroy do
     def user_rate(user)
       find_by(user_id: user.id).try(:rate)
     end
  end
end

这将允许:

@song   = Song.find params[:id]
@rating = @song.votes.user_rate current_user #-> 5 or false

如果您不想执行其他查询,可以尝试通过扩展程序访问的proxy_association对象:

#app/models/song.rb
class Song < ActiveRecord::Base
  has_many :votes, dependent: :destroy do
     def user_rate(user)
       ratings = proxy_association.target.map {|f| [f.user_id, f.rate]}
       rating = ratings.select { |user_id, rate| user_id == user.id }
       rating.first[1] if rating.first
     end
  end
end

您将能够使用:

@song   = Song.find params[:id]
@rating = @song.votes.try(:by, current_user) #-> 5 or false

if @rating
 ...
end

未经测试,但之前我曾以类似的方式使用proxy_association

答案 1 :(得分:0)

改为;

@FunctionalInterface
interface UnaryOperator<T> extends Function<T, T> {
    static UnaryOperator<Object> IDENTITY_FUNCTION = (k -> k);
    static <T> UnaryOperator<T> identity() {
        return (UnaryOperator)IDENTITY_FUNCTION;
    }
}