如何减少类似查询的数量

时间:2013-05-01 20:14:23

标签: ruby-on-rails

我正在为我的Rails应用程序制作一个非常基本的分析功能。我想提供统计信息,告诉用户有多少访问者查看了他们的个人资料,然后根据每个访问者的特定角色(我在我的应用中使用'rolify')将其分解。

在Users控制器的show动作中,我这样做

@profileviews = Profileview.where(:user_id => @user.id)
@profileviewsbysomerole = Profileview.where({:user_id => @user.id, :viewer_role => 'someRole'})
@profileviewsbysomeotherrole = Profileview.where({:user_id => @user.id, :viewer_role => 'someOtherRole'})

然后在show动作中,我会做

Your profile has been viewed <%= @profileviews.size %> times.
Your profile has been viewed by users with a particular role <%= @profileviewsbysomerole.size %> times.
Your profile has been viewed by users with some other role <%= @profileviewsbysomeotherrole.size %> times.

有没有办法在不进行三次单独查询的情况下完成我想要做的事情,或者这是获得这些统计数据的最佳方式(在不降低性能方面)。

3 个答案:

答案 0 :(得分:1)

在性能方面,我认为没关系。另一种选择是查询所有对象然后在内存中过滤,但我不认为这是一个好主意。让数据库做它最擅长的事情是最好的。

我想到的一件事 - 您可以使用单个查询和group_by以避免进行第二次和第三次通话,但如果您希望获得相关内容,则相关一些汇总数据。

在编码风格方面,您可以为不同的查询定义范围,并在视图中使用它们,但这可能是一种过度杀伤。

答案 1 :(得分:1)

如果您发现自己一起使用这些东西,将它们捆绑在一起可能会很好:

# in user model
def profile_view_hash(*roles)
  views = { 'all' => Profileview.where(:user_id => id).all }
  roles.each do |role|
    views.merge!({
      role => Profileview.where(:user_id => id, :viewer_role => role).all
    })
  end
  views
end

这应该允许你像这样使用它:

# in controller
@profile_views = @user.profile_view_hash('someRole','someOtherRole')

# in view
<%= @profile_views['all'] %>
<%= @profile_views['someRole'] %>
<%= @profile_views['someOtherRole'] %>

顺便说一句,在您的示例中,您只显示.size的输出。如果这就是您所需要的,那么您应该使用count而不是all

答案 2 :(得分:1)

我会将它们全部留在控制器之外,然后像这样制作视图:

Your profile has been viewed <%= @user.profileviews.size %> times.
Your profile has been viewed by users with a particular role <%= @user.profileviews.select { |profile_view| profile_view.viewer_role == 'someRole' }.size %> times.
Your profile has been viewed by users with some other role <%= @user.profileviews.select { |profile_view| profile_view.viewer_role == 'someOtherRole' }.size %> times.

你可以通过将这个逻辑移到Profileview来更进一步,可能就像:

def views_for_role( role )
  select { |profile_view| profile_view.viewer_role == role }.size
end

并将其委托给User,也许就像:

delegate :views_for_role, :to => :profileview

哪会让你的观点看起来像这样:

Your profile has been viewed <%= @user.profileviews.size %> times.
Your profile has been viewed by users with a particular role <%= @user.views_for_role 'someRole' %> times.
Your profile has been viewed by users with some other role <%= @user.views_for_role 'someOtherRole' %> times.