diff --git a/lib/mongoid/extensions/hash.rb b/lib/mongoid/extensions/hash.rb index 862ecf1ba4..fdc773b1f7 100644 --- a/lib/mongoid/extensions/hash.rb +++ b/lib/mongoid/extensions/hash.rb @@ -187,7 +187,7 @@ def to_criteria # Get the value for the provided operator, klass, key and value. # - # This is necessary for special cases like $rename, $addToSet and $push. + # This is necessary for special cases like $rename, $addToSet, $push, $pull and $pop. # # @param [ String ] operator The operator. # @param [ Class ] klass The model class. @@ -198,7 +198,7 @@ def to_criteria def value_for(operator, klass, key, value) case operator when "$rename" then value.to_s - when "$addToSet", "$push" then value.mongoize + when "$addToSet", "$push", '$pull', '$pop' then value.mongoize else mongoize_for(operator, klass, key, value) end end diff --git a/spec/mongoid/contextual/mongo_spec.rb b/spec/mongoid/contextual/mongo_spec.rb index c8d1be576d..92b81447c3 100644 --- a/spec/mongoid/contextual/mongo_spec.rb +++ b/spec/mongoid/contextual/mongo_spec.rb @@ -3746,6 +3746,75 @@ expect(new_order.reload.genres).to eq(["electronic"]) end end + + context "when operation is $pull" do + context "when pulling single element" do + + before do + depeche_mode.update_attribute(:genres, ["electronic", "pop"]) + new_order.update_attribute(:genres, ["electronic", "pop"]) + context.update_all("$pull" => { genres: "electronic" }) + end + + it "updates the first matching document" do + expect(depeche_mode.reload.genres).to eq(["pop"]) + end + + it "updates the last matching document" do + expect(new_order.reload.genres).to eq(["pop"]) + end + end + + context "when pulling based on condition" do + before do + depeche_mode.update_attribute(:genres, ["electronic", "pop", "dance"]) + new_order.update_attribute(:genres, ["electronic", "pop", "dance"]) + context.update_all("$pull" => { genres: { '$in' => ["electronic", "pop"] } }) + end + + it "updates the first matching document" do + expect(depeche_mode.reload.genres).to eq(["dance"]) + end + + it "updates the last matching document" do + expect(new_order.reload.genres).to eq(["dance"]) + end + end + end + + context "when operation is $pop" do + + before do + depeche_mode.update_attribute(:genres, ["pop", "electronic"]) + end + + it "removes first element in array" do + context.update_all("$pop" => { genres: -1 }) + expect(depeche_mode.reload.genres).to eq(["electronic"]) + end + + it "removes last element in array" do + context.update_all("$pop" => { genres: 1 }) + expect(depeche_mode.reload.genres).to eq(["pop"]) + end + end + + context "when operation is $pullAll" do + + before do + depeche_mode.update_attribute(:genres, ["pop", "electronic", "dance", "pop" ]) + new_order.update_attribute(:genres, ["electronic", "pop", "electronic", "dance"]) + context.update_all("$pullAll" => { genres: ["pop", "electronic"] }) + end + + it "updates the first matching document" do + expect(depeche_mode.reload.genres).to eq(["dance"]) + end + + it "updates the last matching document" do + expect(new_order.reload.genres).to eq(["dance"]) + end + end end context 'when using aliased field names' do