3

我有一个Fact模型,它has_many :votes。投票也有一个user_id字段。我想在Fact模型的范围内表达以下内容:给我所有有 0 票且user_id等于 X的事实。

我对 Arel 不太熟悉,无法理解如何解决这个问题。想法?

4

2 回答 2

1

这有效:

class Fact < ActiveRecord::Base
  scope :by_user, lambda { |id| joins(:user).where('users.id == ?', id).readonly(false)    }
  scope :vote_count, lambda { |count| where('? == (select count(fact_id) from votes where votes.fact_id == facts.id)', count)}
end

Fact.by_user(1).vote_count(0)

vote_count 范围有点 sql,但您可以随意链接这些查找器,您还可以使用以下命令查看底层 sql:

Fact.by_user(1).vote_count(0).to_sql

根据您的评论,您可以通过首先声明关系在纯 Arel 中做同样的事情:

f = Arel::Table.new(:facts)
v = Arel::Table.new(:votes)
u = Arel::Table.new(:users)

然后组合查询并将其呈现给 sql。

sql = f.join(u).on(f[:user_id].eq(1)).where('0 == (select count(fact_id) from votes where votes.fact_id == facts.id)').to_sql

您可以使用运算符对列进行操作: f[:user_id].eq(1)

然后使用它:

Fact.find_by_sql(sql)

我相信你可以做更多的事情来获得更优雅的语法(没有 'where 0 == ...' )。我也很确定 Rails3 范围在幕后使用 Arel - http://m.onkey.org/active-record-query-interface

于 2010-11-14T19:36:23.013 回答
1

我最终用以下范围解决了这个问题:

  scope :not_voted_on_by_user, lambda {|user_id| select("distinct `facts`.*").joins("LEFT JOIN `votes` ON `facts`.id = `votes`.fact_id").where(["votes.user_id != ? OR votes.user_id IS NULL",user_id])}
于 2010-11-19T01:29:31.153 回答