Skip to content
This repository
Browse code

ActiveRecord::Relation#pluck method

  • Loading branch information...
commit a382d60f6abc94b6a965525872f858e48abc00de 1 parent d9c2882
Bogdan Gusiev authored November 30, 2011
9  activerecord/CHANGELOG.md
Source Rendered
... ...
@@ -1,5 +1,14 @@
1 1
 ## Rails 3.2.0 (unreleased) ##
2 2
 
  3
+
  4
+*   Implemented ActiveRecord::Relation#pluck method
  5
+
  6
+    Method returns Array of column value from table under ActiveRecord model
  7
+        
  8
+        Client.pluck(:id)
  9
+
  10
+    *Bogdan Gusiev*
  11
+
3 12
 *   Automatic closure of connections in threads is deprecated.  For example
4 13
     the following code is deprecated:
5 14
 
2  activerecord/lib/active_record/associations/collection_proxy.rb
@@ -39,7 +39,7 @@ class CollectionProxy # :nodoc:
39 39
       instance_methods.each { |m| undef_method m unless m.to_s =~ /^(?:nil\?|send|object_id|to_a)$|^__|^respond_to|proxy_/ }
40 40
 
41 41
       delegate :group, :order, :limit, :joins, :where, :preload, :eager_load, :includes, :from,
42  
-               :lock, :readonly, :having, :to => :scoped
  42
+               :lock, :readonly, :having, :pluck, :to => :scoped
43 43
 
44 44
       delegate :target, :load_target, :loaded?, :scoped,
45 45
                :to => :@association
2  activerecord/lib/active_record/base.rb
@@ -449,7 +449,7 @@ class << self # Class methods
449 449
       delegate :select, :group, :order, :except, :reorder, :limit, :offset, :joins,
450 450
                :where, :preload, :eager_load, :includes, :from, :lock, :readonly,
451 451
                :having, :create_with, :uniq, :to => :scoped
452  
-      delegate :count, :average, :minimum, :maximum, :sum, :calculate, :to => :scoped
  452
+      delegate :count, :average, :minimum, :maximum, :sum, :calculate, :pluck, :to => :scoped
453 453
 
454 454
       def inherited(child_class) #:nodoc:
455 455
         # force attribute methods to be higher in inheritance hierarchy than other generated methods
17  activerecord/lib/active_record/relation/calculations.rb
@@ -166,6 +166,23 @@ def calculate(operation, column_name, options = {})
166 166
       0
167 167
     end
168 168
 
  169
+    # This method is designed to perform select by a single column as direct SQL query
  170
+    # Returns <tt>Array</tt> with values of the specified column name
  171
+    # The values has same data type as column. 
  172
+    #
  173
+    # Examples:
  174
+    #
  175
+    #   Person.pluck(:id) # SELECT people.id FROM people
  176
+    #   Person.uniq.pluck(:role) # SELECT DISTINCT role FROM people
  177
+    #   Person.where(:confirmed => true).limit(5).pluck(:id)
  178
+    #
  179
+    def pluck(column_name)
  180
+      scope = self.select(column_name)
  181
+      self.connection.select_values(scope.to_sql).map! do |value|
  182
+        type_cast_using_column(value, column_for(column_name))
  183
+      end
  184
+    end
  185
+
169 186
     private
170 187
 
171 188
     def perform_calculation(operation, column_name, options = {})
25  activerecord/test/cases/calculations_test.rb
... ...
@@ -1,5 +1,6 @@
1 1
 require "cases/helper"
2 2
 require 'models/company'
  3
+require "models/contract"
3 4
 require 'models/topic'
4 5
 require 'models/edge'
5 6
 require 'models/club'
@@ -446,4 +447,28 @@ def test_distinct_is_honored_when_used_with_count_operation_after_group
446 447
     distinct_authors_for_approved_count = Topic.group(:approved).count(:author_name, :distinct => true)[true]
447 448
     assert_equal distinct_authors_for_approved_count, 2
448 449
   end
  450
+
  451
+  def test_pluck
  452
+    assert_equal [1,2,3,4], Topic.order(:id).pluck(:id)
  453
+  end
  454
+
  455
+  def test_pluck_type_cast
  456
+    topic = topics(:first)
  457
+    relation = Topic.where(:id => topic.id)
  458
+    assert_equal [ topic.approved ], relation.pluck(:approved)
  459
+    assert_equal [ topic.last_read ], relation.pluck(:last_read)
  460
+    assert_equal [ topic.written_on ], relation.pluck(:written_on)
  461
+
  462
+  end
  463
+
  464
+  def test_pluck_and_uniq
  465
+    assert_equal [50, 53, 55, 60], Account.order(:credit_limit).uniq.pluck(:credit_limit)
  466
+  end
  467
+
  468
+  def test_pluck_in_relation
  469
+    company = Company.first
  470
+    contract = company.contracts.create!
  471
+    assert_equal [contract.id], company.contracts.pluck(:id)
  472
+  end
  473
+
449 474
 end
9  railties/guides/source/active_record_querying.textile
Source Rendered
@@ -1146,6 +1146,15 @@ h3. +select_all+
1146 1146
 Client.connection.select_all("SELECT * FROM clients WHERE id = '1'")
1147 1147
 </ruby>
1148 1148
 
  1149
+h3. +pluck+
  1150
+
  1151
