mongo 2.19.1 → 2.21.0

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 (356) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +40 -1
  3. data/Rakefile +83 -174
  4. data/lib/mongo/address.rb +22 -3
  5. data/lib/mongo/auth/aws/credentials_retriever.rb +70 -17
  6. data/lib/mongo/auth/base.rb +1 -1
  7. data/lib/mongo/bulk_write.rb +35 -2
  8. data/lib/mongo/client.rb +38 -6
  9. data/lib/mongo/client_encryption.rb +6 -3
  10. data/lib/mongo/cluster/reapers/cursor_reaper.rb +6 -1
  11. data/lib/mongo/cluster/sdam_flow.rb +20 -7
  12. data/lib/mongo/cluster/topology/base.rb +16 -0
  13. data/lib/mongo/cluster.rb +41 -5
  14. data/lib/mongo/collection/helpers.rb +1 -1
  15. data/lib/mongo/collection/view/aggregation/behavior.rb +131 -0
  16. data/lib/mongo/collection/view/aggregation.rb +33 -99
  17. data/lib/mongo/collection/view/builder/aggregation.rb +1 -7
  18. data/lib/mongo/collection/view/change_stream.rb +80 -27
  19. data/lib/mongo/collection/view/iterable.rb +92 -60
  20. data/lib/mongo/collection/view/map_reduce.rb +25 -8
  21. data/lib/mongo/collection/view/readable.rb +79 -30
  22. data/lib/mongo/collection/view/writable.rb +109 -48
  23. data/lib/mongo/collection/view.rb +44 -3
  24. data/lib/mongo/collection.rb +185 -26
  25. data/lib/mongo/config.rb +2 -2
  26. data/lib/mongo/crypt/auto_encrypter.rb +4 -6
  27. data/lib/mongo/crypt/binding.rb +4 -4
  28. data/lib/mongo/crypt/context.rb +20 -14
  29. data/lib/mongo/crypt/encryption_io.rb +56 -26
  30. data/lib/mongo/crypt/explicit_encrypter.rb +49 -20
  31. data/lib/mongo/crypt/explicit_encryption_context.rb +17 -11
  32. data/lib/mongo/crypt/kms/azure/credentials_retriever.rb +22 -6
  33. data/lib/mongo/crypt/kms/gcp/credentials_retriever.rb +29 -4
  34. data/lib/mongo/csot_timeout_holder.rb +119 -0
  35. data/lib/mongo/cursor/kill_spec.rb +5 -2
  36. data/lib/mongo/cursor/nontailable.rb +27 -0
  37. data/lib/mongo/cursor.rb +86 -24
  38. data/lib/mongo/cursor_host.rb +82 -0
  39. data/lib/mongo/database/view.rb +81 -14
  40. data/lib/mongo/database.rb +88 -18
  41. data/lib/mongo/error/operation_failure.rb +209 -204
  42. data/lib/mongo/error/server_timeout_error.rb +12 -0
  43. data/lib/mongo/error/socket_timeout_error.rb +3 -1
  44. data/lib/mongo/error/timeout_error.rb +23 -0
  45. data/lib/mongo/error/transactions_not_supported.rb +34 -0
  46. data/lib/mongo/error.rb +3 -0
  47. data/lib/mongo/grid/fs_bucket.rb +48 -9
  48. data/lib/mongo/grid/stream/read.rb +15 -1
  49. data/lib/mongo/grid/stream/write.rb +21 -4
  50. data/lib/mongo/index/view.rb +77 -16
  51. data/lib/mongo/monitoring/event/secure.rb +1 -1
  52. data/lib/mongo/operation/context.rb +40 -2
  53. data/lib/mongo/operation/create_search_indexes/op_msg.rb +31 -0
  54. data/lib/mongo/operation/create_search_indexes.rb +15 -0
  55. data/lib/mongo/operation/delete/op_msg.rb +2 -1
  56. data/lib/mongo/operation/drop_search_index/op_msg.rb +33 -0
  57. data/lib/mongo/operation/drop_search_index.rb +15 -0
  58. data/lib/mongo/operation/find/op_msg.rb +45 -0
  59. data/lib/mongo/operation/get_more/op_msg.rb +33 -0
  60. data/lib/mongo/operation/insert/op_msg.rb +3 -2
  61. data/lib/mongo/operation/insert/result.rb +4 -2
  62. data/lib/mongo/operation/list_collections/result.rb +1 -1
  63. data/lib/mongo/operation/map_reduce/result.rb +1 -1
  64. data/lib/mongo/operation/op_msg_base.rb +3 -1
  65. data/lib/mongo/operation/result.rb +26 -5
  66. data/lib/mongo/operation/shared/executable.rb +55 -28
  67. data/lib/mongo/operation/shared/op_msg_executable.rb +4 -1
  68. data/lib/mongo/operation/shared/response_handling.rb +25 -27
  69. data/lib/mongo/operation/shared/sessions_supported.rb +1 -1
  70. data/lib/mongo/operation/shared/specifiable.rb +7 -0
  71. data/lib/mongo/operation/shared/timed.rb +52 -0
  72. data/lib/mongo/operation/shared/write.rb +4 -1
  73. data/lib/mongo/operation/update/op_msg.rb +2 -1
  74. data/lib/mongo/operation/update_search_index/op_msg.rb +34 -0
  75. data/lib/mongo/operation/update_search_index.rb +15 -0
  76. data/lib/mongo/operation.rb +4 -0
  77. data/lib/mongo/protocol/message.rb +1 -4
  78. data/lib/mongo/protocol/msg.rb +2 -2
  79. data/lib/mongo/retryable/base_worker.rb +28 -3
  80. data/lib/mongo/retryable/read_worker.rb +78 -36
  81. data/lib/mongo/retryable/write_worker.rb +59 -25
  82. data/lib/mongo/retryable.rb +8 -2
  83. data/lib/mongo/search_index/view.rb +232 -0
  84. data/lib/mongo/server/app_metadata/environment.rb +64 -9
  85. data/lib/mongo/server/app_metadata.rb +5 -4
  86. data/lib/mongo/server/connection.rb +11 -5
  87. data/lib/mongo/server/connection_base.rb +22 -2
  88. data/lib/mongo/server/connection_pool.rb +32 -14
  89. data/lib/mongo/server/description/features.rb +2 -1
  90. data/lib/mongo/server/description.rb +18 -5
  91. data/lib/mongo/server/monitor.rb +7 -4
  92. data/lib/mongo/server/pending_connection.rb +25 -8
  93. data/lib/mongo/server/{round_trip_time_averager.rb → round_trip_time_calculator.rb} +25 -7
  94. data/lib/mongo/server.rb +11 -6
  95. data/lib/mongo/server_selector/base.rb +54 -12
  96. data/lib/mongo/session/server_session/dirtyable.rb +52 -0
  97. data/lib/mongo/session/server_session.rb +3 -0
  98. data/lib/mongo/session/session_pool.rb +12 -18
  99. data/lib/mongo/session.rb +110 -9
  100. data/lib/mongo/socket/ssl.rb +131 -18
  101. data/lib/mongo/socket/tcp.rb +40 -6
  102. data/lib/mongo/socket.rb +154 -25
  103. data/lib/mongo/uri/options_mapper.rb +1 -0
  104. data/lib/mongo/uri.rb +0 -4
  105. data/lib/mongo/version.rb +1 -5
  106. data/lib/mongo.rb +2 -0
  107. data/mongo.gemspec +9 -18
  108. data/spec/atlas/atlas_connectivity_spec.rb +9 -9
  109. data/spec/atlas/operations_spec.rb +5 -5
  110. data/spec/faas/ruby-sam-app/Gemfile +9 -0
  111. data/spec/faas/ruby-sam-app/mongodb/Gemfile +4 -0
  112. data/spec/faas/ruby-sam-app/mongodb/app.rb +149 -0
  113. data/spec/faas/ruby-sam-app/template.yaml +48 -0
  114. data/spec/integration/client_side_encryption/auto_encryption_mongocryptd_spawn_spec.rb +2 -1
  115. data/spec/integration/client_side_encryption/auto_encryption_spec.rb +494 -487
  116. data/spec/integration/client_side_encryption/corpus_spec.rb +10 -2
  117. data/spec/integration/client_side_encryption/on_demand_aws_credentials_spec.rb +1 -1
  118. data/spec/integration/client_side_encryption/range_explicit_encryption_prose_spec.rb +67 -20
  119. data/spec/integration/client_side_operations_timeout/encryption_prose_spec.rb +131 -0
  120. data/spec/integration/connection_pool_populator_spec.rb +2 -0
  121. data/spec/integration/cursor_pinning_spec.rb +15 -60
  122. data/spec/integration/cursor_reaping_spec.rb +1 -1
  123. data/spec/integration/docs_examples_spec.rb +1 -1
  124. data/spec/integration/find_options_spec.rb +227 -0
  125. data/spec/integration/operation_failure_code_spec.rb +1 -1
  126. data/spec/integration/operation_failure_message_spec.rb +3 -3
  127. data/spec/integration/retryable_errors_spec.rb +2 -2
  128. data/spec/integration/retryable_reads_errors_spec.rb +196 -31
  129. data/spec/integration/retryable_writes_errors_spec.rb +156 -0
  130. data/spec/integration/sdam_error_handling_spec.rb +4 -1
  131. data/spec/integration/search_indexes_prose_spec.rb +172 -0
  132. data/spec/integration/server_spec.rb +4 -3
  133. data/spec/integration/transactions_api_examples_spec.rb +2 -0
  134. data/spec/kerberos/kerberos_spec.rb +4 -0
  135. data/spec/lite_spec_helper.rb +34 -20
  136. data/spec/mongo/auth/user/view_spec.rb +1 -1
  137. data/spec/mongo/caching_cursor_spec.rb +1 -1
  138. data/spec/mongo/client_encryption_spec.rb +1 -0
  139. data/spec/mongo/client_spec.rb +158 -4
  140. data/spec/mongo/cluster_spec.rb +36 -0
  141. data/spec/mongo/collection/view/aggregation_spec.rb +20 -40
  142. data/spec/mongo/collection/view/change_stream_spec.rb +3 -3
  143. data/spec/mongo/collection/view/explainable_spec.rb +2 -0
  144. data/spec/mongo/collection_crud_spec.rb +2 -1
  145. data/spec/mongo/collection_spec.rb +5 -6
  146. data/spec/mongo/crypt/auto_encrypter_spec.rb +14 -12
  147. data/spec/mongo/crypt/data_key_context_spec.rb +3 -1
  148. data/spec/mongo/crypt/explicit_encryption_context_spec.rb +2 -2
  149. data/spec/mongo/crypt/handle_spec.rb +1 -1
  150. data/spec/mongo/cursor_spec.rb +26 -9
  151. data/spec/mongo/error/operation_failure_heavy_spec.rb +2 -2
  152. data/spec/mongo/operation/context_spec.rb +79 -0
  153. data/spec/mongo/operation/create/op_msg_spec.rb +106 -110
  154. data/spec/mongo/operation/delete/op_msg_spec.rb +6 -5
  155. data/spec/mongo/operation/find/op_msg_spec.rb +66 -0
  156. data/spec/mongo/operation/get_more/op_msg_spec.rb +65 -0
  157. data/spec/mongo/operation/insert/op_msg_spec.rb +128 -131
  158. data/spec/mongo/operation/insert_spec.rb +1 -1
  159. data/spec/mongo/operation/shared/csot/examples.rb +113 -0
  160. data/spec/mongo/query_cache_spec.rb +243 -225
  161. data/spec/mongo/retryable/write_worker_spec.rb +39 -0
  162. data/spec/mongo/retryable_spec.rb +1 -0
  163. data/spec/mongo/server/app_metadata/environment_spec.rb +135 -0
  164. data/spec/mongo/server/app_metadata_spec.rb +12 -2
  165. data/spec/mongo/server/connection_spec.rb +26 -0
  166. data/spec/mongo/server/round_trip_time_calculator_spec.rb +120 -0
  167. data/spec/mongo/session/session_pool_spec.rb +1 -16
  168. data/spec/mongo/session_transaction_spec.rb +15 -0
  169. data/spec/mongo/socket/ssl_spec.rb +0 -10
  170. data/spec/mongo/uri_spec.rb +0 -9
  171. data/spec/runners/change_streams/test.rb +2 -2
  172. data/spec/runners/crud/operation.rb +1 -1
  173. data/spec/runners/crud/test.rb +0 -8
  174. data/spec/runners/crud/verifier.rb +3 -1
  175. data/spec/runners/crud.rb +1 -1
  176. data/spec/runners/transactions/operation.rb +4 -6
  177. data/spec/runners/transactions/test.rb +12 -3
  178. data/spec/runners/unified/ambiguous_operations.rb +13 -0
  179. data/spec/runners/unified/assertions.rb +20 -3
  180. data/spec/runners/unified/change_stream_operations.rb +14 -24
  181. data/spec/runners/unified/crud_operations.rb +82 -47
  182. data/spec/runners/unified/ddl_operations.rb +38 -7
  183. data/spec/runners/unified/grid_fs_operations.rb +37 -2
  184. data/spec/runners/unified/search_index_operations.rb +63 -0
  185. data/spec/runners/unified/support_operations.rb +46 -9
  186. data/spec/runners/unified/test.rb +33 -12
  187. data/spec/runners/unified.rb +1 -1
  188. data/spec/solo/clean_exit_spec.rb +2 -0
  189. data/spec/spec_helper.rb +1 -1
  190. data/spec/spec_tests/client_side_operations_timeout_spec.rb +15 -0
  191. data/spec/spec_tests/data/change_streams_unified/change-streams-clusterTime.yml +3 -1
  192. data/spec/spec_tests/data/change_streams_unified/change-streams-disambiguatedPaths.yml +3 -1
  193. data/spec/spec_tests/data/change_streams_unified/change-streams-errors.yml +3 -1
  194. data/spec/spec_tests/data/change_streams_unified/change-streams-pre_and_post_images.yml +1 -1
  195. data/spec/spec_tests/data/change_streams_unified/change-streams-resume-allowlist.yml +1 -1
  196. data/spec/spec_tests/data/change_streams_unified/change-streams-resume-errorLabels.yml +1 -1
  197. data/spec/spec_tests/data/change_streams_unified/change-streams-showExpandedEvents.yml +1 -1
  198. data/spec/spec_tests/data/client_side_encryption/badQueries.yml +2 -1
  199. data/spec/spec_tests/data/client_side_encryption/explain.yml +2 -2
  200. data/spec/spec_tests/data/client_side_encryption/fle2v2-BypassQueryAnalysis.yml +1 -0
  201. data/spec/spec_tests/data/client_side_encryption/fle2v2-Compact.yml +1 -0
  202. data/spec/spec_tests/data/client_side_encryption/fle2v2-CreateCollection.yml +1 -0
  203. data/spec/spec_tests/data/client_side_encryption/fle2v2-DecryptExistingData.yml +1 -0
  204. data/spec/spec_tests/data/client_side_encryption/fle2v2-Delete.yml +1 -0
  205. data/spec/spec_tests/data/client_side_encryption/fle2v2-EncryptedFields-vs-EncryptedFieldsMap.yml +1 -0
  206. data/spec/spec_tests/data/client_side_encryption/fle2v2-EncryptedFields-vs-jsonSchema.yml +1 -0
  207. data/spec/spec_tests/data/client_side_encryption/fle2v2-EncryptedFieldsMap-defaults.yml +1 -0
  208. data/spec/spec_tests/data/client_side_encryption/fle2v2-FindOneAndUpdate.yml +1 -0
  209. data/spec/spec_tests/data/client_side_encryption/fle2v2-InsertFind-Indexed.yml +1 -0
  210. data/spec/spec_tests/data/client_side_encryption/fle2v2-InsertFind-Unindexed.yml +1 -0
  211. data/spec/spec_tests/data/client_side_encryption/fle2v2-MissingKey.yml +1 -0
  212. data/spec/spec_tests/data/client_side_encryption/fle2v2-NoEncryption.yml +1 -0
  213. data/spec/spec_tests/data/client_side_encryption/fle2v2-Update.yml +1 -0
  214. data/spec/spec_tests/data/client_side_encryption/fle2v2-validatorAndPartialFieldExpression.yml +2 -1
  215. data/spec/spec_tests/data/client_side_encryption/timeoutMS.yml +67 -0
  216. data/spec/spec_tests/data/client_side_operations_timeout/bulkWrite.yml +87 -0
  217. data/spec/spec_tests/data/client_side_operations_timeout/change-streams.yml +358 -0
  218. data/spec/spec_tests/data/client_side_operations_timeout/close-cursors.yml +129 -0
  219. data/spec/spec_tests/data/client_side_operations_timeout/command-execution.yml +250 -0
  220. data/spec/spec_tests/data/client_side_operations_timeout/convenient-transactions.yml +113 -0
  221. data/spec/spec_tests/data/client_side_operations_timeout/cursors.yml +70 -0
  222. data/spec/spec_tests/data/client_side_operations_timeout/deprecated-options.yml +3982 -0
  223. data/spec/spec_tests/data/client_side_operations_timeout/error-transformations.yml +96 -0
  224. data/spec/spec_tests/data/client_side_operations_timeout/global-timeoutMS.yml +3236 -0
  225. data/spec/spec_tests/data/client_side_operations_timeout/gridfs-advanced.yml +207 -0
  226. data/spec/spec_tests/data/client_side_operations_timeout/gridfs-delete.yml +152 -0
  227. data/spec/spec_tests/data/client_side_operations_timeout/gridfs-download.yml +182 -0
  228. data/spec/spec_tests/data/client_side_operations_timeout/gridfs-find.yml +100 -0
  229. data/spec/spec_tests/data/client_side_operations_timeout/gridfs-upload.yml +249 -0
  230. data/spec/spec_tests/data/client_side_operations_timeout/legacy-timeouts.yml +204 -0
  231. data/spec/spec_tests/data/client_side_operations_timeout/non-tailable-cursors.yml +307 -0
  232. data/spec/spec_tests/data/client_side_operations_timeout/override-collection-timeoutMS.yml +1877 -0
  233. data/spec/spec_tests/data/client_side_operations_timeout/override-operation-timeoutMS.yml +1918 -0
  234. data/spec/spec_tests/data/client_side_operations_timeout/retryability-legacy-timeouts.yml +1676 -0
  235. data/spec/spec_tests/data/client_side_operations_timeout/retryability-timeoutMS.yml +2824 -0
  236. data/spec/spec_tests/data/client_side_operations_timeout/sessions-inherit-timeoutMS.yml +168 -0
  237. data/spec/spec_tests/data/client_side_operations_timeout/sessions-override-operation-timeoutMS.yml +171 -0
  238. data/spec/spec_tests/data/client_side_operations_timeout/sessions-override-timeoutMS.yml +168 -0
  239. data/spec/spec_tests/data/client_side_operations_timeout/tailable-awaitData.yml +247 -0
  240. data/spec/spec_tests/data/client_side_operations_timeout/tailable-non-awaitData.yml +181 -0
  241. data/spec/spec_tests/data/connection_string/invalid-uris.yml +0 -10
  242. data/spec/spec_tests/data/connection_string/valid-options.yml +13 -0
  243. data/spec/spec_tests/data/crud_unified/aggregate-write-readPreference.yml +6 -0
  244. data/spec/spec_tests/data/crud_unified/db-aggregate-write-readPreference.yml +6 -0
  245. data/spec/spec_tests/data/crud_unified/find-test-all-options.yml +377 -0
  246. data/spec/spec_tests/data/index_management/createSearchIndex.yml +64 -0
  247. data/spec/spec_tests/data/index_management/createSearchIndexes.yml +86 -0
  248. data/spec/spec_tests/data/index_management/dropSearchIndex.yml +43 -0
  249. data/spec/spec_tests/data/index_management/listSearchIndexes.yml +91 -0
  250. data/spec/spec_tests/data/index_management/updateSearchIndex.yml +46 -0
  251. data/spec/spec_tests/data/retryable_writes/unified/bulkWrite-serverErrors.yml +3 -6
  252. data/spec/spec_tests/data/retryable_writes/unified/insertOne-serverErrors.yml +3 -6
  253. data/spec/spec_tests/data/run_command_unified/runCommand.yml +319 -0
  254. data/spec/spec_tests/data/sessions_unified/driver-sessions-dirty-session-errors.yml +351 -0
  255. data/spec/spec_tests/data/unified/valid-pass/poc-crud.yml +1 -1
  256. data/spec/spec_tests/data/unified/valid-pass/poc-retryable-writes.yml +7 -7
  257. data/spec/spec_tests/data/unified/valid-pass/poc-sessions.yml +3 -4
  258. data/spec/spec_tests/data/unified/valid-pass/poc-transactions-convenient-api.yml +1 -1
  259. data/spec/spec_tests/data/unified/valid-pass/poc-transactions-mongos-pin-auto.yml +1 -1
  260. data/spec/spec_tests/data/unified/valid-pass/poc-transactions.yml +3 -3
  261. data/spec/spec_tests/index_management_unified_spec.rb +13 -0
  262. data/spec/spec_tests/run_command_unified_spec.rb +13 -0
  263. data/spec/spec_tests/sdam_unified_spec.rb +2 -0
  264. data/spec/spec_tests/server_selection_rtt_spec.rb +6 -6
  265. data/spec/spec_tests/transactions_unified_spec.rb +2 -1
  266. data/spec/support/certificates/atlas-ocsp-ca.crt +89 -79
  267. data/spec/support/certificates/atlas-ocsp.crt +117 -122
  268. data/spec/support/certificates/retrieve-atlas-cert +1 -1
  269. data/spec/support/cluster_tools.rb +3 -3
  270. data/spec/support/common_shortcuts.rb +2 -2
  271. data/spec/support/constraints.rb +6 -0
  272. data/spec/support/crypt/encrypted_fields/range-encryptedFields-Date.json +1 -1
  273. data/spec/support/crypt/encrypted_fields/range-encryptedFields-DecimalNoPrecision.json +1 -1
  274. data/spec/support/crypt/encrypted_fields/range-encryptedFields-DecimalPrecision.json +1 -1
  275. data/spec/support/crypt/encrypted_fields/range-encryptedFields-DoubleNoPrecision.json +1 -1
  276. data/spec/support/crypt/encrypted_fields/range-encryptedFields-DoublePrecision.json +1 -1
  277. data/spec/support/crypt/encrypted_fields/range-encryptedFields-Int.json +1 -1
  278. data/spec/support/crypt/encrypted_fields/range-encryptedFields-Long.json +1 -1
  279. data/spec/support/ocsp +1 -1
  280. data/spec/support/recording_logger.rb +27 -0
  281. data/spec/support/shared/session.rb +2 -2
  282. data/spec/support/spec_config.rb +5 -0
  283. data/spec/support/spec_setup.rb +2 -2
  284. data/spec/support/utils.rb +3 -1
  285. metadata +1329 -1368
  286. checksums.yaml.gz.sig +0 -0
  287. data/spec/mongo/server/round_trip_time_averager_spec.rb +0 -48
  288. data/spec/shared/LICENSE +0 -20
  289. data/spec/shared/bin/get-mongodb-download-url +0 -17
  290. data/spec/shared/bin/s3-copy +0 -45
  291. data/spec/shared/bin/s3-upload +0 -69
  292. data/spec/shared/lib/mrss/child_process_helper.rb +0 -80
  293. data/spec/shared/lib/mrss/cluster_config.rb +0 -231
  294. data/spec/shared/lib/mrss/constraints.rb +0 -378
  295. data/spec/shared/lib/mrss/docker_runner.rb +0 -295
  296. data/spec/shared/lib/mrss/eg_config_utils.rb +0 -51
  297. data/spec/shared/lib/mrss/event_subscriber.rb +0 -210
  298. data/spec/shared/lib/mrss/lite_constraints.rb +0 -238
  299. data/spec/shared/lib/mrss/server_version_registry.rb +0 -113
  300. data/spec/shared/lib/mrss/session_registry.rb +0 -69
  301. data/spec/shared/lib/mrss/session_registry_legacy.rb +0 -60
  302. data/spec/shared/lib/mrss/spec_organizer.rb +0 -179
  303. data/spec/shared/lib/mrss/utils.rb +0 -37
  304. data/spec/shared/share/Dockerfile.erb +0 -330
  305. data/spec/shared/share/haproxy-1.conf +0 -16
  306. data/spec/shared/share/haproxy-2.conf +0 -17
  307. data/spec/shared/shlib/config.sh +0 -27
  308. data/spec/shared/shlib/distro.sh +0 -74
  309. data/spec/shared/shlib/server.sh +0 -416
  310. data/spec/shared/shlib/set_env.sh +0 -169
  311. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Aggregate.yml +0 -241
  312. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Correctness.yml +0 -422
  313. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Delete.yml +0 -182
  314. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-FindOneAndUpdate.yml +0 -239
  315. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-InsertFind.yml +0 -235
  316. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Update.yml +0 -252
  317. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Aggregate.yml +0 -1687
  318. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Correctness.yml +0 -293
  319. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Delete.yml +0 -905
  320. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-FindOneAndUpdate.yml +0 -1684
  321. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-InsertFind.yml +0 -1680
  322. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Update.yml +0 -1697
  323. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Aggregate.yml +0 -329
  324. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Correctness.yml +0 -424
  325. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Delete.yml +0 -226
  326. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-FindOneAndUpdate.yml +0 -327
  327. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-InsertFind.yml +0 -319
  328. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Update.yml +0 -336
  329. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Aggregate.yml +0 -913
  330. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Correctness.yml +0 -292
  331. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Delete.yml +0 -518
  332. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-FindOneAndUpdate.yml +0 -911
  333. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-InsertFind.yml +0 -907
  334. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Update.yml +0 -924
  335. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Aggregate.yml +0 -325
  336. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Correctness.yml +0 -424
  337. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Delete.yml +0 -224
  338. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-FindOneAndUpdate.yml +0 -323
  339. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-InsertFind.yml +0 -319
  340. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Update.yml +0 -338
  341. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Aggregate.yml +0 -241
  342. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Correctness.yml +0 -423
  343. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Delete.yml +0 -182
  344. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-FindOneAndUpdate.yml +0 -239
  345. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-InsertFind.yml +0 -235
  346. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Update.yml +0 -254
  347. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Aggregate.yml +0 -241
  348. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Correctness.yml +0 -422
  349. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Delete.yml +0 -182
  350. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-FindOneAndUpdate.yml +0 -239
  351. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-InsertFind.yml +0 -235
  352. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Update.yml +0 -254
  353. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-WrongType.yml +0 -43
  354. data/spec/spec_tests/data/cmap/pool-clear-interrupt-immediately.yml +0 -49
  355. data.tar.gz.sig +0 -0
  356. metadata.gz.sig +0 -2
