mongoid 5.0.0 → 5.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +2 -0
  4. data/CHANGELOG.md +54 -2
  5. data/lib/config/locales/en.yml +1 -1
  6. data/lib/mongoid/attributes.rb +1 -1
  7. data/lib/mongoid/clients.rb +7 -4
  8. data/lib/mongoid/clients/options.rb +2 -2
  9. data/lib/mongoid/contextual/aggregable/mongo.rb +2 -1
  10. data/lib/mongoid/contextual/geo_near.rb +1 -1
  11. data/lib/mongoid/contextual/memory.rb +4 -1
  12. data/lib/mongoid/contextual/mongo.rb +4 -5
  13. data/lib/mongoid/document.rb +1 -0
  14. data/lib/mongoid/indexable/specification.rb +3 -5
  15. data/lib/mongoid/indexable/validators/options.rb +7 -1
  16. data/lib/mongoid/matchable/exists.rb +1 -1
  17. data/lib/mongoid/persistable.rb +2 -1
  18. data/lib/mongoid/persistable/creatable.rb +1 -1
  19. data/lib/mongoid/persistable/deletable.rb +1 -1
  20. data/lib/mongoid/persistable/updatable.rb +2 -2
  21. data/lib/mongoid/positional.rb +75 -0
  22. data/lib/mongoid/relations/counter_cache.rb +19 -0
  23. data/lib/mongoid/relations/eager/base.rb +4 -2
  24. data/lib/mongoid/relations/embedded/batchable.rb +10 -3
  25. data/lib/mongoid/relations/proxy.rb +1 -1
  26. data/lib/mongoid/relations/touchable.rb +1 -1
  27. data/lib/mongoid/scopable.rb +6 -5
  28. data/lib/mongoid/selectable.rb +36 -1
  29. data/lib/mongoid/threaded.rb +34 -2
  30. data/lib/mongoid/timestamps/created.rb +1 -2
  31. data/lib/mongoid/timestamps/timeless.rb +19 -2
  32. data/lib/mongoid/timestamps/updated.rb +1 -1
  33. data/lib/mongoid/traversable.rb +1 -1
  34. data/lib/mongoid/version.rb +1 -1
  35. data/lib/rails/generators/mongoid/config/templates/mongoid.yml +3 -1
  36. data/spec/app/models/account.rb +8 -0
  37. data/spec/app/models/answer.rb +2 -0
  38. data/spec/app/models/article.rb +2 -0
  39. data/spec/app/models/author.rb +2 -0
  40. data/spec/app/models/baby.rb +4 -0
  41. data/spec/app/models/book.rb +2 -0
  42. data/spec/app/models/consumption_period.rb +7 -0
  43. data/spec/app/models/exhibitor.rb +1 -0
  44. data/spec/app/models/kaleidoscope.rb +6 -0
  45. data/spec/app/models/kangaroo.rb +4 -0
  46. data/spec/app/models/note.rb +3 -0
  47. data/spec/app/models/page.rb +11 -0
  48. data/spec/app/models/simple.rb +5 -0
  49. data/spec/config/mongoid.yml +3 -1
  50. data/spec/mongoid/atomic/paths_spec.rb +17 -10
  51. data/spec/mongoid/attributes_spec.rb +2 -2
  52. data/spec/mongoid/clients/options_spec.rb +15 -0
  53. data/spec/mongoid/clients_spec.rb +6 -2
  54. data/spec/mongoid/config_spec.rb +3 -2
  55. data/spec/mongoid/contextual/aggregable/mongo_spec.rb +25 -2
  56. data/spec/mongoid/contextual/atomic_spec.rb +6 -6
  57. data/spec/mongoid/contextual/mongo_spec.rb +28 -75
  58. data/spec/mongoid/criteria_spec.rb +54 -0
  59. data/spec/mongoid/fields/standard_spec.rb +1 -1
  60. data/spec/mongoid/fields_spec.rb +1 -1
  61. data/spec/mongoid/indexable/specification_spec.rb +1 -1
  62. data/spec/mongoid/indexable_spec.rb +7 -7
  63. data/spec/mongoid/interceptable_spec.rb +55 -0
  64. data/spec/mongoid/persistable/creatable_spec.rb +19 -0
  65. data/spec/mongoid/persistable/destroyable_spec.rb +50 -0
  66. data/spec/mongoid/persistable/incrementable_spec.rb +56 -4
  67. data/spec/mongoid/persistable/pushable_spec.rb +11 -0
  68. data/spec/mongoid/persistable/savable_spec.rb +20 -2
  69. data/spec/mongoid/positional_spec.rb +221 -0
  70. data/spec/mongoid/query_cache_spec.rb +19 -0
  71. data/spec/mongoid/relations/auto_save_spec.rb +1 -1
  72. data/spec/mongoid/relations/bindings/referenced/many_to_many_spec.rb +1 -1
  73. data/spec/mongoid/relations/counter_cache_spec.rb +64 -11
  74. data/spec/mongoid/relations/eager/has_many_spec.rb +37 -0
  75. data/spec/mongoid/relations/eager_spec.rb +11 -0
  76. data/spec/mongoid/relations/embedded/many_spec.rb +38 -9
  77. data/spec/mongoid/relations/embedded/one_spec.rb +1 -1
  78. data/spec/mongoid/relations/proxy_spec.rb +22 -0
  79. data/spec/mongoid/relations/reflections_spec.rb +1 -1
  80. data/spec/mongoid/scopable_spec.rb +160 -19
  81. data/spec/mongoid/selectable_spec.rb +16 -6
  82. data/spec/mongoid/timestamps/timeless_spec.rb +17 -0
  83. data/spec/mongoid/validatable/uniqueness_spec.rb +17 -0
  84. metadata +40 -5
  85. metadata.gz.sig +3 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: add22f9a2555b543be72f205bad6bc0baa3f00c5
