ActiveRecord查询对我的小脑子来说太难了

时间:2013-05-06 01:31:43

标签: ruby-on-rails-3 postgresql activerecord

我有一个活跃的记录模型Event我想按排名排序。等级将是加权属性的总和。例如,我可能想要使用某些逻辑对事件进行排名:

LOG(number of followers) + (7 - number of days from now)

以下工作,但不能令人满意,因为它返回结果集而不是关系对象。因此,我无法将其视为范围。 (仅供参考我使用Postgres和PostGIS扩展程序)

x = Event.find_by_sql("
            SELECT   
                (
                    CASE WHEN COUNT(follows.*) = 0 
                    THEN 0 
                    ELSE LOG(COUNT(follows.*)) END

                    +

                    SIGN(7 - (EXTRACT(EPOCH FROM start) - EXTRACT(EPOCH FROM NOW())) / 86400) * LOG(ABS(7 - (EXTRACT(EPOCH FROM start) - EXTRACT(EPOCH FROM NOW())) / 86400))
                ) as score,
                events.*
            FROM events
            LEFT OUTER JOIN follows 
                ON events.id = follows.followable_id AND follows.followable_type = 'Event' AND follows.blocked = 'f'
            WHERE (events.start > NOW()) AND (ST_DWithin(st_setsrid(st_point(#{@location[:lng]}, #{@location[:lat]}), 4326), st_transform(loc, 4326), 48280.2, true))
            GROUP BY events.id
            ORDER BY 1 DESC
            ")

我知道我可以在Events表中添加一个计数器缓存并避免连接,但将来我会想通过其他一些关联来计算排名,所以知道如何非常有帮助。

由于

2 个答案:

答案 0 :(得分:3)

这实际上很容易分成ActiveRecord::Relation个查询。

x = Event.select("(
                CASE WHEN COUNT(follows.*) = 0 
                THEN 0 
                ELSE LOG(COUNT(follows.*)) END

                +

                SIGN(7 - (EXTRACT(EPOCH FROM start) - EXTRACT(EPOCH FROM NOW())) / 86400) * LOG(ABS(7 - (EXTRACT(EPOCH FROM start) - EXTRACT(EPOCH FROM NOW())) / 86400))
            ) as score,
            events.*")

x = x.joins("LEFT OUTER JOIN follows 
            ON events.id = follows.followable_id AND follows.followable_type = 'Event' AND follows.blocked = 'f'")

x = x.where("(events.start > NOW()) AND (ST_DWithin(st_setsrid(st_point(#{@location[:lng]}, #{@location[:lat]}), 4326), st_transform(loc, 4326), 48280.2, true))")

x = x.group('events.id')

x = x.order('1 desc')

当然,我建议将它们分成不同的范围,但这至少应该让你朝着正确的方向发展。

答案 1 :(得分:-1)

我认为这不会回答你的问题,但我想使用格式化。

我会更轻松,因为我对ruby更熟悉,我会在你的Event类中创建一个新方法:

def rank
  # you rank computation according model columns and associations
  Math.log(followers_count) + (7 - number_of_days_from_now)
end

def self.by_rank(order = 'desc')
  all.sort{ |event, event1| order == 'desc' ? event1 <=> event : event <=> event1 }
end

然后你可以扩展你的计算缓存,在事件表中创建一个排名列,这样你就可以这样做:

Event.order('rank desc')
Event.order('rank asc')