mongo 2.9.2 → 2.10.0.rc0

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 (227) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/lib/mongo.rb +1 -0
  5. data/lib/mongo/auth/user/view.rb +4 -4
  6. data/lib/mongo/bulk_write.rb +14 -8
  7. data/lib/mongo/bulk_write/result.rb +1 -1
  8. data/lib/mongo/bulk_write/result_combiner.rb +2 -2
  9. data/lib/mongo/bulk_write/transformable.rb +17 -9
  10. data/lib/mongo/client.rb +107 -16
  11. data/lib/mongo/cluster.rb +47 -25
  12. data/lib/mongo/cluster/topology/replica_set_no_primary.rb +1 -1
  13. data/lib/mongo/cluster_time.rb +139 -0
  14. data/lib/mongo/collection.rb +84 -25
  15. data/lib/mongo/collection/view.rb +7 -3
  16. data/lib/mongo/collection/view/aggregation.rb +4 -4
  17. data/lib/mongo/collection/view/builder/aggregation.rb +31 -6
  18. data/lib/mongo/collection/view/builder/find_command.rb +4 -1
  19. data/lib/mongo/collection/view/builder/map_reduce.rb +4 -1
  20. data/lib/mongo/collection/view/change_stream.rb +54 -66
  21. data/lib/mongo/collection/view/iterable.rb +2 -2
  22. data/lib/mongo/collection/view/map_reduce.rb +6 -4
  23. data/lib/mongo/collection/view/readable.rb +36 -16
  24. data/lib/mongo/collection/view/writable.rb +68 -22
  25. data/lib/mongo/cursor.rb +87 -20
  26. data/lib/mongo/database.rb +47 -43
  27. data/lib/mongo/database/view.rb +54 -11
  28. data/lib/mongo/error.rb +13 -4
  29. data/lib/mongo/error/invalid_write_concern.rb +2 -2
  30. data/lib/mongo/error/operation_failure.rb +65 -11
  31. data/lib/mongo/error/parser.rb +41 -8
  32. data/lib/mongo/grid/fs_bucket.rb +26 -6
  33. data/lib/mongo/grid/stream/read.rb +9 -2
  34. data/lib/mongo/grid/stream/write.rb +21 -5
  35. data/lib/mongo/index/view.rb +3 -3
  36. data/lib/mongo/lint.rb +10 -3
  37. data/lib/mongo/operation.rb +2 -0
  38. data/lib/mongo/operation/aggregate/result.rb +19 -6
  39. data/lib/mongo/operation/collections_info.rb +1 -1
  40. data/lib/mongo/operation/get_more/result.rb +9 -0
  41. data/lib/mongo/operation/list_collections/command.rb +1 -3
  42. data/lib/mongo/operation/list_collections/op_msg.rb +1 -2
  43. data/lib/mongo/operation/parallel_scan/command.rb +4 -1
  44. data/lib/mongo/operation/parallel_scan/op_msg.rb +4 -1
  45. data/lib/mongo/operation/result.rb +27 -4
  46. data/lib/mongo/operation/shared/executable.rb +19 -5
  47. data/lib/mongo/operation/shared/executable_no_validate.rb +1 -2
  48. data/lib/mongo/operation/shared/executable_transaction_label.rb +0 -9
  49. data/lib/mongo/operation/shared/polymorphic_result.rb +9 -1
  50. data/lib/mongo/operation/shared/result/aggregatable.rb +2 -2
  51. data/lib/mongo/operation/shared/sessions_supported.rb +42 -32
  52. data/lib/mongo/operation/shared/specifiable.rb +40 -0
  53. data/lib/mongo/operation/shared/unpinnable.rb +39 -0
  54. data/lib/mongo/operation/shared/write.rb +1 -1
  55. data/lib/mongo/protocol/update.rb +6 -2
  56. data/lib/mongo/retryable.rb +79 -39
  57. data/lib/mongo/server/connection.rb +10 -3
  58. data/lib/mongo/server/description.rb +25 -1
  59. data/lib/mongo/server/monitor/connection.rb +1 -1
  60. data/lib/mongo/server_selector.rb +10 -0
  61. data/lib/mongo/server_selector/selectable.rb +172 -32
  62. data/lib/mongo/session.rb +654 -581
  63. data/lib/mongo/session/session_pool.rb +1 -1
  64. data/lib/mongo/socket.rb +7 -28
  65. data/lib/mongo/socket/ssl.rb +26 -1
  66. data/lib/mongo/socket/tcp.rb +3 -0
  67. data/lib/mongo/socket/unix.rb +3 -0
  68. data/lib/mongo/uri.rb +112 -265
  69. data/lib/mongo/uri/srv_protocol.rb +4 -1
  70. data/lib/mongo/version.rb +1 -1
  71. data/lib/mongo/write_concern.rb +10 -29
  72. data/lib/mongo/write_concern/acknowledged.rb +12 -0
  73. data/lib/mongo/write_concern/base.rb +17 -13
  74. data/lib/mongo/write_concern/unacknowledged.rb +12 -0
  75. data/spec/atlas/atlas_connectivity_spec.rb +7 -37
  76. data/spec/atlas/operations_spec.rb +25 -0
  77. data/spec/integration/change_stream_examples_spec.rb +45 -31
  78. data/spec/integration/change_stream_spec.rb +305 -5
  79. data/spec/integration/client_spec.rb +44 -0
  80. data/spec/integration/command_monitoring_spec.rb +1 -0
  81. data/spec/integration/command_spec.rb +7 -1
  82. data/spec/integration/mmapv1_spec.rb +28 -0
  83. data/spec/integration/mongos_pinning_spec.rb +34 -0
  84. data/spec/integration/operation_failure_code_spec.rb +2 -2
  85. data/spec/integration/{read_concern.rb → read_concern_spec.rb} +7 -1
  86. data/spec/integration/read_preference_spec.rb +485 -0
  87. data/spec/integration/retryable_writes_spec.rb +8 -19
  88. data/spec/integration/sdam_error_handling_spec.rb +1 -1
  89. data/spec/integration/sdam_events_spec.rb +2 -2
  90. data/spec/integration/server_description_spec.rb +14 -17
  91. data/spec/integration/server_selector_spec.rb +7 -3
  92. data/spec/integration/server_spec.rb +48 -0
  93. data/spec/integration/ssl_uri_options_spec.rb +1 -1
  94. data/spec/integration/step_down_spec.rb +10 -4
  95. data/spec/integration/transactions_examples_spec.rb +11 -10
  96. data/spec/lite_spec_helper.rb +19 -16
  97. data/spec/mongo/auth/scram/negotiation_spec.rb +11 -8
  98. data/spec/mongo/bulk_write/ordered_combiner_spec.rb +6 -6
  99. data/spec/mongo/bulk_write/unordered_combiner_spec.rb +4 -4
  100. data/spec/mongo/bulk_write_spec.rb +12 -2
  101. data/spec/mongo/client_construction_spec.rb +160 -8
  102. data/spec/mongo/client_spec.rb +5 -4
  103. data/spec/mongo/cluster_spec.rb +6 -6
  104. data/spec/mongo/cluster_time_spec.rb +148 -0
  105. data/spec/mongo/collection/view/aggregation_spec.rb +34 -15
  106. data/spec/mongo/collection/view/change_stream_spec.rb +62 -3
  107. data/spec/mongo/collection/view/map_reduce_spec.rb +7 -5
  108. data/spec/mongo/collection/view/readable_spec.rb +4 -4
  109. data/spec/mongo/collection_spec.rb +331 -14
  110. data/spec/mongo/cursor_spec.rb +117 -5
  111. data/spec/mongo/database_spec.rb +240 -8
  112. data/spec/mongo/error/operation_failure_spec.rb +47 -1
  113. data/spec/mongo/error/parser_spec.rb +160 -23
  114. data/spec/mongo/operation/insert/bulk_spec.rb +2 -1
  115. data/spec/mongo/operation/result_spec.rb +27 -0
  116. data/spec/mongo/operation/update/bulk_spec.rb +1 -0
  117. data/spec/mongo/retryable_spec.rb +2 -0
  118. data/spec/mongo/server/app_metadata_spec.rb +2 -2
  119. data/spec/mongo/server/connection_spec.rb +13 -17
  120. data/spec/mongo/server/monitor/connection_spec.rb +13 -10
  121. data/spec/mongo/server_selector_spec.rb +34 -2
  122. data/spec/mongo/session/session_pool_spec.rb +14 -3
  123. data/spec/mongo/session_spec.rb +3 -3
  124. data/spec/mongo/session_transaction_spec.rb +4 -3
  125. data/spec/mongo/socket/ssl_spec.rb +19 -5
  126. data/spec/mongo/socket_spec.rb +1 -62
  127. data/spec/mongo/uri/srv_protocol_spec.rb +14 -20
  128. data/spec/mongo/uri_option_parsing_spec.rb +94 -8
  129. data/spec/mongo/uri_spec.rb +23 -10
  130. data/spec/mongo/write_concern_spec.rb +56 -3
  131. data/spec/spec_tests/change_streams_spec.rb +2 -1
  132. data/spec/spec_tests/cmap_spec.rb +1 -1
  133. data/spec/spec_tests/crud_spec.rb +12 -2
  134. data/spec/spec_tests/data/change_streams/change-streams-errors.yml +24 -1
  135. data/spec/spec_tests/data/change_streams/change-streams.yml +172 -3
  136. data/spec/spec_tests/data/command_monitoring/bulkWrite.yml +1 -1
  137. data/spec/spec_tests/data/command_monitoring/updateMany.yml +0 -2
  138. data/spec/spec_tests/data/command_monitoring/updateOne.yml +0 -5
  139. data/spec/spec_tests/data/crud/read/aggregate-out.yml +0 -6
  140. data/spec/spec_tests/data/crud/read/count-empty.yml +29 -0
  141. data/spec/spec_tests/data/crud/write/bulkWrite-arrayFilters.yml +1 -0
  142. data/spec/spec_tests/data/crud/write/bulkWrite-collation.yml +101 -0
  143. data/spec/spec_tests/data/crud/write/bulkWrite.yml +401 -0
  144. data/spec/spec_tests/data/crud/write/insertMany.yml +58 -2
  145. data/spec/spec_tests/data/crud/write/updateMany-arrayFilters.yml +3 -0
  146. data/spec/spec_tests/data/crud/write/updateOne-arrayFilters.yml +6 -1
  147. data/spec/spec_tests/data/crud_v2/aggregate-merge.yml +103 -0
  148. data/spec/spec_tests/data/crud_v2/aggregate-out-readConcern.yml +110 -0
  149. data/spec/spec_tests/data/crud_v2/bulkWrite-arrayFilters.yml +81 -0
  150. data/spec/spec_tests/data/crud_v2/db-aggregate.yml +38 -0
  151. data/spec/spec_tests/data/crud_v2/updateWithPipelines.yml +92 -0
  152. data/spec/spec_tests/data/retryable_writes/insertOne-serverErrors.yml +2 -2
  153. data/spec/spec_tests/data/transactions/abort.yml +3 -0
  154. data/spec/spec_tests/data/transactions/bulk.yml +3 -8
  155. data/spec/spec_tests/data/transactions/causal-consistency.yml +3 -8
  156. data/spec/spec_tests/data/transactions/commit.yml +3 -1
  157. data/spec/spec_tests/data/transactions/count.yml +3 -0
  158. data/spec/spec_tests/data/transactions/delete.yml +3 -0
  159. data/spec/spec_tests/data/transactions/error-labels.yml +4 -1
  160. data/spec/spec_tests/data/transactions/errors-client.yml +56 -0
  161. data/spec/spec_tests/data/transactions/errors.yml +3 -0
  162. data/spec/spec_tests/data/transactions/findOneAndDelete.yml +3 -0
  163. data/spec/spec_tests/data/transactions/findOneAndReplace.yml +3 -0
  164. data/spec/spec_tests/data/transactions/findOneAndUpdate.yml +3 -0
  165. data/spec/spec_tests/data/transactions/insert.yml +3 -0
  166. data/spec/spec_tests/data/transactions/isolation.yml +3 -0
  167. data/spec/spec_tests/data/transactions/mongos-pin-auto.yml +1671 -0
  168. data/spec/spec_tests/data/transactions/mongos-recovery-token.yml +347 -0
  169. data/spec/spec_tests/data/transactions/pin-mongos.yml +557 -0
  170. data/spec/spec_tests/data/transactions/read-concern.yml +3 -0
  171. data/spec/spec_tests/data/transactions/read-pref.yml +3 -0
  172. data/spec/spec_tests/data/transactions/reads.yml +3 -0
  173. data/spec/spec_tests/data/transactions/retryable-abort.yml +5 -2
  174. data/spec/spec_tests/data/transactions/retryable-commit.yml +4 -1
  175. data/spec/spec_tests/data/transactions/retryable-writes.yml +3 -0
  176. data/spec/spec_tests/data/transactions/run-command.yml +3 -0
  177. data/spec/spec_tests/data/transactions/transaction-options.yml +6 -0
  178. data/spec/spec_tests/data/transactions/update.yml +3 -8
  179. data/spec/spec_tests/data/transactions/write-concern.yml +348 -38
  180. data/spec/spec_tests/data/transactions_api/callback-aborts.yml +6 -0
  181. data/spec/spec_tests/data/transactions_api/callback-commits.yml +5 -0
  182. data/spec/spec_tests/data/transactions_api/callback-retry.yml +7 -2
  183. data/spec/spec_tests/data/transactions_api/commit-retry.yml +70 -15
  184. data/spec/spec_tests/data/transactions_api/commit-transienttransactionerror-4.2.yml +3 -0
  185. data/spec/spec_tests/data/transactions_api/commit-transienttransactionerror.yml +3 -0
  186. data/spec/spec_tests/data/transactions_api/commit-writeconcernerror.yml +59 -109
  187. data/spec/spec_tests/data/transactions_api/commit.yml +5 -0
  188. data/spec/spec_tests/data/transactions_api/transaction-options.yml +10 -0
  189. data/spec/spec_tests/retryable_reads_spec.rb +5 -2
  190. data/spec/spec_tests/retryable_writes_spec.rb +5 -2
  191. data/spec/spec_tests/sdam_monitoring_spec.rb +3 -3
  192. data/spec/spec_tests/sdam_spec.rb +2 -2
  193. data/spec/spec_tests/transactions_api_spec.rb +1 -67
  194. data/spec/spec_tests/transactions_spec.rb +2 -66
  195. data/spec/support/authorization.rb +4 -0
  196. data/spec/support/change_streams.rb +30 -10
  197. data/spec/support/change_streams/operation.rb +27 -0
  198. data/spec/support/client_registry.rb +44 -25
  199. data/spec/support/cluster_config.rb +25 -14
  200. data/spec/support/cluster_tools.rb +32 -10
  201. data/spec/support/command_monitoring.rb +1 -1
  202. data/spec/support/common_shortcuts.rb +30 -0
  203. data/spec/support/connection_string.rb +8 -3
  204. data/spec/support/constraints.rb +34 -0
  205. data/spec/support/crud.rb +31 -16
  206. data/spec/support/crud/context.rb +23 -0
  207. data/spec/support/crud/operation.rb +311 -14
  208. data/spec/support/crud/spec.rb +2 -1
  209. data/spec/support/crud/test.rb +24 -27
  210. data/spec/support/crud/test_base.rb +22 -0
  211. data/spec/support/crud/verifier.rb +15 -1
  212. data/spec/support/event_subscriber.rb +12 -0
  213. data/spec/support/sdam_formatter_integration.rb +12 -6
  214. data/spec/support/shared/server_selector.rb +10 -0
  215. data/spec/support/shared/session.rb +13 -12
  216. data/spec/support/spec_config.rb +32 -22
  217. data/spec/support/spec_setup.rb +2 -2
  218. data/spec/support/transactions.rb +87 -0
  219. data/spec/support/transactions/context.rb +33 -0
  220. data/spec/support/transactions/operation.rb +99 -349
  221. data/spec/support/transactions/spec.rb +1 -3
  222. data/spec/support/transactions/test.rb +110 -49
  223. data/spec/support/utils.rb +74 -1
  224. metadata +52 -10
  225. metadata.gz.sig +0 -0
  226. data/spec/support/crud/read.rb +0 -265
  227. data/spec/support/crud/write.rb +0 -284
