【问题标题】:Sorting users based on scores根据分数对用户进行排序
【发布时间】:2014-03-30 23:02:24
【问题描述】:

在我的 rails 应用中,对于每个用户,我都是这样计算他们的分数的:

<% comment_vote_count = @user.comments.map{|c| c.votes.count}.sum

   comment_vote_count ||=0 

   comment_vote_count *= 2

   final_score = comment_vote_count + @user.base_score %>

这只是在某些视图中呈现,并没有保存在任何地方的数据库中。

我正在尝试按最高分到最低分的顺序填充所有用户的列表,并对结果进行分页。

这就是我的控制器中的内容:

  def user_list
    @users = User.order("created_at DESC").paginate(:page => params[:page], :per_page => 10)
    respond_to do |format|
      format.html
    end
  end

在我的用户列表视图中

<ol>
<%= render (@users) %>
</ol>

<%= will_paginate @users, :next_label => “next” , :previous_label => '', :class => "next_pagination" %>

如何根据分数呈现有序的用户列表?我应该将分数存储在数据库中吗?

【问题讨论】:

  • 这只是分配给每个用户的基本分数。这是存储在数据库中
  • @Katie H 首先你不应该在视图中编写业务逻辑。现在对于您的问题,您有业务逻辑来获取单个用户的投票计数,在用户模型中对所有用户使用相同的逻辑。迭代所有用户,使用相同的逻辑来获得投票计数,将他们推入一个数组并按照您选择的顺序进行排序。
  • 我该怎么做?如果我有成千上万的用户怎么办?我应该遍历所有这些吗??
  • magnum2002 提出的@KatieH 方法将更快地返回用户集合。

标签: ruby-on-rails ruby ruby-on-rails-4


【解决方案1】:

如果你使用 PostGreSQL 作为你的数据库后端并且不怕弄脏你的手,那么你可以替换

@users = User.order("created_at DESC").paginate(:page => params[:page], :per_page => 10)

@users = User.select('u.*, 2 * case when count(v.id) is null then 0 else count(v.id) end + u.score as cnt')
             .from('users u').joins('left join comments c on c.user_id = u.id')
             .joins('left join votes v on v.comment_id = c.id')
             .group('u.id').order('cnt desc').paginate(:page => params[:page], :per_page => 10)

我在 Rails 控制台和 psql 中的本地数据库上对此进行了测试,它们给出了相同的结果——用户按总分降序排列。生成的查询是

SELECT u.id, 2 * case when count(v.id) is null then 0 else count(c.votes) end + u.score as cnt
FROM users u
LEFT JOIN comments c on c.user_id = u.id
LEFT JOIN votes v on v.comment_id = c.id
GROUP BY u.id
ORDER BY cnt desc

执行您正在查找的计算。 case 语句是必要的,因为 PG 在对所有 NULLs 求和时返回 NULL 而不是 0

【讨论】:

  • 这确实是一个非常好的方法,但是你误认为 votes 是 cmets 的属性, votes 是另一个表,看她的代码 se 是使用关联调用 user.cmets[index].votes。跨度>
【解决方案2】:

假设 user.rb 包含:

has_many :votes, through: :comments

你需要一个初始化器

require 'will_paginate/array'

但是控制器很简单:

@users=User.all.map {|i| [i.votes.count,i]}.sort.reverse.paginate(:page => params[:page], :per_page => 10)

在你看来:

@users.each do |user|...

user[0] 是投票数
user[1] 包含您的字段(例如:user[1].id 等)

【讨论】:

  • user.rb 没有那个。我正在使用acts_as_votable gem,用户充当投票者,cmets 充当投票者
  • 太好了。大声笑,这仍然是一个很好的解决方案,但不适合你 :) 抱歉
【解决方案3】:

Comment 属于User,因此每次有人投票此评论时,您都可以为该评论添加所有者积分。我会为名为 ex 的用户添加一列:score 类型为 :integer,然后当有人投票时,您将更新此用户 score 字段:

user = @comment.user
score = @comment.votes.count
user.score = score
user.save

然后直接按score 列排序用户:

User.order("score DESC")

如果使用 act_as_voteable gem

投票.rb

after_create :add_score_to_user

def add_score_to_user
  user = self.voteable.user # gets comment user as voteable is a comment
  score = self.voteable.votes.count # votes count of a comment
  user.score = score
  user.save
  # or user.update_attributes(score: score) instead of last 2 lines
end

如果您只想在每次投票后给用户评分加一分,您可以使用increment

def add_score_to_user
  user = self.voteable.user # gets comment user as voteable is a comment
  user.increment!(:score) # this will add +1 to user score for each vote
end

附言

rails g migration add_score_to_users score:integer

编辑迁移以将分数默认设置为0 然后:

User.order("score DESC").order('id DESC')

【讨论】:

  • 这将更快,并且易于维护,因为每次投票 OP 肯定都会有一个 ajax 调用。
  • 没错,感谢上帝 ajax 调用并不难构建 ;)
  • 如何运行它?使用 after_save 电话?我把这个放在哪里?
  • @KatieH,您需要在提交投票时运行此操作,或者在保存投票后的 after_create/before_save 回调中运行。
猜你喜欢
  • 2017-10-01
  • 2021-08-09
  • 1970-01-01
  • 2020-11-17
  • 1970-01-01
  • 2019-12-31
  • 2012-12-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多