mongo 2.9.2 → 2.10.0.rc0

Sign up to get free protection for your applications and to get access to all the features.
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.