ActiveRecord选择添加计数

时间:2013-06-09 21:03:37

标签: ruby-on-rails ruby activerecord

在我的ActiveRecord查询中,我需要在select方法中提供此信息:

(SELECT count(*) from likes where likes.spentit_id = spentits.id) as like_count,
(SELECT count(*) from comments where comments.spentit_id = spentits.id) as comment_count

当然,我将这两个作为字符串传递给.select()部分,但我想知道这样做的正确/替代方法是什么?

这是我要调用的完整查询:

SELECT DISTINCT
    spentits.*,
    username,
    (SELECT count(*) from likes where likes.spentit_id = spentits.id) as like_count,
    (SELECT count(*) from comments where comments.spentit_id = spentits.id) as comment_count,
    (SELECT count(*) from wishlist_items where wishlist_items.spentit_id = spentits.id) as wishlist_count,
    (case when likes.id is null then 0 else 1 end) as is_liked_by_me,
    (case when wishlist_items.id is null then 0 else 1 end) as is_wishlisted_by_me,
    (case when comments.id is null then 0 else 1 end) as is_commented_by_me
FROM spentits
LEFT JOIN users ON users.id = spentits.user_id
LEFT JOIN likes ON likes.user_id = 9 AND likes.spentit_id = spentits.id
LEFT JOIN wishlist_items ON wishlist_items.user_id = 9 AND wishlist_items.spentit_id = spentits.id
LEFT JOIN comments ON comments.user_id = 9 AND comments.spentit_id = spentits.id
WHERE spentits.user_id IN
    (SELECT follows.following_id
     FROM follows
     WHERE follows.follower_id = 9 AND follows.accepted = 1)
ORDER BY id DESC LIMIT 15 OFFSET 0;

此处的所有表都有各自的ActiveRecord对象。真的很困惑如何使用最少的SQL将此查询转换为'activerecord'/ rails方式。 '9'user_id被假定为参数。

更新 好的,这就是我在inmean时间所做的事情,它比原始的SQL语句要好得多,但它对我来说仍然看起来很难看:

class Spentit < ActiveRecord::Base
  belongs_to :user
  has_many :likes
  has_many :wishlist_items
  has_many :comments

  scope :include_author_info, lambda {
    joins([:user]).
    select("username").
    select("users.photo_uri as user_photo_uri").
    select("spentits.*")
  }

  scope :include_counts, lambda {
    select("(SELECT count(*) from likes where likes.spentit_id = spentits.id) as like_count").
    select("(SELECT count(*) from comments where comments.spentit_id = spentits.id) as comment_count").
    select("(SELECT count(*) from wishlist_items where wishlist_items.spentit_id = spentits.id) as wishlist_items_count").
    select("spentits.*")
  }
end

使用这些范围方法,我可以这样做:

Spentit.where(:id => 7520).include_counts.include_author_info.customize_for_user(45)

关于课程的一点。 User有很多SpentitsSpentit有许多commentslikescomments

1 个答案:

答案 0 :(得分:0)

好的,你说“做错了”,一点点。而不是

  scope :include_counts, lambda {
    select("(SELECT count(*) from likes where likes.spentit_id = spentits.id) as like_count").
    select("(SELECT count(*) from comments where comments.spentit_id = spentits.id) as comment_count").
    select("(SELECT count(*) from wishlist_items where wishlist_items.spentit_id = spentits.id) as wishlist_items_count").
    select("spentits.*")
  }

Spentit.find(7520).likes.count
Spentit.find(7520).wishlist_items.count
Spentit.find(7520).comments.count

而不是

  scope :include_author_info, lambda {
    joins([:user]).
    select("username").
    select("users.photo_uri as user_photo_uri").
    select("spentits.*")
  }

DO

Spentit.find(7520).user.username
Spentit.find(7520).user.photo_uri

此外,您可以在引用的模型中定义范围,并使用它们:

class Follow < ActiveRecord::Base
  belongs_to :follower, :class_name => "User"
  belongs_to :following, :class_name => "User"

  scope :accepted, lambda{ where(:accepted => 1) }
end

Spentits.where(:user => Follow.where(:follower => User.find(9)).accepted)

现在,也许你也这样做:

class Spentit
  def to_hash
    hash = self.attributes
    hash[:like_count] = self.like.count
    # ...
  end
end

但你不需要做任何想要在正常情况下获得这些计数的东西,你已经拥有它们。

但是,请注意,您可能还需要执行eager loading,您显然可以将其作为default scope的一部分,或者您将执行比您需要的查询更多的查询。