Permalink
Browse files

Refactor pluck. [ close #2337 ]

  • Loading branch information...
1 parent 441abe2 commit 6465ed0340de9a19f3ef1143f50a7fa507dcdcc5 @durran durran committed Sep 7, 2012
Showing with 130 additions and 34 deletions.
  1. +6 −0 CHANGELOG.md
  2. +19 −0 lib/mongoid/contextual/mongo.rb
  3. +22 −15 lib/mongoid/finders.rb
  4. +51 −0 spec/mongoid/criteria_spec.rb
  5. +32 −19 spec/mongoid/finders_spec.rb
View
@@ -20,6 +20,12 @@ For instructions on upgrading to newer versions, visit
Band.where(name: "Depeche Mode").first_or_initialize
Band.where(name: "Tool").first_or_initialize(active: true)
+* Added `Model.pluck` and `Criteria#pluck` similar to Active Record's, which
+ returns an array of values for the provided field. (Jason Lee)
+
+ Band.where(name: "Depeche Mode").pluck(:_id)
+ Band.where(name: "Tool").pluck(:likes)
+
* \#2317 Added `Document.first_or_create` and `Criteria#first_or_create`.
This will return the first matching document or create one with additional
attributes if one does not exist. (incorvia)
@@ -275,6 +275,25 @@ def map_reduce(map, reduce)
MapReduce.new(collection, criteria, map, reduce)
end
+ # Pluck the single field values from the database. Will return duplicates
+ # if they exist and only works for top level fields.
+ #
+ # @example Pluck a field.
+ # context.pluck(:_id)
+ #
+ # @note This method will return the raw db values - it performs no custom
+ # serialization.
+ #
+ # @param [ String, Symbol ] field The field to pluck.
+ #
+ # @return [ Array<Object> ] The plucked values.
+ #
+ # @since 3.1.0
+ def pluck(field)
+ normalized = field.to_s
+ query.select(normalized => 1).map{ |doc| doc[normalized] }.compact
+ end
+
# Skips the provided number of documents.
#
# @example Skip the documents.
View
@@ -7,9 +7,28 @@ module Finders
extend Origin::Forwardable
select_with :with_default_scope
- delegate :aggregates, :avg, :each, :each_with_index, :extras, :find_and_modify,
- :first_or_create, :first_or_create!, :first_or_initialize, :for_js, :includes,
- :map_reduce, :max, :min, :sum, :update, :update_all, to: :with_default_scope
+
+ # These are methods defined on the criteria that should also be accessible
+ # directly from the the class level.
+ delegate \
+ :aggregates,
+ :avg,
+ :each,
+ :each_with_index,
+ :extras,
+ :find_and_modify,
+ :first_or_create,
+ :first_or_create!,
+ :first_or_initialize,
+ :for_js,
+ :includes,
+ :map_reduce,
+ :max,
+ :min,
+ :pluck,
+ :sum,
+ :update,
+ :update_all, to: :with_default_scope
# Returns a count of records in the database.
# If you want to specify conditions use where.
@@ -129,17 +148,6 @@ def first
def last
with_default_scope.last
end
-
- # Pick up values array by a column
- #
- # @example Pick up all email from User model
- # User.pluck(:email)
- #
- # @return [ Array ] The all values in column
- def pluck(column_name)
- column_name = column_name.to_sym
- with_default_scope.all.only([column_name]).map(&column_name)
- end
protected
@@ -155,6 +163,5 @@ def pluck(column_name)
def find_or(method, attrs = {}, &block)
where(attrs).first || send(method, attrs, &block)
end
-
end
end
@@ -3593,6 +3593,57 @@ class C
end
end
+ describe "#pluck" do
+
+ let!(:depeche) do
+ Band.create(name: "Depeche Mode", likes: 3)
+ end
+
+ let!(:tool) do
+ Band.create(name: "Tool", likes: 3)
+ end
+
+ let!(:photek) do
+ Band.create(name: "Photek", likes: 1)
+ end
+
+ context "when the criteria matches" do
+
+ context "when there are no duplicate values" do
+
+ let(:plucked) do
+ Band.where(:name.exists => true).pluck(:name)
+ end
+
+ it "returns the values" do
+ plucked.should eq([ "Depeche Mode", "Tool", "Photek" ])
+ end
+ end
+
+ context "when there are duplicate values" do
+
+ let(:plucked) do
+ Band.where(:name.exists => true).pluck(:likes)
+ end
+
+ it "returns the duplicates" do
+ plucked.should eq([ 3, 3, 1 ])
+ end
+ end
+ end
+
+ context "when the criteria does not match" do
+
+ let(:plucked) do
+ Band.where(name: "New Order").pluck(:_id)
+ end
+
+ it "returns an empty array" do
+ plucked.should be_empty
+ end
+ end
+ end
+
describe "#respond_to?" do
let(:criteria) do
@@ -446,28 +446,41 @@
end
end
end
-
-
- describe "#pluck" do
- it "work" do
- Person.create(title: "Senorita")
- Person.create(title: "Jason")
- Person.pluck(:title).should eq(%w(Senorita Jason))
+
+ describe ".pluck" do
+
+ let!(:depeche) do
+ Band.create(name: "Depeche Mode", likes: 3)
+ end
+
+ let!(:tool) do
+ Band.create(name: "Tool", likes: 3)
end
-
- context "work with Criteria" do
- before do
- Person.create(title: "Senorita")
- Person.create(title: "Jason", pets: true)
- Person.create(title: "Tim")
+
+ let!(:photek) do
+ Band.create(name: "Photek", likes: 1)
+ end
+
+ context "when field values exist" do
+
+ let(:plucked) do
+ Band.pluck(:name)
end
-
- it "with where" do
- Person.where(pets: false).pluck(:title).should eq(%w(Senorita Tim))
+
+ it "returns the field values" do
+ plucked.should eq([ "Depeche Mode", "Tool", "Photek" ])
end
-
- it "with limit" do
- Person.where(pets: false).limit(1).pluck(:title).should eq(%w(Senorita))
+ end
+
+ context "when field values do not exist" do
+
+ let(:plucked) do
+ Band.pluck(:follows)
+ end
+
+ it "returns an empty array" do
+ p plucked
+ plucked.should be_empty
end
end
end

0 comments on commit 6465ed0

Please sign in to comment.