Question

In my Rails 4 app, I have the following models:

class Person < ActiveRecord::Base
    has_many :addresses
  end

class Address < ActiveRecord::Base
  belongs_to :person
  belongs_to :city
end

class City < ActiveRecord::Base
  has_many :addresses
end

I'm using the :includes function to return query result into one variable:

Address.includes(:person, :city).where("person_id = 1")

It works as expected, except that I do not want the query to return every single column.

Here's what I've tried:

  • use select and specify table name and column names explicitly, e.g. "city.name", but Rails generates a big query with outer joins, that can be very costly, especially when there are lots of concurrent requests, so prefer a better solution.
  • don't want to hard code complete and raw SQL statements, because of maintenance issue later on
  • create a new "dummy" belongs_to relationship like in Address: belongs_to :city_select_columns, -> { select('name') }, :class => 'City', but that doesn't work (actually I'm not sure if that select is even supported, only came across documentation about where so far).
  • maybe define scope in City and Person? but I'm not sure how it should be defined or if it'd make sense to do it this way

Suggestions? Thanks

Was it helpful?

Solution

Have you tried this?

class Person < ActiveRecord::Base
    has_many :addresses
    has_many :cities, :through => :addresses
end

class Address < ActiveRecord::Base
  belongs_to :person
  belongs_to :city
end

class City < ActiveRecord::Base
  has_many :addresses
end

Then:

Person.find(1).cities.pluck(:name)

Looks like this generates an INNER JOIN but with indexes it shouldn't be too costly?

OTHER TIPS

Did you try select?

Address.select(<output_columns>).includes(:person, :city).where("person_id = 1")

Could not find a good query method using Rails' API, I ended up writing a raw inner join SQL, then call ActiveRecord::Base.connection.execute to run it.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top