@@ -60,19 +60,21 @@ module Mongo
60
60
  # @param [ Mongo::ServerSelector::Selectable ] server_selector Server
61
61
  # selector for the operation.
62
62
  # @param [ CollectionView ] view The +CollectionView+ defining the query.
63
+ # @param [ Operation::Context | nil ] context the operation context to use
64
+ # with the cursor.
63
65
  # @param [ Proc ] block The block to execute.
64
66
  #
65
67
  # @return [ Cursor ] The cursor for the result set.
66
- def read_with_retry_cursor(session, server_selector, view, &block)
67
- read_with_retry(session, server_selector) do |server|
68
+ def read_with_retry_cursor(session, server_selector, view, context: nil, &block)
69
+ read_with_retry(session, server_selector, context) do |server|
68
70
  result = yield server
69
71
 
70
72
  # RUBY-2367: This will be updated to allow the query cache to
71
73
  # cache cursors with multi-batch results.
72
74
  if QueryCache.enabled? && !view.collection.system_collection?
73
- CachingCursor.new(view, result, server, session: session)
75
+ CachingCursor.new(view, result, server, session: session, context: context)
74
76
  else
75
- Cursor.new(view, result, server, session: session)
77
+ Cursor.new(view, result, server, session: session, context: context)
76
78
  end
