【问题标题】:Ruby on Rails - Scope with Join and OrderRuby on Rails - 具有联接和顺序的范围
【发布时间】:2012-07-21 00:30:59
【问题描述】:

我有 2 节课:

class Solution
  has_many :solution_votes

class SolutionVote
  belongs_to :solution

我的观点是这样的:


    Proposed Solution A - 2 votes up - 5 votes down
    Proposed Solution B - 1 vote up  - 0 votes down
    Proposed Solution C - 0 votes up - 0 votes down
    Proposed Solution D - 7 votes up - 2 votes down
    Proposed Solution E - 3 votes up - 1 vote down
     

我想要的是按最多 UP 票对它进行排序,使其看起来像这样:


    Proposed Solution D - 7 votes up - 2 votes down
    Proposed Solution E - 3 votes up - 1 vote down
    Proposed Solution A - 2 votes up - 5 votes down
    Proposed Solution B - 1 vote up  - 0 votes down
    Proposed Solution C - 0 votes up - 0 votes down
    

到目前为止我有这个:

 scope :most_votes_up,
  select("solutions.*, count(solution_votes.id) AS votes_count").
      where(['solutions.state = ?', 'Proposed']).
      joins(:solution_votes).
      group("solutions.id").
      order("votes_count DESC")

产生这个输出:


    Proposed Solution D - 7 votes up - 2 votes down
    Proposed Solution A - 2 votes up - 5 votes down
    Proposed Solution E - 3 votes up - 1 vote down
    Proposed Solution B - 1 vote up  - 0 votes down
    

但是...我仍然遇到的问题是:
1. 没有投票的建议解决方案丢失(上例中的建议解决方案 C 从显示的列表中丢失)
2. 我如何指定仅对 UP 投票的计数(现在,它是根据哪个提议的解决方案获得最多投票(上下)而不是仅向上来排序)?

我正在使用 PostGRESQL

【问题讨论】:

  • 我刚刚意识到为什么我的 0 票没有出现在视图中:因为我在 Solutions_vote 表中还没有他们的记录。那么,我该怎么办?我仍然需要它们出现在视图中。

标签: sql ruby-on-rails-3 postgresql scope


【解决方案1】:
  1. 使用左连接(而不是默认的内连接)来包含具有 0 个相关解决方案投票的解决方案。

  2. 您可以只在计数中包含赞成票。

这是我修改范围的方式:

select("solutions.*, count(solution_votes.id) AS votes_count").
  joins("left join solution_votes on solution_votes.solution_id = solutions.id").
  where(['solutions.state = ? and solution_votes.direction = ?', 'Proposed', 'up']).
  group("solutions.id").
  order("votes_count DESC")

这对您的列名做了一些假设,但是您应该能够根据您的实际架构对其进行调整,而不会有太多麻烦。我还将joins 放在where 之前——这在技术上不会产生影响,但这是SQL 需要的顺序,对我来说更符合逻辑。

编辑: 听起来好像您想将 votes_count 保留为所有选票的计数,但仍按赞成票数排序。我不确定为什么会这样,除非您在返回的解决方案对象上调用 .votes_count,但这是可能的。为了做到这一点,您从count 聚合切换到sum,然后执行一些操作,将匹配条件的记录视为 1,将不匹配的记录视为 0。两种方式呈现自己,一个 @ 987654327@ 表达式,如sum(case when solution_votes.direction = 'up' then 1 else 0 end),或进行一些创造性的转换以将布尔值转换为整数,例如sum(cast(solution_votes.direction = 'up' as integer))。这两种方法都可以 - 总和将是赞成票的总数,然后您可以在 order 子句中使用它。选择第一个,没有特别的原因,我们最终得到以下修改后的范围:

select("solutions.*, count(solution_votes.id) AS votes_count, sum(case when solution_votes.direction = 'up' then 1 else 0 end) as up_votes").
  joins("left join solution_votes on solution_votes.solution_id = solutions.id").
  where(['solutions.state = ? and solution_votes.direction = ?', 'Proposed', 'up']).
  group("solutions.id").
  order("up_votes DESC")

【讨论】:

  • 谢谢! - 这解决了我的 2 个问题中的 1 个:使用左连接,现在甚至出现 0 票。但是,我仍然对所有出现的选票有疑问,只是按 UP 选票排序。您的代码只会返回 UP 投票,而我需要返回所有投票。
  • “返回”和“出现”是什么意思?鉴于这是 Rails,将返回的是解决方案对象的集合。范围只是决定你得到哪些以及它们的顺序。
  • 再次感谢您。这就是我所缺少的。我将它稍微修改为返回所有选票(不仅仅是投票),但在你的帮助下这很容易: scope :most_votes_up, select("solutions.*, count(solution_votes.id) AS votes_count, sum(case when solution_votes.投票 = 'up' 然后 1 else 0 end) as up_votes")。 joins("left join solution_votes on solution_votes.solution_id = solutions.id"). where(['solutions.state = ?', 'Proposed']).组(“解决方案.id”)。 order("up_votes DESC")
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-24
  • 1970-01-01
相关资源
最近更新 更多