Skip to content

Commit 687df5e

Browse files
johnnyshieldsp
andauthored
MONGOID-5140 Atomic #unset method should allow Hash args (in addition to current String/Symbol/Array) (#5034)
* The native MongoDB interface for $unset uses a Hash object rather than an array. Therefore, for consistency, MongoDB should allow a Hash to be the argument to `.unset`. ``` { $unset: { <field1>: "", ... } } ``` According to [MongoDB's docs](https://docs.mongodb.com/manual/reference/operator/update/unset/): > The specified value in the $unset expression (i.e. "") does not impact the operation. So I'm simply taking the array keys, even if the value is nil or false. * clarify the docstring * use map instead of collect * test nil value Co-authored-by: shields <[email protected]> Co-authored-by: Oleg Pudeyev <[email protected]>
1 parent f273227 commit 687df5e

File tree

2 files changed

+71
-27
lines changed

2 files changed

+71
-27
lines changed

lib/mongoid/contextual/atomic.rb

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,13 +173,18 @@ def set(sets)
173173
# @example Unset the field on the matches.
174174
# context.unset(:name)
175175
#
176-
# @param [ String, Symbol, Array ] args The name of the fields.
176+
# @param [ String | Symbol | Array<String|Symbol> | Hash ] args
177+
# The name(s) of the field(s) to unset.
178+
# If a Hash is specified, its keys will be used irrespective of what
179+
# each key's value is, even if the value is nil or false.
177180
#
178181
# @return [ nil ] Nil.
179182
#
180183
# @since 3.0.0
181184
def unset(*args)
182-
fields = args.__find_args__.collect { |f| [database_field_name(f), true] }
185+
fields = args.map { |a| a.is_a?(Hash) ? a.keys : a }
186+
.__find_args__
187+
.map { |f| [database_field_name(f), true] }
183188
view.update_many("$unset" => Hash[fields])
184189
end
185190

spec/mongoid/contextual/atomic_spec.rb

Lines changed: 64 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -801,35 +801,37 @@
801801
context.unset(:name)
802802
end
803803

804-
it "unsets the first existing field" do
805-
expect(depeche_mode.reload.name).to be_nil
806-
end
807-
808-
it "unsets the last existing field" do
809-
expect(new_order.reload.name).to be_nil
804+
it "unsets the fields from all documents" do
805+
depeche_mode.reload
806+
new_order.reload
807+
expect(depeche_mode.name).to be_nil
808+
expect(depeche_mode.years).to_not be_nil
809+
expect(new_order.name).to be_nil
810+
expect(new_order.years).to_not be_nil
810811
end
811812
end
812813

813814
context "when the field is aliased" do
814-
815815
before do
816816
context.unset(:years)
817817
end
818818

819-
it "unsets the first existing field" do
820-
expect(depeche_mode.reload.years).to be_nil
821-
end
822-
823-
it "unsets the last existing field" do
824-
expect(new_order.reload.years).to be_nil
819+
it "unsets the fields from all documents" do
820+
depeche_mode.reload
821+
new_order.reload
822+
expect(depeche_mode.name).to_not be_nil
823+
expect(depeche_mode.years).to be_nil
824+
expect(new_order.name).to_not be_nil
825+
expect(new_order.years).to be_nil
825826
end
826827
end
827828
end
828829

829830
context "when unsetting multiple fields" do
830831

831832
let!(:new_order) do
832-
Band.create(name: "New Order", genres: [ "electro", "dub" ], years: 10)
833+
Band.create(name: "New Order", genres: %w[electro dub], years: 10,
834+
likes: 200, rating: 4.3, origin: 'Space')
833835
end
834836

835837
let(:criteria) do
@@ -846,12 +848,13 @@
846848
context.unset(:name, :genres)
847849
end
848850

849-
it "unsets name field" do
850-
expect(new_order.reload.name).to be_nil
851-
end
852-
853-
it "unsets genres field" do
854-
expect(new_order.reload.genres).to be_nil
851+
it "unsets the specified fields" do
852+
new_order.reload
853+
expect(new_order.name).to be_nil
854+
expect(new_order.genres).to be_nil
855+
expect(new_order.years).to_not be_nil
856+
expect(new_order.likes).to_not be_nil
857+
expect(new_order.rating).to_not be_nil
855858
end
856859
end
857860

@@ -861,12 +864,46 @@
861864
context.unset(:name, :years)
862865
end
863866

864-
it "unsets the unaliased field" do
865-
expect(new_order.reload.name).to be_nil
867+
it "unsets the specified fields" do
868+
new_order.reload
869+
expect(new_order.name).to be_nil
870+
expect(new_order.genres).to_not be_nil
871+
expect(new_order.years).to be_nil
872+
expect(new_order.likes).to_not be_nil
873+
expect(new_order.rating).to_not be_nil
874+
end
875+
end
876+
877+
context "when using Hash arguments" do
878+
879+
before do
880+
context.unset({ years: true, likes: "" }, { rating: false, origin: nil })
881+
end
882+
883+
it "unsets the specified fields" do
884+
new_order.reload
885+
expect(new_order.name).to_not be_nil
886+
expect(new_order.genres).to_not be_nil
887+
expect(new_order.years).to be_nil
888+
expect(new_order.likes).to be_nil
889+
expect(new_order.rating).to be_nil
890+
expect(new_order.origin).to be_nil
891+
end
892+
end
893+
894+
context "when mixing argument types" do
895+
896+
before do
897+
context.unset(:name, [:years], { likes: "" }, { rating: false })
866898
end
867899

868-
it "unsets the aliased field" do
869-
expect(new_order.reload.years).to be_nil
900+
it "unsets the specified fields" do
901+
new_order.reload
902+
expect(new_order.name).to be_nil
903+
expect(new_order.genres).to_not be_nil
904+
expect(new_order.years).to be_nil
905+
expect(new_order.likes).to be_nil
906+
expect(new_order.rating).to be_nil
870907
end
871908
end
872909
end
@@ -895,7 +932,9 @@
895932
end
896933

897934
it "unsets the unaliased field" do
898-
expect(depeche_mode.reload.name).to be_nil
935+
depeche_mode.reload
936+
expect(depeche_mode.name).to be_nil
937+
expect(depeche_mode.years).to_not be_nil
899938
end
900939
end
901940
end

0 commit comments

Comments
 (0)