77
79
  end
78
80
  end
@@ -107,16 +109,18 @@ module Mongo
107
109
  # is being run on.
108
110
  # @param [ Mongo::ServerSelector::Selectable | nil ] server_selector
109
111
  # Server selector for the operation.
112
+ # @param [ Mongo::Operation::Context | nil ] context Context for the
113
+ # read operation.
110
114
  # @param [ Proc ] block The block to execute.
111
115
  #
112
116
  # @return [ Result ] The result of the operation.
113
- def read_with_retry(session = nil, server_selector = nil, &block)
117
+ def read_with_retry(session = nil, server_selector = nil, context = nil, &block)
114
118
  if session.nil? && server_selector.nil?
115
119
  deprecated_legacy_read_with_retry(&block)
116
120
  elsif session&.retry_reads?
117
- modern_read_with_retry(session, server_selector, &block)
121
+ modern_read_with_retry(session, server_selector, context, &block)
118
122
  elsif client.max_read_retries > 0
119
- legacy_read_with_retry(session, server_selector, &block)
123
+ legacy_read_with_retry(session, server_selector, context, &block)
120
124
  else
121
125
  read_without_retry(session, server_selector, &block)
122
126
  end
@@ -186,18 +190,26 @@ module Mongo
186
190
  # being run on.
