diff --git a/lib/mongoid/extensions/hash.rb b/lib/mongoid/extensions/hash.rb index 086473c2a7..4a187807b8 100644 --- a/lib/mongoid/extensions/hash.rb +++ b/lib/mongoid/extensions/hash.rb @@ -43,7 +43,7 @@ def __consolidate__(klass) real_key = klass.database_field_name(key2) value.delete(key2) if real_key != key2 - value[real_key] = (key == "$rename") ? value2.to_s : mongoize_for(key, klass, real_key, value2) + value[real_key] = value_for(key, klass, real_key, value2) end consolidated[key] ||= {} consolidated[key].update(value) @@ -185,6 +185,24 @@ def to_criteria private + # Get the value for the provided operator, klass, key and value. + # + # This is necessary for special cases like $rename, $addToSet and $push. + # + # @param [ String ] operator The operator. + # @param [ Class ] klass The model class. + # @param [ String | Symbol ] key The field key. + # @param [ Object ] value The original value. + # + # @return [ Object ] Value prepared for the provided operator. + def value_for(operator, klass, key, value) + case operator + when "$rename" then value.to_s + when "$addToSet", "$push" then value.mongoize + else mongoize_for(operator, klass, operator, value) + end + end + # Mongoize for the klass, key and value. # # @api private diff --git a/spec/mongoid/contextual/mongo_spec.rb b/spec/mongoid/contextual/mongo_spec.rb index 66a7fafaba..cf57de4e1c 100644 --- a/spec/mongoid/contextual/mongo_spec.rb +++ b/spec/mongoid/contextual/mongo_spec.rb @@ -3573,16 +3573,51 @@ context "when the attributes are in the correct type" do - before do - context.update_all("$set" => { name: "Smiths" }) + context "when operation is $set" do + + before do + context.update_all("$set" => { name: "Smiths" }) + end + + it "updates the first matching document" do + expect(depeche_mode.reload.name).to eq("Smiths") + end + + it "updates the last matching document" do + expect(new_order.reload.name).to eq("Smiths") + end end - it "updates the first matching document" do - expect(depeche_mode.reload.name).to eq("Smiths") + context "when operation is $push" do + + before do + depeche_mode.update_attribute(:genres, ["electronic"]) + new_order.update_attribute(:genres, ["electronic"]) + context.update_all("$push" => { genres: "pop" }) + end + + it "updates the first matching document" do + expect(depeche_mode.reload.genres).to eq(["electronic", "pop"]) + end + + it "updates the last matching document" do + expect(new_order.reload.genres).to eq(["electronic", "pop"]) + end end - it "updates the last matching document" do - expect(new_order.reload.name).to eq("Smiths") + context "when operation is $addToSet" do + + before do + context.update_all("$addToSet" => { genres: "electronic" }) + end + + it "updates the first matching document" do + expect(depeche_mode.reload.genres).to eq(["electronic"]) + end + + it "updates the last matching document" do + expect(new_order.reload.genres).to eq(["electronic"]) + end end end