MetaWhere updates, in more detail


With Rails 3.0 RC released, I decided I'd better step up the pace on the promise I made to cover recent updates to MetaWhere and MetaSearch in more detail prior to Rails 3 final. Tonight, I'd like to spend some time on detailing the changes to MetaWhere since 0.3.3.

Smarter Order Clauses

This one is pretty simple. ActiveRecord 3.0 query syntax normally dictates that you supply a valid SQL string to the order method, like so:

    Article.joins(:comments).order('comments.created_at desc')
    => "SELECT "articles".* FROM "articles"
       INNER JOIN "comments" ON "comments"."article_id" = "articles"."id"
       ORDER BY comments.created_at desc" 

With MetaWhere, you can supply your order clauses in Ruby, instead of SQL. Since you're supplying hashes, the same inference about table names is in effect here as in your where clauses, meaning that multiple joins of the same table will reference the proper table alias automatically, and (of course) you can use autojoin.

    Article.order(:comments => :created_at.desc).autojoin.to_sql
    => "SELECT "articles".* FROM "articles"
       INNER JOIN "comments" ON "comments"."article_id" = "articles"."id"
       ORDER BY "comments"."created_at" DESC" 

Speaking of table aliasing...

Multiple Self-referencing Join Support

Let's say you have a model called Person. A person belongs_to a parent and has_many children, both of which are also instances of Person (unless biology has changed significantly since when I learned it). Now, you want to search your people for people whose grandchildren are named "Jacob" (this is yanked straight from the MetaWhere test suite by the way):

    Person.where(:children => {:children => {:name => 'Jacob'}}).autojoin
    => "SELECT "people".* FROM "people"
       INNER JOIN "people" "children_people"
         ON "children_people"."parent_id" = "people"."id"
       INNER JOIN "people" "children_people_2"
         ON "children_people_2"."parent_id" = "children_people"."id"
       WHERE ("children_people_2"."name" = 'Jacob')"

As you can see, MetaWhere takes care of figuring out which table alias you want your condition to apply to. This works in both directions, so, for example, assuming a Person belongs_to both a father and mother, this will let you get a person whose father is named Abraham and mother is named Sarah:

      :father => {:name => 'Abraham'},
      :mother => {:name => 'Sarah'}

Very readable.

Opt-in Operators

Overriding operators is a very polarizing thing. Some people get really bent out of shape over it, claiming it's a surefire way to earn yourself a one-way ticket to coder hell. Personally, I think that if the overrides suit your problem domain, then go for it. Still, I can respect the concern that an override on Symbol operators stands a good chance of conflicting with someone else's code, so they are off by default in MetaWhere. If you want to use operators in your conditions (such as :created_at >= 1.week.ago instead of :created_at.gteq => 1.week.ago you will need to add MetaWhere.operator_overload! to an initializer in your application.

That's it for now! I hope you're enjoying Rails 3 as much as I am, and I hope that MetaWhere proves to be a useful tool in your Rails 3 development!

comments powered by Disqus