map(&:id) work but not pluck(:id)

2019-06-03 07:47发布

In part of my code I need to grab users's id but pluck doesn't grab them. Only map(&:id) can do it. I was wondering why.

I've wrote a quick and dirty block of code to find what's happen

  def remove_users user_ids
    p users_to_remove.class
    users_to_remove = self.users.where(id: user_ids)
    if users_to_remove.present?
      self.users -= users_to_remove
      self.save

      p users_to_remove.class
      p users_to_remove
      Rails::logger.info "\n#{users_to_remove}\n"
      users_to_remove_map = users_to_remove.map(&:id)
      p users_to_remove_map
      Rails::logger.info "\nmap id: #{users_to_remove_map}\n"
      users_to_remove_pluck = users_to_remove.pluck(:id)
      p users_to_remove_pluck
      Rails::logger.info "\npluck id: #{users_to_remove_pluck}\n"
      #...
    end
    self.user_ids
  end

Who return in my test.log

#<User::ActiveRecord_AssociationRelation:0x007fb0f9c64da8>


map id: [144004]

   (0.3ms)  SELECT "users"."id" FROM "users" INNER JOIN "groups_users" ON "users"."id" = "groups_users"."user_id" WHERE "groups_users"."group_id" = $1 AND "users"."id" IN (144004, 144005)  [["group_id", 235819]]

pluck id: []

And in my test

User::ActiveRecord_AssociationRelation
User::ActiveRecord_AssociationRelation
#<ActiveRecord::AssociationRelation [#<User id: 144004, created_at: "2015-08-06 08:55:11", updated_at: "2015-08-06 08:55:11", email: "user_2@test.com", token: "rpf5fqf5bs1ofme6aq66fwtcz", reset_password_token: nil, sign_in_count: 0, current_sign_in_at: nil, last_sign_in_at: nil, current_sign_in_ip: nil, last_sign_in_ip: nil, disabled: false, first_name: "John_2", last_name: "Rivers_4", is_owner: false, organisation_id: 235826, encrypted_password: "$2a$04$8ev1j...f6ICL.ezS....", reset_password_sent_at: nil, default_language: nil, uid: nil, api_key: "rmhh...noyn">]>
[144004]
[]

The strange thing is. I have user with id. map can get them. pluck not.

I don't understand sql log also. How can I get map id result without any select in sql log? Caching ?

2条回答
一夜七次
2楼-- · 2019-06-03 08:33

pluck doesnt work on an array, it works on an ActiveRecord::Relation, it's goal is to avoid to make a full query to only get the ids.

Once you've retrieved all columns from db, you can just map what you need.

You create the array when you do self.users -= users_to_remove, or maybe even when you do .present?, since you should use .exists?

查看更多
Juvenile、少年°
3楼-- · 2019-06-03 08:41

This line:

users_to_remove = self.users.where(id: user_ids)

Doesn't fire off SQL query immediately. It sends the request whenever you need some details of these users. And it caches the result in SQL cache (so when the same request goes to DB again, it intercepted by Rails and never reaches the database).

So when you call:

users_to_remove.map(&:id)

It uses that cached result. But when you use

users_to_remove.pluck(:id)

It re-fetches the result, because the actual SQL query differs. With #map it is SELECT * FROM ..., and with #pluck it's SELECT id FROM.... And when query reaches the database, IDs doesn't belong to 'self' any longer (you deleted them right before that), so they aren't returned.

查看更多
登录 后发表回答