I can’t figure out how to count objects in a scope appropriately.
For this example, let’s assume products (“skus”) had a lat and lng, and so do users.
I have a find_by_sql example that works:
def self.with_max_distance(user, distance)
Sku.find_by_sql(
<<-eos
SELECT *,
( 3959 * acos( cos( radians(#{user.lat}) ) * cos( radians( skus.lat ) )
* cos( radians(skus.lng) - radians(#{user.lng})) + sin(radians(#{user.lat}))
* sin( radians(skus.lat)))) AS distance
FROM skus
HAVING distance < #{distance}
eos
)
end
I was able to get the following scope to work partially:
scope :scope_with_max_distance, lambda { |user, distance|
{ :select => "skus.*, ( 3959 * acos( cos( radians(#{user.lat}) ) * cos( radians( skus.lat ) ) * cos( radians(skus.lng) - radians(#{user.lng})) + sin(radians(#{user.lat})) * sin( radians(skus.lat)))) AS distance",
:having => "distance < #{distance}"
}
}
The scope appears to get the objects correctly, and although I haven’t tested it extensively, it appears to work if I chain it with my other scopes.
However, I am unable to count() the objects, which is really annoying. I know I can use length(), but the fact that it doesn’t work with count leaves me wondering if there’s a better way to do this.
1.9.3p125 :048 > Sku.scope_with_max_distance(User.first, 50).limit(10).count
User Load (0.5ms) SELECT `users`.* FROM `users` LIMIT 1
(0.6ms) SELECT COUNT(count_column) FROM (SELECT 1 AS count_column FROM `skus` HAVING distance < 50 LIMIT 10) subquery_for_count
ActiveRecord::StatementInvalid: Mysql2::Error: Unknown column 'distance' in 'having clause': SELECT COUNT(count_column) FROM (SELECT 1 AS count_column FROM `skus` HAVING distance < 50 LIMIT 10) subquery_for_count
I’d really appreciate any advice or ideas – I’ve tried many different approaches but haven’t been able to find one that works completely. Please let me know if the issue needs more clarification.
countwill replace yourselectwith just the simple elements it needs to build a count as efficiently as it can. It will not check other constraints to ensure that the SQL is valid. You can replace thedistancealias inhavingwith the formula you would be selecting to get around this problem. Also, I recommend that you replacehavingwithwhereto avoid implicit grouping that is not database portable:One last thing, you are calling
countafterlimit(10)which would mean that count will never be greater than 10. That’s not inherently wrong, but I’m not sure that it’s what you meant to do.