187
191
  # @param [ Mongo::ServerSelector::Selectable ] server_selector Server
188
192
  # selector for the operation.
193
+ # @param [ Mongo::Operation::Context ] context Context for the
194
+ # read operation.
189
195
  # @param [ Proc ] block The block to execute.
190
196
  #
191
197
  # @return [ Result ] The result of the operation.
192
- def modern_read_with_retry(session, server_selector, &block)
193
- yield select_server(cluster, server_selector, session)
194
- rescue *retryable_exceptions, Error::OperationFailure, Auth::Unauthorized, Error::PoolError => e
198
+ def modern_read_with_retry(session, server_selector, context, &block)
199
+ server = select_server(
200
+ cluster,
201
+ server_selector,
202
+ session,
203
+ timeout: context&.remaining_timeout_sec
204
+ )
205
+ yield server
206
+ rescue *retryable_exceptions, Error::OperationFailure::Family, Auth::Unauthorized, Error::PoolError => e
195
207
  e.add_notes('modern retry', 'attempt 1')
196
208
  raise e if session.in_transaction?
197
209
  raise e if !is_retryable_exception?(e) && !e.write_retryable?
198
- retry_read(e, session, server_selector, &block)
210
+ retry_read(e, session, server_selector, context: context, failed_server: server, &block)
199
211
  end
