9

I'm trying to select only certain columns using eager_load, but I'm facing the problem that it cancels my 'select'.

Model:

class Timeline < ActiveRecord::Base
    belongs_to :timeline_category, foreign_key: :timeline_category_id
    belongs_to :user, foreign_key: :user_id
    scope :with_relations, -> { eager_load(:timeline_category).eager_load(:user).order(created_at: :desc) 
end

Query:

Timeline.select('timelines.*, users.username, timeline_categories.icon').eager_load(:timeline_category).eager_load(:user)

I tried also:

Timeline.select('timelines.*, users.username, timeline_categories.icon').with_relations

For some reason, it keeps selecting all columns of all 3 tables. How can I fix it?

developer033
  • 24,267
  • 8
  • 82
  • 108
  • I've had a similar issue, as I remember, you can only add custom columns, but you can't fetch them selectively, as method always fetches all of them. – potashin Jun 20 '16 at 15:13

3 Answers3

4

Rails 5 introduced the left_outer_joins method. So it's finally possible:

Timeline.left_outer_joins(:timeline_category, :user)
        .select('timelines.*, users.username, timeline_categories.icon')
Fabian Winkler
  • 1,401
  • 16
  • 24
0

I'm not sure if it is possible or not, but you could do something like this:

relation = TimeLine.joins(:timeline_category, :user).select('users.username AS username, timeline_categories.icon AS categories_icon')

and then get those fields with relation.each{ |timeline| puts "username: #{timeline.username} and icon: #{timeline.categories_icon}" }

xamenrax
  • 1,724
  • 3
  • 27
  • 47
0

Have wanted a way to trim the SELECT list for #includes and #eager_load for quite a while now. Finally today sat down and figured it out, and it's been released as a part of a data-related gem I maintain, The Brick.

By overriding ActiveRecord::Associations::JoinDependency.apply_column_aliases() like this then when you add a .select(...) then it can act as a filter to choose which column aliases get built out.

With gem 'brick' loaded, in order to enable this selective behaviour, add the special column name :_brick_eager_load as the first entry in your .select(...), which turns on the filtering of columns while the aliases are being built out. Here's an example:

Employee.includes(orders: :order_details)
        .references(orders: :order_details)
        .select(:_brick_eager_load,
                'employees.first_name', 'orders.order_date', 'order_details.product_id')

Because foreign keys are essential to have everything be properly associated, they are automatically added, so you do not need to include them in your select list.

Would welcome any feedback to this approach -- hope it can save you both query time and some RAM!

Lorin Thwaits
  • 301
  • 1
  • 3