@@ -34,7 +34,15 @@ module Mongo
34
34
  #
35
35
  # @param [ Hash ] opts The options.
36
36
  #
37
+ # @option opts [ Integer ] :max_time_ms The maximum amount of time to allow the command
38
+ # to run in milliseconds.
39
+ # @option opts [ Hash ] :projection The fields to include or exclude in the returned doc.
40
+ # @option opts [ Hash ] :sort The key and direction pairs by which the result set
41
+ # will be sorted.
42
+ # @option opts [ Hash ] :write_concern The write concern options.
43
+ # Defaults to the collection's write concern.
37
44
  # @option opts [ Hash ] :collation The collation to use.
45
+ # @option opts [ Session ] :session The session to use.
38
46
  #
39
47
  # @return [ BSON::Document, nil ] The document, if found.
40
48
  #
@@ -44,10 +52,11 @@ module Mongo
44
52
  cmd[:fields] = projection if projection
45
53
  cmd[:sort] = sort if sort
46
54
  cmd[:maxTimeMS] = max_time_ms if max_time_ms
47
- cmd[:writeConcern] = write_concern.options if write_concern
48
55
 
49
56
  with_session(opts) do |session|
50
- write_with_retry(session, write_concern) do |server, txn_num|
57
+ applied_write_concern = applied_write_concern(session)
58
+ cmd[:writeConcern] = applied_write_concern.options if applied_write_concern
59
+ write_with_retry(session, applied_write_concern) do |server, txn_num|
51
60
  apply_collation!(cmd, server, opts)