+<tt>pluck</tt> can be used to query single column from table under model. It accepts column name as argument and returns Array of values of the specified column with corresponding data type.
  1152
+
  1153
+<ruby>
  1154
+Client.where(:active => true).pluck(:id) # SELECT id FROM clients WHERE clients.active
  1155
+Client.uniq.pluck(:role) # SELECT DISTINCT role FROM clients
  1156
+</ruby>
  1157
+
1149 1158
 h3. Existence of Objects
1150 1159
 
1151 1160
 If you simply want to check for the existence of the object there's a method called +exists?+. This method will query the database using the same query as +find+, but instead of returning an object or collection of objects it will return either +true+ or +false+.

46 notes on commit a382d60

jtmkrueger

+1 This looks super useful!

Adam Hawkins

+1 agree

Kennon Ballou

As someone who has literally had to write "Order.select("id").collect(&:id)" four times today, this is a big +1 from me!

Bryce Thornton

I've wanted a method like this for so long! I'm always writing little bits of code to do this. Thanks!

Philippe Creux

+1 !

Gimi Liang

Agree with @kennon, no SomeThing.select('col').map(&:col) any more!

Christopher Jones

+1 Very useful!

Collin Shepherd

Can't try this out at the the moment, so here's a question: How would this compare to https://github.com/ernie/valium ? Same thing / functionality?

Ben Scheirman

pluck yeah ;)

Paweł Gościcki

+1 really useful piece of code

Andrew Kalek

+1 ;)

Guilherme da Silva Mello

Why not several columns? Wouldn't it be as useful? or am I missing something here?

Marty Zalega

+2 because it's that good

Pierre Nespo

Thanks a lot!

Dmitriy Kiriyenko

Does it work with serialization like ernie/valium?

Collin Shepherd

@dmitriy-kiriyenko Since no one answered my previous question about how it compares to valium, and judging by the other comments, I'd say no one here actually knew about valium.

Gimi Liang

@cvshepherd just got to know valium, and I think it's better.

defsdoor

is "pluck" really the right name for this ?

Alexandre de Oliveira

+1: Is "pluck" really the right name for this ?

Matt White

Seriously, I would propose 'project' as a better name

defsdoor

'project' is just as un-obvious. For a method that returns an array of columns I'd expect the method name to have something to do with getting an array of columns....

Something like 'selective_columns' and extend it to return optionally more than one column.

Matt White

It's not un-obvious if you've heard of an SQL projection

Dmitriy Kiriyenko

values_at? =)

Dmitriy Kiriyenko

But I definitely like project. +1 for project.
We'll have another reserved word to avoid in business code.

Alexandre de Oliveira

+1, but I doubt it'll be changed anyway.

Cameron Knight

Being able to "pluck" multiple columns would be quite useful as well.

Mario Visic

+1 although I think values_at (what valium uses) would be a better name than pluck.

Colin MacKenzie IV

+1, but I actually like "pluck". It's used pretty rarely in everyday speech, and to me seeing it is odd enough that I'll remember it as a method name. -- like "tap", the other most awesome method name ever! Plus, "pluck" is such a fun word!

Leo.liang

:+1: , I actually think values_at is better than pluck or project

Ali Anwar

+1 multiple columns

Marcel Morgan

+1 multiple columns

Jeremy Kemper
Owner

bike shed

Matt White

Green??? What a stupid color for a bikeshed.

But seriously, project is a better name ;)

Dmitriy Kiriyenko

@whitethunder, I told them, but they don't trust me. Seriously, project is an excellent name. More, I'm looking forward for a method like "user" or "company". Also a great idea would be methods "topic", "post" and "comment". =)

Ernie Miller

For those who were asking, it looks like this patch handles serialized columns, because Column#type_cast decodes encoded columns in current master.

This wasn't the case in 3-0-stable, which is why Valium's implementation is (only slightly) more involved.

I agree with @jeremy, though -- this is a whole lot of discussion for a very simple change. In fact, I'd have submitted Valium's implementation as a patch long ago if I'd thought it had a chance to be accepted. One of those things where it was so ridiculously simple that I figured there was a reason it wasn't part of the AR API already. ;)

Ernie Miller

Hmm. I take back my comment about working with serialization. It looks like the only place that a Column's coder is being set in the current AR code is in 3 tests in column_definition_test at this point, unless I missed something. It doesn't look like SchemaCache would be the right place to handle this, either.

Anyway, I have a rough version of Valium's take on this ported to a Rails 3.2 patch and passing all but the serialization tests (due to the issue mentioned). I can work out the remaining issues there and submit a value(s)_of implementation for Rails 3.2 if the core team is interested.

Jeremy Kemper
Owner

@ernie Cool, yeah, let's see it. Wish we'd known you had Valium already implemented, sorry about that. Pull request came in; didn't look for prior art. Thanks for pitching in in any case.

Ernie Miller

@jeremy pushing it up now -- thanks!

Ernie Miller

For those visiting this thread: See #3871 for the pull request with alternate implementation supporting serialization, multiple values, etc.

Aaron Patterson
Owner

unicorn

Ernie Miller

@tenderlove unicorns, rainbows and ponies would be the "etc" part

Cristiano Betta

Love it! +1

Fabricio Quagliariello

wow, amazing +1

Please sign in to comment.
Something went wrong with that request. Please try again.