4
- data.tar.gz: e5e3472e31f1f826a4ad7cd0e2607aeef72a4adc
3
+ metadata.gz: 8dff7445e398ee462ff1e26c6baaa5684ee9318d
4
+ data.tar.gz: beb9a64681e530861fcece3129d5347484342344
5
5
  SHA512:
6
- metadata.gz: 6349535d149183db9330fa3469e8ad25a119e3bb78545046f0cf44e1de7c75be8ca3f1785aeb387856d2ab7d50e079a17dc3328c3a8fe4d26cf1332923a3a994
7
- data.tar.gz: 78fecf90c0308eb831ace2022c9acd4ac817b3b4080332a8207c31db0aeafd1d683b4ce30257e99bfe20e1745215d9789d6c88e5a99c05e847d76f77e5f429f9
6
+ metadata.gz: d86170a4c56e57d6d5e11d89003307e9a7c8f2e45d2c75d7cb98ef81781b66a05b69ba8a71701161c0ef800a2284a322beaff08c6a3a70d03014d7313f7f8205
7
+ data.tar.gz: d8615708189aee7700312ac743c8ae63d69d092f45788f4137c9ec3e931985a876d38ec08819359b960e3a64a333c981535b1da8f6405ab55d2dc6d591d15d37
Binary file
@@ -0,0 +1,2 @@
1
+ vʶ�N=�Y-h�m>_�B��LH��2�$a ���\MJc� N�}2����In�F7y.��Q��CR���n�F"bՒ�9���y���rn�is���n�{D6��4.�b�X�s�5�*edA���9�)�9��
2
+ �QG��û!^���i�3R�gK��~�j����$X 7h��V ��#�z �h��d�2���M�@ǷS�6��Ԩ�X���]-R���vw#�3��������F�i�sY��!���
@@ -3,7 +3,59 @@
3
3
  For instructions on upgrading to newer versions, visit
