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



我有一个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表并避免连接,但在未来,我将希望通过一些其他关联来计算排名,因此了解如何计算将非常有帮助。

谢谢

这实际上很容易分割成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')

当然,我建议将它们划分为不同的作用域,但这至少可以让您找到正确的方向。

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

我会让事情变得更容易,因为我更熟悉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')

最新更新