52
61
  Operation::Command.new(
53
62
  :selector => cmd,
@@ -93,6 +102,11 @@ module Mongo
93
102
  # @param [ BSON::Document ] document The updates.
94
103
  # @param [ Hash ] opts The options.
95
104
  #
105
+ # @option options [ Integer ] :max_time_ms The maximum amount of time to allow the command
106
+ # to run in milliseconds.
107
+ # @option opts [ Hash ] :projection The fields to include or exclude in the returned doc.
108
+ # @option opts [ Hash ] :sort The key and direction pairs by which the result set
109
+ # will be sorted.
96
110
  # @option opts [ Symbol ] :return_document Either :before or :after.
97
111
  # @option opts [ true, false ] :upsert Whether to upsert if the document doesn't exist.
98
112
  # @option opts [ true, false ] :bypass_document_validation Whether or
@@ -102,6 +116,7 @@ module Mongo
102
116
  # @option opts [ Hash ] :collation The collation to use.
103
117
  # @option opts [ Array ] :array_filters A set of filters specifying to which array elements
104
118
  # an update should apply.
119
+ # @option opts [ Session ] :session The session to use.
105
120
  #
106
121
  # @return [ BSON::Document ] The document.
107
122
  #
@@ -115,10 +130,11 @@ module Mongo
115
130
  cmd[:upsert] = opts[:upsert] if opts[:upsert]
116
131
  cmd[:maxTimeMS] = max_time_ms if max_time_ms
117
132
  cmd[:bypassDocumentValidation] = !!opts[:bypass_document_validation]
118
- cmd[:writeConcern] = write_concern.options if write_concern
119
133
 
120
134
  value = with_session(opts) do |session|
121
- write_with_retry(session, write_concern) do |server, txn_num|
135
+ applied_write_concern = applied_write_concern(opts[:session])
136
+ cmd[:writeConcern] = applied_write_concern.options if applied_write_concern
137
+ write_with_retry(session, applied_write_concern) do |server, txn_num|
122
138
  apply_collation!(cmd, server, opts)
123
139
  apply_array_filters!(cmd, server, opts)
124
140
  Operation::Command.new(
@@ -140,6 +156,7 @@ module Mongo
140
156
  # @param [ Hash ] opts The options.
141
157
  #
142
158
  # @option opts [ Hash ] :collation The collation to use.
159
+ # @option opts [ Session ] :session The session to use.
143
160
  #
144
161
  # @return [ Result ] The response from the database.
145
162
  #
@@ -147,13 +164,14 @@ module Mongo
147
164
  def delete_many(opts = {})
148
165
  delete_doc = { Operation::Q => filter, Operation::LIMIT => 0 }
149
166
  with_session(opts) do |session|
150
- legacy_write_with_retry do |server|
167
+ write_concern = write_concern_with_session(session)
168
+ nro_write_with_retry(session, write_concern) do |server|
151
169
  apply_collation!(delete_doc, server, opts)
152
170
  Operation::Delete.new(
153
171
  :deletes => [ delete_doc ],
154
172
  :db_name => collection.database.name,
155
173
  :coll_name => collection.name,
156
- :write_concern => collection.write_concern,
174
+ :write_concern => write_concern,
157
175
  :session => session
158
176
  ).execute(server)
159
177
  end
@@ -168,14 +186,15 @@ module Mongo
168
186
  # @param [ Hash ] opts The options.
169
187
  #
170
188
  # @option opts [ Hash ] :collation The collation to use.
189
+ # @option opts [ Session ] :session The session to use.
171
190
  #
172
191
  # @return [ Result ] The response from the database.
173
192
  #
174
193
  # @since 2.0.0
175
194
  def delete_one(opts = {})
176
195
  delete_doc = { Operation::Q => filter, Operation::LIMIT => 1 }
177
- write_concern = collection.write_concern
178
196
  with_session(opts) do |session|
197
+ write_concern = write_concern_with_session(session)
179
198
  write_with_retry(session, write_concern) do |server, txn_num|
180
199
  apply_collation!(delete_doc, server, opts)
181
200
  Operation::Delete.new(
@@ -200,7 +219,10 @@ module Mongo
200
219
  #
201
220
  # @option opts [ true, false ] :upsert Whether to upsert if the
202
221
  # document doesn't exist.
222
+ # @option opts [ true, false ] :bypass_document_validation Whether or
223
+ # not to skip document level validation.
203
224
  # @option opts [ Hash ] :collation The collation to use.
225
+ # @option opts [ Session ] :session The session to use.
204
226
  #
205
227
  # @return [ Result ] The response from the database.
206
228
  #
@@ -208,11 +230,12 @@ module Mongo
208
230
  def replace_one(replacement, opts = {})
209
231
  update_doc = { Operation::Q => filter,
210
232
  Operation::U => replacement,
211
- Operation::MULTI => false,
212
- Operation::UPSERT => !!opts[:upsert]
213
233
  }
214
- write_concern = collection.write_concern
234
+ if opts[:upsert]
235
+ update_doc[:upsert] = true
236
+ end
215
237
  with_session(opts) do |session|
238
+ write_concern = write_concern_with_session(session)
216
239
  write_with_retry(session, write_concern) do |server, txn_num|
217
240
  apply_collation!(update_doc, server, opts)
218
241
  apply_array_filters!(update_doc, server, opts)
@@ -235,14 +258,17 @@ module Mongo
235
258
  # @example Update multiple documents in the collection.
236
259
  # collection_view.update_many('$set' => { name: 'test' })
237
260
  #
238
- # @param [ Hash ] spec The update statement.
261
+ # @param [ Hash | Array<Hash> ] spec The update document or pipeline.
239
262
  # @param [ Hash ] opts The options.
240
263
  #
241
264
  # @option opts [ true, false ] :upsert Whether to upsert if the
242
265
  # document doesn't exist.
266
+ # @option opts [ true, false ] :bypass_document_validation Whether or
267
+ # not to skip document level validation.
243
268
  # @option opts [ Hash ] :collation The collation to use.
244
- # @option opts [ Array ] :array_filters A set of filters specifying to which array elements
245
- # an update should apply.
269
+ # @option opts [ Array ] :array_filters A set of filters specifying to
270
+ # which array elements an update should apply.
271
+ # @option opts [ Session ] :session The session to use.
246
272
  #
247
273
  # @return [ Result ] The response from the database.
248
274
  #
@@ -251,16 +277,20 @@ module Mongo
251
277
  update_doc = { Operation::Q => filter,
252
278
  Operation::U => spec,
253
279
  Operation::MULTI => true,
254
- Operation::UPSERT => !!opts[:upsert] }
280
+ }
281
+ if opts[:upsert]
282
+ update_doc[:upsert] = true
283
+ end
255
284
  with_session(opts) do |session|
256
- legacy_write_with_retry do |server|
285
+ write_concern = write_concern_with_session(session)
286
+ nro_write_with_retry(session, write_concern) do |server|
257
287
  apply_collation!(update_doc, server, opts)
258
288
  apply_array_filters!(update_doc, server, opts)
259
289
  Operation::Update.new(
260
290
  :updates => [ update_doc ],
261
291
  :db_name => collection.database.name,
262
292
  :coll_name => collection.name,
263
- :write_concern => collection.write_concern,
293
+ :write_concern => write_concern,
264
294
  :bypass_document_validation => !!opts[:bypass_document_validation],
265
295
  :session => session
266
296
  ).execute(server)
@@ -273,14 +303,17 @@ module Mongo
273
303
  # @example Update a single document in the collection.
274
304
  # collection_view.update_one('$set' => { name: 'test' })
275
305
  #
276
- # @param [ Hash ] spec The update statement.
306
+ # @param [ Hash | Array<Hash> ] spec The update document or pipeline.
277
307
  # @param [ Hash ] opts The options.
278
308
  #
279
309
  # @option opts [ true, false ] :upsert Whether to upsert if the
280
310
  # document doesn't exist.
311
+ # @option opts [ true, false ] :bypass_document_validation Whether or
312
+ # not to skip document level validation.
281
313
  # @option opts [ Hash ] :collation The collation to use.
282
- # @option opts [ Array ] :array_filters A set of filters specifying to which array elements
283
- # an update should apply.
314
+ # @option opts [ Array ] :array_filters A set of filters specifying to
315
+ # which array elements an update should apply.
316
+ # @option opts [ Session ] :session The session to use.
284
317
  #
285
318
  # @return [ Result ] The response from the database.
286
319
  #
@@ -288,10 +321,12 @@ module Mongo
288
321
  def update_one(spec, opts = {})
289
322
  update_doc = { Operation::Q => filter,
290
323
  Operation::U => spec,
291
- Operation::MULTI => false,
292
- Operation::UPSERT => !!opts[:upsert] }
293
- write_concern = collection.write_concern
324
+ }
325
+ if opts[:upsert]
326
+ update_doc[:upsert] = true
327
+ end
294
328
  with_session(opts) do |session|
329
+ write_concern = write_concern_with_session(session)
295
330
  write_with_retry(session, write_concern) do |server, txn_num|
296
331
  apply_collation!(update_doc, server, opts)
297
332
  apply_array_filters!(update_doc, server, opts)
@@ -323,6 +358,17 @@ module Mongo
323
358
  raise Error::UnsupportedArrayFilters.new
324
359
  end
325
360
  end
361
+
362
+ # Get the write concern for an operation
363
+ #
364
+ # @return [ Mongo::WriteConcern ] The write concern.
365
+ def applied_write_concern(session)
366
+ if wco = options[:write_concern] || options[:write]
367
+ WriteConcern.get(wco)
368
+ else
369
+ write_concern_with_session(session)
370
+ end
371
+ end
326
372
  end
327
373
  end
328
374
  end
@@ -42,6 +42,12 @@ module Mongo
42
42
  # @return [ Collection::View ] view The collection view.
43
43
  attr_reader :view
44
44
 
45
+ # The resume token tracked by the cursor for change stream resuming
46
+ #
47
+ # @return [ BSON::Document | nil ] The cursor resume token.
48
+ # @api private
49
+ attr_reader :resume_token
50
+
45
51
  # Creates a +Cursor+ object.
46
52
  #
47
53
  # @example Instantiate the cursor.
@@ -125,20 +131,39 @@ module Mongo
125
131
  #
126
132
  # @since 2.0.0
127
133
  def each
128
- process(@initial_result).each { |doc| yield doc }
129
- while more?
130
- return kill_cursors if exhausted?
131
- get_more.each { |doc| yield doc }
134
+ # If we already iterated past the first batch (i.e., called get_more
135
+ # at least once), the cursor on the server side has advanced past
136
+ # the first batch and restarting iteration from the beginning by
137
+ # returning initial result would miss documents in the second batch
138
+ # and subsequent batches up to wherever the cursor is. Detect this
139
+ # condition and abort the iteration.
140
+ #
141
+ # In a future driver version, each would either continue from the
142
+ # end of previous iteration or would always restart from the
143
+ # beginning.
144
+ if @get_more_called
145
+ raise NotImplementedError, 'Cannot restart iteration of a cursor which issued a getMore'
132
146
  end
147
+
148
+ # To maintain compatibility with pre-2.10 driver versions, reset
149
+ # the documents array each time a new iteration is started.
150
+ @documents = nil
151
+
152
+ loop do
153
+ document = try_next
154
+ yield document if document
155
+ end
156
+ rescue StopIteration => e
157
+ return self
133
158
  end
134
159
 
135
160
  # Return one document from the query, if one is available.
136
161
  #
137
- # Retries once on a resumable error.
138
- #
139
162
  # This method will wait up to max_await_time_ms milliseconds
140
163
  # for changes from the server, and if no changes are received
141
- # it will return nil.
164
+ # it will return nil. If there are no more documents to return
165
+ # from the server, or if we have exhausted the cursor, it will
166
+ # raise a StopIteration exception.
142
167
  #
143
168
  # @note This method is experimental and subject to change.
144
169
  #
@@ -146,30 +171,45 @@ module Mongo
146
171
  # @api private
147
172
  def try_next
148
173
  if @documents.nil?
149
- @documents = process(@initial_result)
174
+ # Since published versions of Mongoid have a copy of old driver cursor
175
+ # code, our dup call in #process isn't invoked when Mongoid query
176
+ # cache is active. Work around that by also calling dup here on
177
+ # the result of #process which might come out of Mongoid's code.
178
+ @documents = process(@initial_result).dup
150
179
  # the documents here can be an empty array, hence
151
180
  # we may end up issuing a getMore in the first try_next call
152
181
  end
153
182
 
154
183
  if @documents.empty?
184
+ # On empty batches, we cache the batch resume token
185
+ cache_batch_resume_token
186
+
155
187
  if more?
156
188
  if exhausted?
157
189
  kill_cursors
158
- return nil
190
+ raise StopIteration
159
191
  end
160
-
161
192
  @documents = get_more
193
+ else
194
+ raise StopIteration
162
195
  end
163
196
  else
164
197
  # cursor is closed here
165
198
  # keep documents as an empty array
166
199
  end
167
200
 
168
- if @documents
169
- return @documents.shift
201
+ # If there is at least one document, cache its _id
202
+ if @documents[0]
203
+ cache_resume_token(@documents[0])
170
204
  end
171
205
 
172
- nil
206
+ # Cache the batch resume token if we are iterating
207
+ # over the last document, or if the batch is empty
208
+ if @documents.size <= 1
209
+ cache_batch_resume_token
210
+ end
211
+
212
+ return @documents.shift
173
213
  end
174
214
 
175
215
  # Get the batch size.
@@ -235,13 +275,15 @@ module Mongo
235
275
  use_limit? ? @remaining : (batch_size || 0)
236
276
  end
237
277
 
238
- private
239
-
240
- def exhausted?
241
- limited? ? @remaining <= 0 : false
242
- end
243
-
278
+ # Execute a getMore command and return the batch of documents
279
+ # obtained from the server.
280
+ #
281
+ # @return [ Array<BSON::Document> ] The batch of documents
282
+ #
283
+ # @api private
244
284
  def get_more
285
+ @get_more_called = true
286
+
245
287
  # Modern retryable reads specification prohibits retrying getMores.
246
288
  # Legacy retryable read logic used to retry getMores, but since
247
289
  # doing so may result in silent data loss, the driver no longer retries
@@ -250,6 +292,22 @@ module Mongo
250
292
  process(get_more_operation.execute(@server))
251
293
  end
252
294
 
295
+ private
296
+
297
+ def exhausted?
298
+ limited? ? @remaining <= 0 : false
299
+ end
300
+
301
+ def cache_resume_token(doc)
302
+ if doc[:_id] && doc[:_id].is_a?(Hash)
303
+ @resume_token = doc[:_id] && doc[:_id].dup.freeze
304
+ end
305
+ end
306
+
307
+ def cache_batch_resume_token
308
+ @resume_token = @post_batch_resume_token if @post_batch_resume_token
309
+ end
310
+
253
311
  def get_more_operation
254
312
  if @server.features.find_command_enabled?
255
313
  spec = Builder::GetMoreCommand.new(self, @session).specification
@@ -298,8 +356,17 @@ module Mongo
298
356
  @coll_name ||= result.namespace.sub("#{database.name}.", '') if result.namespace
299
357
  unregister if result.cursor_id == 0
300
358
  @cursor_id = result.cursor_id
359
+
360
+ if result.respond_to?(:post_batch_resume_token)
361
+ @post_batch_resume_token = result.post_batch_resume_token
362
+ end
363
+
301
364
  end_session if !more?
302
- result.documents
365
+
366
+ # Since our iteration code mutates the documents array by calling #shift
367
+ # on it, duplicate the documents here to permit restarting iteration
368
+ # from the beginning of the cursor as long as get_more was not called
369
+ result.documents.dup
303
370
  end
304
371
 
305
372
  def use_limit?
@@ -42,6 +42,7 @@ module Mongo
42
42
  # Database name field constant.
43
43
  #
44
44
  # @since 2.1.0
45
+ # @deprecated
45
46
  NAME = 'name'.freeze
46
47
 
47
48
  # Databases constant.
@@ -109,34 +110,36 @@ module Mongo
109
110
 
110
111
  # Get all the names of the non-system collections in the database.
111
112
  #
112
- # @example Get the collection names.
113
- # database.collection_names
113
+ # @note The set of returned collection names depends on the version of
114
+ # MongoDB server that fulfills the request.
114
115
  #
115
- # @return [ Array<String> ] The names of all non-system collections.
116
+ # @return [ Array<String> ] Names of the collections.
116
117
  #
117
118
  # @since 2.0.0
118
119
  def collection_names(options = {})
119
120
  View.new(self).collection_names(options)
120
121
  end
121
122
 
122
- # Get info on all the collections in the database.
123
+ # Get info on all the non-system collections in the database.
123
124
  #
124
- # @example Get info on each collection.
125
- # database.list_collections
125
+ # @note The set of collections returned, and the schema of the
126
+ # information hash per collection, depends on the MongoDB server
127
+ # version that fulfills the request.
126
128
  #
127
- # @return [ Array<Hash> ] Info for each collection in the database.
129
+ # @return [ Array<Hash> ] Array of information hashes, one for each
130
+ # collection in the database.
128
131
  #
129
132
  # @since 2.0.5
130
133
  def list_collections
131
134
  View.new(self).list_collections
132
135
  end
133
136
 
134
- # Get all the collections that belong to this database.
137
+ # Get all the non-system collections that belong to this database.
135
138
  #
136
- # @example Get all the collections.
137
- # database.collections
139
+ # @note The set of returned collections depends on the version of
140
+ # MongoDB server that fulfills the request.
138
141
  #
139
- # @return [ Array<Mongo::Collection> ] All the collections.
142
+ # @return [ Array<Mongo::Collection> ] The collections.
140
143
  #
141
144
  # @since 2.0.0
142
145
  def collections
@@ -156,37 +159,6 @@ module Mongo
156
159
  #
157
160
  # @return [ Hash ] The result of the command execution.
158
161
  def command(operation, opts = {})
159
- txn_read_pref = if opts[:session] && opts[:session].in_transaction?
160
- opts[:session].txn_read_preference
161
- else
162
- nil
163
- end
164
- txn_read_pref ||= opts[:read] || ServerSelector::PRIMARY
165
- Lint.validate_underscore_read_preference(txn_read_pref)
166
- selector = ServerSelector.get(txn_read_pref)
167
-
168
- client.send(:with_session, opts) do |session|
169
- server = selector.select_server(cluster)
170
- Operation::Command.new({
171
- :selector => operation.dup,
172
- :db_name => name,
173
- :read => selector,
174
- :session => session
175
- }).execute(server)
176
- end
177
- end
178
-
179
- # Execute a read command on the database, retrying the read if necessary.
180
- #
181
- # @param [ Hash ] operation The command to execute.
182
- # @param [ Hash ] opts The command options.
183
- #
184
- # @option opts :read [ Hash ] The read preference for this command.
185
- # @option opts :session [ Session ] The session to use for this command.
186
- #
187
- # @return [ Hash ] The result of the command execution.
188
- # @api private
189
- def read_command(operation, opts = {})
190
162
  txn_read_pref = if opts[:session] && opts[:session].in_transaction?
191
163
  opts[:session].txn_read_preference
192
164
  else
@@ -228,7 +200,7 @@ module Mongo
228
200
  db_name: name,
229
201
  write_concern: write_concern,
230
202
  session: session
231
- }).execute(next_primary)
203
+ }).execute(next_primary(nil, session))
232
204
  end