200
-
212
+
201
213
  # Attempts to do a "legacy" read with retry. The operation will be
202
214
  # attempted multiple times, up to the client's `max_read_retries`
203
215
  # setting.
@@ -206,23 +218,26 @@ module Mongo
206
218
  # being run on.
207
219
  # @param [ Mongo::ServerSelector::Selectable ] server_selector Server
208
220
  # selector for the operation.
221
+ # @param [ Mongo::Operation::Context | nil ] context Context for the
222
+ # read operation.
209
223
  # @param [ Proc ] block The block to execute.
210
224
  #
211
225
  # @return [ Result ] The result of the operation.
212
- def legacy_read_with_retry(session, server_selector, &block)
226
+ def legacy_read_with_retry(session, server_selector, context = nil, &block)
227
+ context&.check_timeout!
213
228
  attempt = attempt ? attempt + 1 : 1
214
229
  yield select_server(cluster, server_selector, session)
215
- rescue *retryable_exceptions, Error::OperationFailure, Error::PoolError => e
230
+ rescue *legacy_retryable_exceptions, Error::OperationFailure::Family => e
216
231
  e.add_notes('legacy retry', "attempt #{attempt}")
217
-
218
- if is_retryable_exception?(e)
232
+
233
+ if is_legacy_retryable_exception?(e)
219
234
  raise e if attempt > client.max_read_retries || session&.in_transaction?
220
235
  elsif e.retryable? && !session&.in_transaction?
221
236
  raise e if attempt > client.max_read_retries
222
237
  else
223
238
  raise e
224
239
  end
225
-
240
+
226
241
  log_retry(e, message: 'Legacy read retry')
227
242
  sleep(client.read_retry_interval) unless is_retryable_exception?(e)
228
243
  retry
@@ -243,7 +258,7 @@ module Mongo
243
258
 
244
259
  begin
245
260
  yield server
246
- rescue *retryable_exceptions, Error::PoolError, Error::OperationFailure => e
261
+ rescue *retryable_exceptions, Error::PoolError, Error::OperationFailure::Family => e
247
262
  e.add_note('retries disabled')
248
263
  raise e
249
264
  end
@@ -257,40 +272,67 @@ module Mongo
257
272
  # being run on.
258
273
  # @param [ Mongo::ServerSelector::Selectable ] server_selector Server
259
274
  # selector for the operation.
275
+ # @param [ Mongo::Operation::Context | nil ] :context Context for the
276
+ # read operation.
277
+ # @param [ Mongo::Server | nil ] :failed_server The server on which the original
278
+ # operation failed.
260
279
  # @param [ Proc ] block The block to execute.
261
- #
280
+ #
262
281
  # @return [ Result ] The result of the operation.
