ActiveRecord :includes - how to use map with loade

2019-04-12 08:25发布

问题:

I have a small rails app, and I'm trying to get some order statistics. So I have an Admin model, and an Order model, with one-to-many association.

class Admin < ActiveRecord::Base
  attr_accessible :name
  has_many :orders
class Order < ActiveRecord::Base
  attr_accessible :operation
  belongs_to :admin

And I'm trying to get specifical orders using this query:

admins = Admin.where(...).includes(:orders).where('orders.operation = ?', 'new gifts!')

That works just as expected. But when I try to make json using map like that

admins.map {|a| [a.name, a.orders.pluck(:operation)]}

Rails loads orders again using new query, ignoring already loaded objects.

(5.6ms)  SELECT "orders"."operation" FROM "orders" WHERE "orders"."admin_id" = 26
(6.8ms)  SELECT "orders"."operation" FROM "orders" WHERE "orders"."admin_id" = 24
(2.9ms)  SELECT "orders"."operation" FROM "orders" WHERE "orders"."admin_id" = 30
(3.3ms)  SELECT "orders"."operation" FROM "orders" WHERE "orders"."admin_id" = 29
(4.8ms)  SELECT "orders"."operation" FROM "orders" WHERE "orders"."admin_id" = 27
(3.3ms)  SELECT "orders"."operation" FROM "orders" WHERE "orders"."admin_id" = 28
(5.1ms)  SELECT "orders"."operation" FROM "orders" WHERE "orders"."admin_id" = 25

When I try to use loop instead of map, it works as it should:

admins.each do |a|
  p a.orders.pluck(:operation)
end

this code doesn't load all orders, and prints only those loaded in the first query. Is it possible to get the same result using map? What are the drawbacks of using loop instead of map?

回答1:

pluck should always make a new query to database. Not sure why you think it does not happen in an each loop. Maybe you did not see the log because it is in between your prints?

There are 2 possibilities how to avoid additional queries.

  1. Since orders are already loaded because you include them, you can do admins.map {|a| [a.name, a.orders.collect(&:operation)]}

  2. Using joins (see @tihom's comment).

Edit: I just tested the each/ map behavior and it reloads every time as expected.