233
205
  end
234
206
 
@@ -287,6 +259,38 @@ module Mongo
287
259
  Auth::User::View.new(self)
288
260
  end
289
261
 
262
+ # Perform an aggregation on the database.
263
+ #
264
+ # @example Perform an aggregation.
265
+ # collection.aggregate([ { "$listLocalSessions" => {} } ])
266
+ #
267
+ # @param [ Array<Hash> ] pipeline The aggregation pipeline.
268
+ # @param [ Hash ] options The aggregation options.
269
+ #
270
+ # @option options [ true, false ] :allow_disk_use Set to true if disk
271
+ # usage is allowed during the aggregation.
272
+ # @option options [ Integer ] :batch_size The number of documents to return
273
+ # per batch.
274
+ # @option options [ true, false ] :bypass_document_validation Whether or
275
+ # not to skip document level validation.
276
+ # @option options [ Hash ] :collation The collation to use.
277
+ # @option options [ String ] :comment Associate a comment with the aggregation.
278
+ # @option options [ String ] :hint The index to use for the aggregation.
279
+ # @option options [ Integer ] :max_time_ms The maximum amount of time in
280
+ # milliseconds to allow the aggregation to run.
281
+ # @option options [ true, false ] :use_cursor Indicates whether the command
282
+ # will request that the server provide results using a cursor. Note that
283
+ # as of server version 3.6, aggregations always provide results using a
284
+ # cursor and this option is therefore not valid.
285
+ # @option options [ Session ] :session The session to use.
286
+ #
287
+ # @return [ Aggregation ] The aggregation object.
288
+ #
289
+ # @since 2.10.0
290
+ def aggregate(pipeline, options = {})
291
+ View.new(self).aggregate(pipeline, options)
292
+ end
293
+
290
294
  # As of version 3.6 of the MongoDB server, a ``$changeStream`` pipeline stage is supported
291
295
  # in the aggregation framework. As of version 4.0, this stage allows users to request that
292
296
  # notifications are sent for all changes that occur in the client's database.