263
- def retry_read(original_error, session, server_selector, &block)
264
- begin
265
- server = select_server(cluster, server_selector, session)
266
- rescue Error, Error::AuthError => e
267
- original_error.add_note("later retry failed: #{e.class}: #{e}")
268
- raise original_error
269
- end
270
-
282
+ def retry_read(original_error, session, server_selector, context: nil, failed_server: nil, &block)
283
+ server = select_server_for_retry(
284
+ original_error, session, server_selector, context, failed_server
285
+ )
286
+
271
287
  log_retry(original_error, message: 'Read retry')
272
-
288
+
273
289
  begin
290
+ context&.check_timeout!
291
+ attempt = attempt ? attempt + 1 : 2
274
292
  yield server, true
293
+ rescue Error::TimeoutError
294
+ raise
275
295
  rescue *retryable_exceptions => e
276
- e.add_notes('modern retry', 'attempt 2')
277
- raise e
278
- rescue Error::OperationFailure, Error::PoolError => e
296
+ e.add_notes('modern retry', "attempt #{attempt}")
297
+ if context&.csot?
298
+ failed_server = server
299
+ retry
300
+ else
301
+ raise e
302
+ end
303
+ rescue Error::OperationFailure::Family, Error::PoolError => e
279
304
  e.add_note('modern retry')
280
- unless e.write_retryable?
305
+ if e.write_retryable?
306
+ e.add_note("attempt #{attempt}")
307
+ if context&.csot?
308
+ failed_server = server
309
+ retry
310
+ else
311
+ raise e
312
+ end
313
+ else
281
314
  original_error.add_note("later retry failed: #{e.class}: #{e}")
282
315
  raise original_error
283
316
  end
284
- e.add_note("attempt 2")
285
- raise e
286
317
  rescue Error, Error::AuthError => e
287
318
  e.add_note('modern retry')
288
319
  original_error.add_note("later retry failed: #{e.class}: #{e}")
289
320
  raise original_error
290
321
  end
291
322
  end
292
-
293
- end
294
323
 
324
+ def select_server_for_retry(original_error, session, server_selector, context, failed_server)
325
+ select_server(
326
+ cluster,
327
+ server_selector,
328
+ session,
329
+ failed_server,
330
+ timeout: context&.remaining_timeout_sec
331
+ )
332
+ rescue Error, Error::AuthError => e
333
+ original_error.add_note("later retry failed: #{e.class}: #{e}")
334
+ raise original_error
335
+ end
336
+ end
295
337
  end
296
338
  end
@@ -74,7 +74,11 @@ module Mongo
74
74
  # If we are here, session is not nil. A session being nil would have
75
75
  # failed retry_write_allowed? check.
76
76
 
77
- server = select_server(cluster, ServerSelector.primary, session)
77
+ server = select_server(
78
+ cluster, ServerSelector.primary,
79
+ session,
80
+ timeout: context.remaining_timeout_sec
81
+ )
78
82
 
79
83
  unless ending_transaction || server.retry_writes?
80
84
  return legacy_write_with_retry(server, context: context, &block)
@@ -103,13 +107,14 @@ module Mongo
103
107
  def nro_write_with_retry(write_concern, context:, &block)
104
108
  session = context.session
105
109
  server = select_server(cluster, ServerSelector.primary, session)
106
-
107
- if session&.client.options[:retry_writes]
110
+ options = session&.client&.options || {}
111
+
112
+ if options[:retry_writes]
108
113
  begin
109
114
  server.with_connection(connection_global_id: context.connection_global_id) do |connection|
110
115
  yield connection, nil, context
111
116
  end
112
- rescue *retryable_exceptions, Error::PoolError, Error::OperationFailure => e
117
+ rescue *retryable_exceptions, Error::PoolError, Error::OperationFailure::Family => e
113
118
  e.add_note('retries disabled')
114
119
  raise e
115
120
  end
@@ -169,6 +174,7 @@ module Mongo
169
174
  # @api private
170
175
  def legacy_write_with_retry(server = nil, context:)
171
176
  session = context.session
177
+ context.check_timeout!
172
178
 
173
179
  # This is the pre-session retry logic, and is not subject to
174
180
  # current retryable write specifications.
@@ -176,12 +182,20 @@ module Mongo
176
182
  attempt = 0
177
183
  begin
178
184
  attempt += 1
179
- server ||= select_server(cluster, ServerSelector.primary, session)
180
- server.with_connection(connection_global_id: context.connection_global_id) do |connection|
185
+ server ||= select_server(
186
+ cluster,
187
+ ServerSelector.primary,
188
+ session,
189
+ timeout: context.remaining_timeout_sec
190
+ )
191
+ server.with_connection(
192
+ connection_global_id: context.connection_global_id,
193
+ context: context
194
+ ) do |connection|
181
195
  # Legacy retries do not use txn_num
182
196
  yield connection, nil, context.dup
183
197
  end
184
- rescue Error::OperationFailure => e
198
+ rescue Error::OperationFailure::Family => e
185
199
  e.add_note('legacy retry')
186
200
  e.add_note("attempt #{attempt}")
187
201
  server = nil
@@ -218,8 +232,11 @@ module Mongo
218
232
  def modern_write_with_retry(session, server, context, &block)
219
233
  txn_num = nil
220
234
  connection_succeeded = false
221
-
222
- server.with_connection(connection_global_id: context.connection_global_id) do |connection|
235
+
236
+ server.with_connection(
237
+ connection_global_id: context.connection_global_id,
238
+ context: context
239
+ ) do |connection|
223
240
  connection_succeeded = true
224
241
 
225
242
  session.materialize_if_needed
@@ -229,10 +246,10 @@ module Mongo
229
246
  # it later for the retry as well.
230
247
  yield connection, txn_num, context.dup
231
248
  end
232
- rescue *retryable_exceptions, Error::PoolError, Auth::Unauthorized, Error::OperationFailure => e
249
+ rescue *retryable_exceptions, Error::PoolError, Auth::Unauthorized, Error::OperationFailure::Family => e
233
250
  e.add_notes('modern retry', 'attempt 1')
234
251
 
235
- if e.is_a?(Error::OperationFailure)
252
+ if e.is_a?(Error::OperationFailure::Family)
236
253
  ensure_retryable!(e)
237
254
  else
238
255
  ensure_labeled_retryable!(e, connection_succeeded, session)
@@ -240,7 +257,7 @@ module Mongo
240
257
 
241
258
  # Context#with creates a new context, which is not necessary here
242
259
  # but the API is less prone to misuse this way.
243
- retry_write(e, txn_num, context: context.with(is_retry: true), &block)
260
+ retry_write(e, txn_num, context: context.with(is_retry: true), failed_server: server, &block)
244
261
  end
245
262
 
