Adding custom SELECT in ActiveRecord 3 Adding custom SELECT in ActiveRecord 3 postgresql postgresql

Adding custom SELECT in ActiveRecord 3


Except for the default SELECT models.*, ActiveRecord normally adds SELECT clauses, instead of replacing them.

> Project.select(:id).select(:name)=> Project Load (0.5ms)  SELECT id, name FROM "projects"

Obviously, this doesn't help when you want "the default SELECT, plus any others I ask for."

You might try a select_star scope

scope :select_star, -> { select("models.*") }scope :select_star, -> { select(self.arel_table[Arel.star]) } # Arel version, if feeling adventurous

which you could use in places where you want all the columns?

> Model.with_distance(point).select_star=> SELECT ST_DISTANCE(models.point_in_model, ST_GeographyFromText('0,0')) AS distance, models.* FROM ...


I have the same issue with select behaviour. Let's conclude these bad spots:

  • by default, when query is being constructed and we never run select before, the Arel.star is used to fetch all columns.
  • Some times we don't want ActiveRecord/ARel to add "*", ex. we are building some intermediate query or query for statistics, and that star just increases a work on our database (and can also affect the ruby callbacks that run on presence of attributes etc).

I think one of solutions (tested on Rails 4.2.4, that also can be easily applied for Rails 3) is to add the following code to your model or ActiveRecord::Base:

app/models/item.rb

...# include "items".* by defaultdefault_scope ->{select(arel_table[Arel.star])}# when we dont want to use all columnsscope :no_columns, ->{except(:select)}...

Now, for example we have the following scope in the app/models/item.rb

...scope :some_business_logic_scope, ->{    # doing some big query    joins(...).    where(...).    select(...) # some virtual attributes    # we dont need to run the following line any more    # select(self.arel_table[Arel.star])}...

Usage

Item.select(:id) # selects all columnsItem.no_columns.select(:id) # selects only column idItem.no_columns.some_business_logic_scope # selects only required columnsItem.some_business_logic_scope # selects all attributes including those added by some_business_logic_scope