Record Filter, everybody’s favorite ruby library for specifying queries in ActiveRecord, has just hit version 0.9.8. This version has some handy improvements for doing distinct queries and aliasing tables in joins, but it in some cases it is not backwards compatible with previous versions. If you are using an earlier version, take note of the breaking changes described here.
Distinct
Prior to 0.9.8, there was no support for forcing queries to select distinct results. The new version adds a ‘distinct’ method to the DSL to take care of that situation.
Blog.filter do
with(:created_at).greater_than(1.day.ago)
having(:posts).with(:permalink, nil)
distinct
end
# SELECT DISTINCT "blogs".* FROM "blogs"
# INNER JOIN "posts" AS blogs__posts ON "blogs".id = blogs__posts.blog_id
# WHERE (("blogs".created_at > '2009-06-03 14:00:56')
# AND (blogs__posts.permalink IS NULL))
Table aliasing for joins
In earlier versions, it was only possible to set a specific alias for a table that was joined with an explicit join. That caused problems in cases where you want to either specify a specific alias with an implicit join or to join against the same association twice. So, this version adds support for an options hash for both types that supports both :alias and :join_type. This is a breaking change from 0.9.7, where join types were specified differently for each method and were required for only one.
Blog.filter do
having(:posts, :join_type => :left, :alias => 'posts_1').with(:title, nil)
having(:posts, :alias => 'posts_2').with(:title, 'abc')
end
# SELECT DISTINCT "blogs".* FROM "blogs"
# LEFT OUTER JOIN "posts" AS posts_1 ON "blogs".id = posts_1.blog_id
# INNER JOIN "posts" AS posts_2 ON "blogs".id = posts_2.blog_id
# WHERE ((posts_1.title IS NULL) AND (posts_2.title = 'abc'))
In earlier versions, the alias argument was not available and the join_type was passed as the first parameter. For explicit joins, the same options hash is used, which is a change from 0.9.7 where the join type and alias were passed as parameters.
Blog.filter do
join(Post, :join_type => :left, :alias => 'da_posts') do
on(:id => :blog_id)
with(:title, 'def')
end
end
# SELECT DISTINCT "blogs".* FROM "blogs"
# LEFT OUTER JOIN "posts" AS da_posts ON "blogs".id = da_posts.blog_id
# WHERE (da_posts.title = 'def')
For both types, :join_type defaults to :inner and :alias defaults to a clever, unique name for the table.
Changes to limit
This version brings a breaking change to the ‘limit’ method, which was guilty of some ugly argument swapping in the previous version. The method still allows you to specify both a limit and an offset but now makes the offset an optional second argument rather than the first.
Comment.filter do
with(:offensive, true)
limit(10, 100)
end
# SELECT * FROM "comments" WHERE ("comments".offensive = 't')
# LIMIT 10 OFFSET 100
If anybody has problems with these changes, feel free to get in touch… aubreyholland at gmail dot com