diff --git a/lib/mongoid/atomic.rb b/lib/mongoid/atomic.rb index 4474df6333..80098d70b7 100644 --- a/lib/mongoid/atomic.rb +++ b/lib/mongoid/atomic.rb @@ -385,11 +385,35 @@ def touch_atomic_updates(field = nil) updates = atomic_updates return {} unless atomic_updates.key?("$set") touches = {} + wanted_keys = %w(updated_at u_at) + # TODO this permits field to be passed as an empty string in which case + # it is ignored, get rid of this behavior. + if field.present? + wanted_keys << field.to_s + end updates["$set"].each_pair do |key, value| - key_regex = /updated_at|u_at#{"|" + field if field.present?}/ - touches.merge!({ key => value }) if key =~ key_regex + if wanted_keys.include?(key.split('.').last) + touches.update(key => value) + end end { "$set" => touches } end + + # Returns the $set atomic updates affecting the specified field. + # + # @param [ String ] field The field name. + # + # @api private + def set_field_atomic_updates(field) + updates = atomic_updates + return {} unless atomic_updates.key?("$set") + sets = {} + updates["$set"].each_pair do |key, value| + if key.split('.').last == field + sets.update(key => value) + end + end + { "$set" => sets } + end end end diff --git a/lib/mongoid/touchable.rb b/lib/mongoid/touchable.rb index 94c81a9321..19efb9b325 100644 --- a/lib/mongoid/touchable.rb +++ b/lib/mongoid/touchable.rb @@ -41,6 +41,16 @@ def touch(field = nil) # _association.inverse_association.options but inverse_association # seems to not always/ever be set here. See MONGOID-5014. _parent.touch + + if field + # If we are told to also touch a field, perform a separate write + # for that field. See MONGOID-5136. + # In theory we should combine the writes, which would require + # passing the fields to be updated to the parents - MONGOID-5142. + sets = set_field_atomic_updates(field) + selector = atomic_selector + _root.collection.find(selector).update_one(positionally(selector, sets), session: _session) + end else # If the current document is not embedded, it is composition root # and we need to persist the write here. diff --git a/spec/mongoid/touchable_spec.rb b/spec/mongoid/touchable_spec.rb index 0cc6466cb1..d73c25c8af 100644 --- a/spec/mongoid/touchable_spec.rb +++ b/spec/mongoid/touchable_spec.rb @@ -132,6 +132,24 @@ include_examples 'updates the child' include_examples 'updates the parent when :touch is true' include_examples 'updates the parent when :touch is not set' + + context 'when also updating an additional field' do + it 'persists the update to the additional field' do + entrance + update_time + entrance.touch(:last_used_at) + + entrance.reload + building.reload + + # This is the assertion we want. + entrance.last_used_at.should == update_time + + # Check other timestamps for good measure. + entrance.updated_at.should == update_time + building.updated_at.should == update_time + end + end end context "when the document is referenced" do diff --git a/spec/mongoid/touchable_spec_models.rb b/spec/mongoid/touchable_spec_models.rb index deef80e88b..acb1f4d932 100644 --- a/spec/mongoid/touchable_spec_models.rb +++ b/spec/mongoid/touchable_spec_models.rb @@ -16,6 +16,8 @@ class Entrance include Mongoid::Timestamps embedded_in :building + + field :last_used_at, type: Time end class Floor