mongoid 5.0.0 → 5.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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