4
4
  [mongoid.org](http://mongoid.org/en/mongoid/docs/upgrading.html).
5
5
 
6
- ## 5.0.0 - Not released
6
+ ## 5.0.1
7
+
8
+ ### Resolved Issues
9
+
10
+ * [MONGOID-3020](https://jira.mongodb.org/browse/MONGOID-3020) Test added to show it's no longer an issue.
11
+ * [MONGOID-3025](https://jira.mongodb.org/browse/MONGOID-3025) Test added to show it's no longer an issue.
12
+ * [MONGOID-3061](https://jira.mongodb.org/browse/MONGOID-3061) No longer an issue.
13
+ * [MONGOID-3073](https://jira.mongodb.org/browse/MONGOID-3073) Test added to show it's no longer an issue.
14
+ * [MONGOID-3085](https://jira.mongodb.org/browse/MONGOID-3085) Test added to show it's no longer an issue.
15
+ * [MONGOID-3101](https://jira.mongodb.org/browse/MONGOID-3101) No longer an issue.
16
+ * [MONGOID-3160](https://jira.mongodb.org/browse/MONGOID-3160) No longer an issue.
17
+ * [MONGOID-3176](https://jira.mongodb.org/browse/MONGOID-3176) No longer an issue.
18
+ * [MONGOID-3214](https://jira.mongodb.org/browse/MONGOID-3214) Test added to show it's no longer an issue.
19
+ * [MONGOID-3296](https://jira.mongodb.org/browse/MONGOID-3296) Add update callback for counter_cache.
20
+ * [MONGOID-3326](https://jira.mongodb.org/browse/MONGOID-3326) Test added to show it's no longer an issue.
21
+ * [MONGOID-3361](https://jira.mongodb.org/browse/MONGOID-3361) No longer an issue.
22
+ * [MONGOID-3365](https://jira.mongodb.org/browse/MONGOID-3365) Test added to show it's no longer an issue.
23
+ * [MONGOID-3402](https://jira.mongodb.org/browse/MONGOID-3402) Apply persistence options to parent.
24
+ * [MONGOID-3524](https://jira.mongodb.org/browse/MONGOID-3524) No longer an issue.
25
+ * [MONGOID-3529](https://jira.mongodb.org/browse/MONGOID-3529) Test exists already showing it's not an issue.
26
+ * [MONGOID-3543](https://jira.mongodb.org/browse/MONGOID-3543) Test exists already showing it's not an issue.
27
+ * [MONGOID-3611](https://jira.mongodb.org/browse/MONGOID-3611) Test added to show it's no longer an issue.
28
+ * [MONGOID-3650](https://jira.mongodb.org/browse/MONGOID-3650) No longer an issue.
29
+ * [MONGOID-3826](https://jira.mongodb.org/browse/MONGOID-3826), [MONGOID-4109](https://jira.mongodb.org/browse/MONGOID-4109) Fix Timelessness leaks.
30
+ * [MONGOID-3946](https://jira.mongodb.org/browse/MONGOID-3946) Test added to show it's no longer an issue.
31
+ * [MONGOID-3969](https://jira.mongodb.org/browse/MONGOID-3969) Test added to show it's no longer an issue.
32
+ * [MONGOID-3971](https://jira.mongodb.org/browse/MONGOID-3971) Not an issue.
33
+ * [MONGOID-3979](https://jira.mongodb.org/browse/MONGOID-3979) Not an issue, tests exist already.
34
+ * [MONGOID-3985](https://jira.mongodb.org/browse/MONGOID-3985) Not an issue.
35
+ * [MONGOID-4078](https://jira.mongodb.org/browse/MONGOID-4078) Behavior is intended.
36
+ * [MONGOID-4079](https://jira.mongodb.org/browse/MONGOID-4079) Not an issue.
37
+ * [MONGOID-4088](https://jira.mongodb.org/browse/MONGOID-4088) Account for sub-document dot notation with #pluck results.
38
+ * [MONGOID-4098](https://jira.mongodb.org/browse/MONGOID-4098) Fixed by a change to the Ruby driver. See RUBY-1029.
39
+ * [MONGOID-4101](https://jira.mongodb.org/browse/MONGOID-4101) Not an issue.
40
+ * [MONGOID-4106](https://jira.mongodb.org/browse/MONGOID-4106) Not an issue.
41
+ * [MONGOID-4110](https://jira.mongodb.org/browse/MONGOID-4110) Not an issue.
42
+ * [MONGOID-4119](https://jira.mongodb.org/browse/MONGOID-4119) Ensure that criteria selector becomes pipeline operator value.
43
+ * [MONGOID-4121](https://jira.mongodb.org/browse/MONGOID-4121) Not an issue.
44
+ * [MONGOID-4123](https://jira.mongodb.org/browse/MONGOID-4123) Fixed as a result of MONGOID-4159.
45
+ * [MONGOID-4125](https://jira.mongodb.org/browse/MONGOID-4125) Make sure none scopes referenced in procs are applied.
46
+ * [MONGOID-4132](https://jira.mongodb.org/browse/MONGOID-4132) Not an issue.
47
+ * [MONGOID-4157](https://jira.mongodb.org/browse/MONGOID-4157) Fixed by version 2.1.2 of the Ruby driver.
48
+ * [MONGOID-4162](https://jira.mongodb.org/browse/MONGOID-4162) Adapt index option mappings to new driver. (@Nielsomat)
49
+ * [MONGOID-3737](https://jira.mongodb.org/browse/MONGOID-3737) Test added to show it's no longer an issue.
50
+ * [MONGOID-3621](https://jira.mongodb.org/browse/MONGOID-3621) Not an issue.
51
+ * [MONGOID-3551](https://jira.mongodb.org/browse/MONGOID-3551) Not an issue.
52
+ * [MONGOID-3696](https://jira.mongodb.org/browse/MONGOID-3696) Test added to show it's no longer an issue.
53
+ * [MONGOID-3858](https://jira.mongodb.org/browse/MONGOID-3858) Test added to show it's no longer an issue.
54
+ * [MONGOID-3672](https://jira.mongodb.org/browse/MONGOID-3672) Not an issue.
55
+ * [MONGOID-4172](https://jira.mongodb.org/browse/MONGOID-4172) Use positional operator only on 1 level deep nesting.
56
+ * Added public cert to repo and sign gem if private key is present
57
+
58
+ ## 5.0.0
7
59
 
8
60
  ### Major Changes (Backwards Incompatible)
9
61
 
@@ -2955,7 +3007,7 @@ child elements.
2955
3007
 
2956
3008
  * \#1394 Fix exists? to work when count is greater than 1. (Nick Hoffman)
2957
3009
 
2958
- * \#1392 Return 0 on aggregation functions where field is nonexistant.
3010
+ * \#1392 Return 0 on aggregation functions where field is nonexistent.
2959
3011
 
2960
3012
  * \#1391 Uniqueness validation now works properly on embedded documents that are
2961
3013
  using primary key definitions.
@@ -447,7 +447,7 @@ en:
447
447
  summary: "You cannot call create or create! through the
448
448
  relation (%{document}) whose parent (%{base}) is
449
449
  not already saved. This would cause the database to be out of sync
450
- since the child could potentially reference a nonexistant parent."
450
+ since the child could potentially reference a nonexistent parent."
451
451
  resolution: "Make sure to only use create or create! when the parent
452
452
  document %{base} is persisted."
453
453
  unsupported_javascript:
@@ -294,7 +294,7 @@ module Mongoid
294
294
  module ClassMethods
295
295
 
296
296
  # Alias the provided name to the original field. This will provide an
297
- # aliased getter, setter, existance check, and all dirty attribute
297
+ # aliased getter, setter, existence check, and all dirty attribute
298
298
  # methods.
299
299
  #
300
300
  # @example Alias the attribute.
@@ -115,10 +115,13 @@ module Mongoid
115
115
  #
116
116
  # @since 3.0.0
117
117
  def mongo_client
118
- name = client_name
119
- client = Clients.with_name(name)
120
- client = client.use(database_name)
121
- client.with(self.persistence_options)
118
+ client = Clients.with_name(client_name)
119
+ opts = self.persistence_options ? self.persistence_options.dup : {}
120
+ if defined?(Mongo::Client::VALID_OPTIONS)
121
+ opts.reject! { |k, v| !Mongo::Client::VALID_OPTIONS.include?(k.to_sym) }
122
+ end
123
+ opts.merge!(database: database_name) unless client.database.name.to_sym == database_name.to_sym
124
+ client.with(opts)
122
125
  end
123
126
  alias :mongo_session :mongo_client
124
127
  deprecate :mongo_session, :mongo_client, 2015, 12
@@ -5,7 +5,7 @@ module Mongoid
5
5
  extend ActiveSupport::Concern
6
6
  extend Gem::Deprecate
7
7
 
8
- # Tell the next persistance operation to store in a specific collection,
8
+ # Tell the next persistence operation to store in a specific collection,
9
9
  # database or client.
10
10
  #
11
11
  # @example Save the current document to a different collection.
@@ -125,7 +125,7 @@ module Mongoid
125
125
  super
126
126
  end
127
127
 
128
- # Tell the next persistance operation to store in a specific collection,
128
+ # Tell the next persistence operation to store in a specific collection,
129
129
  # database or client.
130
130
  #
131
131
  # @example Create a document in a different collection.
@@ -124,7 +124,8 @@ module Mongoid
124
124
  def pipeline(field)
125
125
  db_field = "$#{database_field_name(field)}"
126
126
  pipeline = []
127
- pipeline << { "$match" => criteria.nin(field => nil).selector }
127
+ pipeline << { "$match" => criteria.selector }
128
+ pipeline << { "$match" => criteria.exists(field => true).selector }
128
129
  pipeline << { "$sort" => criteria.options[:sort] } if criteria.options[:sort]
129
130
  pipeline << { "$skip" => criteria.options[:skip] } if criteria.options[:skip]
130
131
  pipeline << { "$limit" => criteria.options[:limit] } if criteria.options[:limit]
@@ -19,7 +19,7 @@ module Mongoid
19
19
  # @since 3.1.0
20
20
  def average_distance
21
21
  average = stats["avgDistance"]
22
- average.nan? ? nil : average
22
+ (average.nil? || average.nan?) ? nil : average
23
23
  end
24
24
 
25
25
  # Iterates over each of the documents in the $geoNear, excluding the
@@ -9,6 +9,7 @@ module Mongoid
9
9
  include Aggregable::Memory
10
10
  include Relations::Eager
11
11
  include Queryable
12
+ include Positional
12
13
 
13
14
  # @attribute [r] root The root document.
14
15
  # @attribute [r] path The atomic path.
@@ -46,7 +47,9 @@ module Mongoid
46
47
  doc.as_document
47
48
  end
48
49
  unless removed.empty?
49
- collection.find(selector).update_one("$pullAll" => { path => removed })
50
+ collection.find(selector).update_one(
51
+ positionally(selector, "$pullAll" => { path => removed })
52
+ )
50
53
  end
51
54
  deleted
52
55
  end
@@ -402,12 +402,11 @@ module Mongoid
402
402
  hash
403
403
  end
404
404
 
405
- view.projection(normalized_select).map do |doc|
406
- if normalized_select.size == 1
407
- doc[normalized_select.keys.first]
408
- else
409
- normalized_select.keys.map { |n| doc[n] }
405
+ view.projection(normalized_select).reduce([]) do |plucked, doc|
406
+ values = normalized_select.keys.map do |n|
407
+ n =~ /\./ ? doc[n.partition('.')[0]] : doc[n]
410
408
  end
409
+ plucked << (values.size == 1 ? values.first : values)
411
410
  end
412
411
  end
413
412
 
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ require "mongoid/positional"
2
3
  require "mongoid/evolvable"
3
4
  require "mongoid/extensions"
4
5
  require "mongoid/errors"
@@ -7,14 +7,12 @@ module Mongoid
7
7
  # @since 4.0.0
8
8
  class Specification
9
9
 
10
- # The mappings of nice Ruby-style names to the corresponding MongoDB
11
- # name.
10
+ # The mappings of nice Ruby-style names to the corresponding driver
11
+ # option name.
12
12
  #
13
13
  # @since 4.0.0
14
14
  MAPPINGS = {
15
- bucket_size: :bucketSize,
16
- drop_dups: :dropDups,
17
- expire_after_seconds: :expireAfterSeconds
15
+ expire_after_seconds: :expire_after
18
16
  }
19
17
 
20
18
  # @!attribute klass
@@ -11,6 +11,7 @@ module Mongoid
11
11
  :background,
12
12
  :database,
13
13
  :default_language,
14
+ :language_override,
14
15
  :drop_dups,
15
16
  :name,
16
17
  :sparse,
@@ -20,7 +21,12 @@ module Mongoid
20
21
  :bits,
21
22
  :bucket_size,
22
23
  :expire_after_seconds,
23
- :weights
24
+ :weights,
25
+ :storage_engine,
26
+ :key,
27
+ :sphere_version,
28
+ :text_version,
29
+ :version
24
30
  ]
25
31
 
26
32
  VALID_TYPES = [
@@ -2,7 +2,7 @@
2
2
  module Mongoid
3
3
  module Matchable
4
4
 
5
- # Checks for existance.
5
+ # Checks for existence.
6
6
  class Exists < Default
7
7
 
8
8
  # Return true if the attribute exists and checking for existence or
@@ -27,6 +27,7 @@ module Mongoid
27
27
  include Incrementable
28
28
  include Logical
29
29
  include Poppable
30
+ include Positional
30
31
  include Pullable
31
32
  include Pushable
32
33
  include Renamable
@@ -208,7 +209,7 @@ module Mongoid
208
209
  def persist_atomic_operations(operations)
209
210
  if persisted?
210
211
  selector = atomic_selector
211
- _root.collection.find(selector).update_one(operations)
212
+ _root.collection.find(selector).update_one(positionally(selector, operations))
212
213
  end
213
214
  end
214
215
  end
@@ -61,7 +61,7 @@ module Mongoid
61
61
  _parent.insert
62
62
  else
63
63
  selector = _parent.atomic_selector
64
- _root.collection.find(selector).update_one(atomic_inserts)
64
+ _root.collection.find(selector).update_one(positionally(selector, atomic_inserts))
65
65
  end
66
66
  end
67
67
 
@@ -62,7 +62,7 @@ module Mongoid
62
62
  _parent.remove_child(self) if notifying_parent?(options)
63
63
  if _parent.persisted?
64
64
  selector = _parent.atomic_selector
65
- _root.collection.find(selector).update_one(atomic_deletes)
65
+ _root.collection.find(selector).update_one(positionally(selector, atomic_deletes))
66
66
  end
67
67
  true
68
68
  end
@@ -141,9 +141,9 @@ module Mongoid
141
141
  unless updates.empty?
142
142
  coll = _root.collection
143
143
  selector = atomic_selector
144
- coll.find(selector).update_one(updates)
144
+ coll.find(selector).update_one(positionally(selector, updates))
145
145
  conflicts.each_pair do |key, value|
146
- coll.find(selector).update_one({ key => value })
146
+ coll.find(selector).update_one(positionally(selector, { key => value }))
147
147
  end
148
148
  end
149
149
  end
@@ -0,0 +1,75 @@
1
+ # encoding: utf-8
2
+ module Mongoid
3
+
4
+ # This module is responsible for taking update selectors and switching out
5
+ # the indexes for the $ positional operator where appropriate.
6
+ #
7
+ # @since 4.0.0
8
+ module Positional
9
+
10
+ # Takes the provided selector and atomic operations and replaces the
11
+ # indexes of the embedded documents with the positional operator when
12
+ # needed.
13
+ #
14
+ # @note The only time we can accurately know when to use the positional
15
+ # operator is at the exact time we are going to persist something. So
16
+ # we can tell by the selector that we are sending if it is actually
17
+ # possible to use the positional operator at all. For example, if the
18
+ # selector is: { "_id" => 1 }, then we could not use the positional
19
+ # operator for updating embedded documents since there would never be a
20
+ # match - we base whether we can based on the number of levels deep the
21
+ # selector goes, and if the id values are not nil.
22
+ #
23
+ # @example Process the operations.
24
+ # positionally(
25
+ # { "_id" => 1, "addresses._id" => 2 },
26
+ # { "$set" => { "addresses.0.street" => "hobrecht" }}
27
+ # )
28
+ #
29
+ # @param [ Hash ] selector The selector.
30
+ # @param [ Hash ] operations The update operations.
31
+ # @param [ Hash ] processed The processed update operations.
32
+ #
33
+ # @return [ Hash ] The new operations.
34
+ #
35
+ # @since 3.1.0
36
+ def positionally(selector, operations, processed = {})
37
+ if selector.size == 1 || selector.values.any? { |val| val.nil? }
38
+ return operations
39
+ end
40
+ keys = selector.keys.map{ |m| m.sub('._id','') } - ['_id']
41
+ keys = keys.sort_by { |s| s.length*-1 }
42
+ process_operations(keys, operations, processed)
43
+ end
44
+
45
+ private
46
+
47
+ def process_operations(keys, operations, processed)
48
+ operations.each_pair do |operation, update|
49
+ processed[operation] = process_updates(keys, update)
50
+ end
51
+ processed
52
+ end
53
+
54
+ def process_updates(keys, update, updates = {})
55
+ update.each_pair do |position, value|
56
+ updates[replace_index(keys, position)] = value
57
+ end
58
+ updates
59
+ end
60
+
61
+ def replace_index(keys, position)
62
+ # replace index with $ only if that key is in the selector and it is only
63
+ # nested a single level deep.
64
+ matches = position.scan(/\.\d+\./)
65
+ if matches.size == 1
66
+ keys.each do |kk|
67
+ if position =~ /^#{kk}\.\d+\.(.*)/
68
+ return "#{kk}.$.#{$1}"
69
+ end
70
+ end
71
+ end
72
+ position
73
+ end
74
+ end
75
+ end
@@ -103,6 +103,25 @@ module Mongoid
103
103
  name = meta.name
104
104
  cache_column = meta.counter_cache_column_name.to_sym
105
105
 
106
+ after_update do
107
+ if record = __send__(name)
108
+ id_field = "#{name}_id"
109
+
110
+ if attribute_changed?(id_field)
111
+ original, current = attribute_change(id_field)
112
+
113
+ unless original.nil?
114
+ record.class.decrement_counter(cache_column, original)
115
+ end
116
+
117
+ unless current.nil?
118
+ record[cache_column] = (record[cache_column] || 0) + 1
119
+ record.class.increment_counter(cache_column, current) if record.persisted?
120
+ end
121
+ end
122
+ end
123
+ end
124
+
106
125
  after_create do
107
126
  if record = __send__(name)
108
127
  record[cache_column] = (record[cache_column] || 0) + 1
@@ -65,12 +65,14 @@ module Mongoid
65
65
 
66
66
  # Run the preloader.
67
67
  #
68
- # @example Iterate over the documents loadded for the current relation
68
+ # @example Iterate over the documents loaded for the current relation
69
69
  # loader.each_loaded_document { |doc| }
70
70
  #
71
71
  # @since 4.0.0
72
72
  def each_loaded_document
73
- @metadata.klass.any_in(key => keys_from_docs).each do |doc|
73
+ criteria = @metadata.klass.any_in(key => keys_from_docs)
74
+ criteria.inclusions = criteria.inclusions - [ @metadata ]
75
+ criteria.each do |doc|
74
76
  yield doc
75
77
  end
76
78
  end
@@ -6,6 +6,7 @@ module Mongoid
6
6
  # Contains behaviour for executing operations in batch on embedded
7
7
  # documents.
8
8
  module Batchable
9
+ include Positional
9
10
 
10
11
  # Insert new documents as a batch push ($pushAll). This ensures that
11
12
  # all callbacks are run at the appropriate time and only 1 request is
@@ -36,7 +37,9 @@ module Mongoid
36
37
  def batch_clear(docs)
37
38
  pre_process_batch_remove(docs, :delete)
38
39
  unless docs.empty?
39
- collection.find(selector).update_one("$unset" => { path => true })
40
+ collection.find(selector).update_one(
41
+ positionally(selector, "$unset" => { path => true })
42
+ )
40
43
  post_process_batch_remove(docs, :delete)
41
44
  end
42
45
  _unscoped.clear
@@ -54,7 +57,9 @@ module Mongoid
54
57
  def batch_remove(docs, method = :delete)
55
58
  removals = pre_process_batch_remove(docs, method)
56
59
  if !docs.empty?
57
- collection.find(selector).update_one("$pullAll" => { path => removals })
60
+ collection.find(selector).update_one(
61
+ positionally(selector, "$pullAll" => { path => removals })
62
+ )
58
63
  post_process_batch_remove(docs, method)
59
64
  end
60
65
  reindex
@@ -125,7 +130,9 @@ module Mongoid
125
130
  self.inserts_valid = true
126
131
  inserts = pre_process_batch_insert(docs)
127
132
  if insertable?
128
- collection.find(selector).update_one(operation => { path => inserts })
133
+ collection.find(selector).update_one(
134
+ positionally(selector, operation => { path => inserts })
135
+ )
129
136
  post_process_batch_insert(docs)
130
137
  end
131
138
  inserts