246
263
  # Called after a failed write, this will retry the write no more than
@@ -250,9 +267,13 @@ module Mongo
250
267
  # retry.
251
268
  # @param [ Number ] txn_num The transaction number.
252
269
  # @param [ Operation::Context ] context The context for the operation.
270
+ # @param [ Mongo::Server ] failed_server The server on which the original
271
+ # operation failed.
253
272
  #
254
273
  # @return [ Result ] The result of the operation.
255
- def retry_write(original_error, txn_num, context:, &block)
274
+ def retry_write(original_error, txn_num, context:, failed_server: nil, &block)
275
+ context&.check_timeout!
276
+
256
277
  session = context.session
257
278
 
258
279
  # We do not request a scan of the cluster here, because error handling
@@ -260,8 +281,14 @@ module Mongo
260
281
  # server description and/or topology as necessary (specifically,
261
282
  # a socket error or a not master error should have marked the respective
262
283
  # server unknown). Here we just need to wait for server selection.
263
- server = select_server(cluster, ServerSelector.primary, session)
264
-
284
+ server = select_server(
285
+ cluster,
286
+ ServerSelector.primary,
287
+ session,
288
+ failed_server,
289
+ timeout: context.remaining_timeout_sec
290
+ )
291
+
265
292
  unless server.retry_writes?
266
293
  # Do not need to add "modern retry" here, it should already be on
267
294
  # the first exception.
@@ -275,15 +302,22 @@ module Mongo
275
302
  # special marker class to bypass the ordinarily applicable rescues.
276
303
  raise Error::RaiseOriginalError
277
304
  end
278
-
305
+
306
+ attempt = attempt ? attempt + 1 : 2
279
307
  log_retry(original_error, message: 'Write retry')
280
308
  server.with_connection(connection_global_id: context.connection_global_id) do |connection|
281
309
  yield(connection, txn_num, context)
282
310
  end
283
311
  rescue *retryable_exceptions, Error::PoolError => e
284
- fail_on_retryable!(e, original_error)
285
- rescue Error::OperationFailure => e
286
- fail_on_operation_failure!(e, original_error)
312
+ maybe_fail_on_retryable(e, original_error, context, attempt)
313
+ failed_server = server
314
+ retry
315
+ rescue Error::OperationFailure::Family => e
316
+ maybe_fail_on_operation_failure(e, original_error, context, attempt)
317
+ failed_server = server
318
+ retry
319
+ rescue Mongo::Error::TimeoutError
320
+ raise
287
321
  rescue Error, Error::AuthError => e
288
322
  fail_on_other_error!(e, original_error)
289
323
  rescue Error::RaiseOriginalError
@@ -329,10 +363,10 @@ module Mongo
329
363
 
330
364
  # Raise either e, or original_error, depending on whether e is
331
365
  # write_retryable.
332
- def fail_on_retryable!(e, original_error)
366
+ def maybe_fail_on_retryable(e, original_error, context, attempt)
333
367
  if e.write_retryable?
334
- e.add_notes('modern retry', 'attempt 2')
335
- raise e
368
+ e.add_notes('modern retry', "attempt #{attempt}")
369
+ raise e unless context&.deadline
336
370
  else
337
371
  original_error.add_note("later retry failed: #{e.class}: #{e}")
338
372
  raise original_error
@@ -341,11 +375,11 @@ module Mongo
341
375
 
342
376
  # Raise either e, or original_error, depending on whether e is
343
377
  # appropriately labeled.
344
- def fail_on_operation_failure!(e, original_error)
378
+ def maybe_fail_on_operation_failure(e, original_error, context, attempt)
345
379
  e.add_note('modern retry')
346
380
  if e.label?('RetryableWriteError') && !e.label?('NoWritesPerformed')
347
- e.add_note('attempt 2')
348
- raise e
381
+ e.add_note("attempt #{attempt}")
382
+ raise e unless context&.deadline
349
383
  else
350
384
  original_error.add_note("later retry failed: #{e.class}: #{e}")
351
385
  raise original_error
@@ -46,8 +46,14 @@ module Mongo
46
46
  # @api private
47
47
  #
48
48
  # @return [ Mongo::Server ] A server matching the server preference.
49
- def select_server(cluster, server_selector, session)
50
- server_selector.select_server(cluster, nil, session)
49
+ def select_server(cluster, server_selector, session, failed_server = nil, timeout: nil)
50
+ server_selector.select_server(
51
+ cluster,
52
+ nil,
53
+ session,
54
+ deprioritized: [failed_server].compact,
55
+ timeout: timeout
56
+ )
51
57
  end
52
58
 
53
59
  # Returns the read worker for handling retryable reads.
@@ -0,0 +1,232 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mongo
4
+ module SearchIndex
5
+ # A class representing a view of search indexes.
6
+ class View
7
+ include Enumerable
8
+ include Retryable
9
+ include Collection::Helpers
10
+
11
+ # @return [ Mongo::Collection ] the collection this view belongs to
12
+ attr_reader :collection
13
+
14
+ # @return [ nil | String ] the index id to query
15
+ attr_reader :requested_index_id
16
+
17
+ # @return [ nil | String ] the index name to query
18
+ attr_reader :requested_index_name
19
+
20
+ # @return [ Hash ] the options hash to use for the aggregate command
21
+ # when querying the available indexes.
22
+ attr_reader :aggregate_options
23
+
24
+ # Create the new search index view.
25
+ #
26
+ # @param [ Collection ] collection The collection.
27
+ # @param [ Hash ] options The options that configure the behavior of the view.
28
+ #
29
+ # @option options [ String ] :id The specific index id to query (optional)
30
+ # @option options [ String ] :name The name of the specific index to query (optional)
31
+ # @option options [ Hash ] :aggregate The options hash to send to the
32
+ # aggregate command when querying the available indexes.
33
+ def initialize(collection, options = {})
34
+ @collection = collection
35
+ @requested_index_id = options[:id]
36
+ @requested_index_name = options[:name]
37
+ @aggregate_options = options[:aggregate] || {}
38
+
39
+ return if @aggregate_options.is_a?(Hash)
40
+
41
+ raise ArgumentError, "The :aggregate option must be a Hash (got a #{@aggregate_options.class})"
42
+ end
43
+
44
+ # Create a single search index with the given definition. If the name is
45
+ # provided, the new index will be given that name.
46
+ #
47
+ # @param [ Hash ] definition The definition of the search index.
48
+ # @param [ nil | String ] name The name to give the new search index.
49
+ #
50
+ # @return [ String ] the name of the new search index.
51
+ def create_one(definition, name: nil)
52
+ create_many([ { name: name, definition: definition } ]).first
53
+ end
54
+
55
+ # Create multiple search indexes with a single command.
56
+ #
57
+ # @param [ Array<Hash> ] indexes The description of the indexes to
58
+ # create. Each element of the list must be a hash with a definition
59
+ # key, and an optional name key.
60
+ #
61
+ # @return [ Array<String> ] the names of the new search indexes.
62
+ def create_many(indexes)
63
+ spec = spec_with(indexes: indexes.map { |v| validate_search_index!(v) })
64
+ result = Operation::CreateSearchIndexes.new(spec).execute(next_primary, context: execution_context)
65
+ result.first['indexesCreated'].map { |idx| idx['name'] }
66
+ end
67
+
68
+ # Drop the search index with the given id, or name. One or the other must
69
+ # be specified, but not both.
70
+ #
71
+ # @param [ String ] id the id of the index to drop
72
+ # @param [ String ] name the name of the index to drop
73
+ #
74
+ # @return [ Mongo::Operation::Result | false ] the result of the
75
+ # operation, or false if the given index does not exist.
76
+ def drop_one(id: nil, name: nil)
77
+ validate_id_or_name!(id, name)
78
+
79
+ spec = spec_with(index_id: id, index_name: name)
80
+ op = Operation::DropSearchIndex.new(spec)
81
+
82
+ # per the spec:
83
+ # Drivers MUST suppress NamespaceNotFound errors for the
84
+ # ``dropSearchIndex`` helper. Drop operations should be idempotent.
85
+ do_drop(op, nil, execution_context)
86
+ end
87
+
88
+ # Iterate over the search indexes.
89
+ #
90
+ # @param [ Proc ] block if given, each search index will be yieleded to
91
+ # the block.
92
+ #
93
+ # @return [ self | Enumerator ] if a block is given, self is returned.
94
+ # Otherwise, an enumerator will be returned.
95
+ def each(&block)
96
+ @result ||= begin
97
+ spec = {}.tap do |s|
98
+ s[:id] = requested_index_id if requested_index_id
99
+ s[:name] = requested_index_name if requested_index_name
100
+ end
101
+
102
+ collection.aggregate(
103
+ [ { '$listSearchIndexes' => spec } ],
104
+ aggregate_options
105
+ )
106
+ end
107
+
108
+ return @result.to_enum unless block
109
+
110
+ @result.each(&block)
111
+ self
112
+ end
113
+
114
+ # Update the search index with the given id or name. One or the other
115
+ # must be provided, but not both.
116
+ #
117
+ # @param [ Hash ] definition the definition to replace the given search
118
+ # index with.
119
+ # @param [ nil | String ] id the id of the search index to update
120
+ # @param [ nil | String ] name the name of the search index to update
121
+ #
122
+ # @return [ Mongo::Operation::Result ] the result of the operation
123
+ def update_one(definition, id: nil, name: nil)
124
+ validate_id_or_name!(id, name)
125
+
126
+ spec = spec_with(index_id: id, index_name: name, index: definition)
127
+ Operation::UpdateSearchIndex.new(spec).execute(next_primary, context: execution_context)
128
+ end
129
+
130
+ # The following methods are to make the view act more like an array,
131
+ # without having to explicitly make it an array...
132
+
133
+ # Queries whether the search index enumerable is empty.
134
+ #
135
+ # @return [ true | false ] whether the enumerable is empty or not.
136
+ def empty?
137
+ count.zero?
138
+ end
139
+
140
+ private
141
+
142
+ # A helper method for building the specification document with certain
143
+ # values pre-populated.
144
+ #
145
+ # @param [ Hash ] extras the values to put into the specification
146
+ #
147
+ # @return [ Hash ] the specification document
148
+ def spec_with(extras)
149
+ {
150
+ coll_name: collection.name,
151
+ db_name: collection.database.name,
152
+ }.merge(extras)
153
+ end
154
+
155
+ # A helper method for retrieving the primary server from the cluster.
156
+ #
157
+ # @return [ Mongo::Server ] the server to use
158
+ def next_primary(ping = nil, session = nil)
159
+ collection.cluster.next_primary(ping, session)
160
+ end
161
+
162
+ # A helper method for constructing a new operation context for executing
163
+ # an operation.
164
+ #
165
+ # @return [ Mongo::Operation::Context ] the operation context
166
+ def execution_context
167
+ Operation::Context.new(client: collection.client)
168
+ end
169
+
170
+ # Validates the given id and name, ensuring that exactly one of them
171
+ # is non-nil.
172
+ #
173
+ # @param [ nil | String ] id the id to validate
174
+ # @param [ nil | String ] name the name to validate
175
+ #
176
+ # @raise [ ArgumentError ] if neither or both arguments are nil
177
+ def validate_id_or_name!(id, name)
178
+ return unless (id.nil? && name.nil?) || (!id.nil? && !name.nil?)
179
+
180
+ raise ArgumentError, 'exactly one of id or name must be specified'
181
+ end
182
+
183
+ # Validates the given search index document, ensuring that it has no
184
+ # extra keys, and that the name and definition are valid.
185
+ #
186
+ # @param [ Hash ] doc the document to validate
187
+ #
188
+ # @raise [ ArgumentError ] if the document is invalid.
189
+ def validate_search_index!(doc)
190
+ validate_search_index_keys!(doc.keys)
191
+ validate_search_index_name!(doc[:name] || doc['name'])
192
+ validate_search_index_definition!(doc[:definition] || doc['definition'])
193
+ doc
194
+ end
195
+
196
+ # Validates the keys of a search index document, ensuring that
197
+ # they are all valid.
198
+ #
199
+ # @param [ Array<String | Hash> ] keys the keys of a search index document
200
+ #
201
+ # @raise [ ArgumentError ] if the list contains any invalid keys
202
+ def validate_search_index_keys!(keys)
203
+ extras = keys - [ 'name', 'definition', :name, :definition ]
204
+
205
+ raise ArgumentError, "invalid keys in search index creation: #{extras.inspect}" if extras.any?
206
+ end
207
+
208
+ # Validates the name of a search index, ensuring that it is either a
209
+ # String or nil.
210
+ #
211
+ # @param [ nil | String ] name the name of a search index
212
+ #
213
+ # @raise [ ArgumentError ] if the name is not valid
214
+ def validate_search_index_name!(name)
215
+ return if name.nil? || name.is_a?(String)
216
+
217
+ raise ArgumentError, "search index name must be nil or a string (got #{name.inspect})"
218
+ end
219
+
220
+ # Validates the definition of a search index.
221
+ #
222
+ # @param [ Hash ] definition the definition of a search index
223
+ #
224
+ # @raise [ ArgumentError ] if the definition is not valid
225
+ def validate_search_index_definition!(definition)
226
+ return if definition.is_a?(Hash)
227
+
228
+ raise ArgumentError, "search index definition must be a Hash (got #{definition.inspect})"
229
+ end
230
+ end
231
+ end
232
+ end