mongo 2.1.0.beta → 2.2.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 (342) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/README.md +10 -3
  4. data/Rakefile +1 -7
  5. data/lib/mongo/address/ipv4.rb +6 -1
  6. data/lib/mongo/address/unix.rb +2 -2
  7. data/lib/mongo/address.rb +32 -10
  8. data/lib/mongo/auth/cr/conversation.rb +1 -1
  9. data/lib/mongo/auth/ldap/conversation.rb +7 -3
  10. data/lib/mongo/auth/scram/conversation.rb +9 -3
  11. data/lib/mongo/auth/user/view.rb +23 -2
  12. data/lib/mongo/auth/x509/conversation.rb +1 -1
  13. data/lib/mongo/bulk_write/combineable.rb +51 -0
  14. data/lib/mongo/bulk_write/ordered_combiner.rb +55 -0
  15. data/lib/mongo/bulk_write/result.rb +61 -8
  16. data/lib/mongo/bulk_write/result_combiner.rb +117 -0
  17. data/lib/mongo/bulk_write/transformable.rb +132 -0
  18. data/lib/mongo/bulk_write/unordered_combiner.rb +52 -0
  19. data/lib/mongo/bulk_write/validatable.rb +62 -0
  20. data/lib/mongo/bulk_write.rb +164 -23
  21. data/lib/mongo/client.rb +75 -18
  22. data/lib/mongo/cluster/topology/replica_set.rb +8 -6
  23. data/lib/mongo/cluster/topology/unknown.rb +5 -2
  24. data/lib/mongo/cluster.rb +85 -5
  25. data/lib/mongo/collection/view/aggregation.rb +19 -45
  26. data/lib/mongo/collection/view/builder/aggregation.rb +98 -0
  27. data/lib/mongo/collection/view/builder/find_command.rb +111 -0
  28. data/lib/mongo/collection/view/builder/flags.rb +62 -0
  29. data/lib/mongo/collection/view/builder/map_reduce.rb +134 -0
  30. data/lib/mongo/collection/view/builder/modifiers.rb +80 -0
  31. data/lib/mongo/collection/view/builder/op_query.rb +83 -0
  32. data/lib/mongo/collection/view/builder.rb +20 -0
  33. data/lib/mongo/collection/view/explainable.rb +15 -0
  34. data/lib/mongo/collection/view/immutable.rb +4 -11
  35. data/lib/mongo/collection/view/iterable.rb +40 -5
  36. data/lib/mongo/collection/view/map_reduce.rb +67 -37
  37. data/lib/mongo/collection/view/readable.rb +114 -100
  38. data/lib/mongo/collection/view/writable.rb +46 -22
  39. data/lib/mongo/collection/view.rb +25 -22
  40. data/lib/mongo/collection.rb +130 -12
  41. data/lib/mongo/cursor/builder/get_more_command.rb +71 -0
  42. data/lib/mongo/cursor/builder/kill_cursors_command.rb +62 -0
  43. data/lib/mongo/cursor/builder/op_get_more.rb +61 -0
  44. data/lib/mongo/cursor/builder/op_kill_cursors.rb +56 -0
  45. data/lib/mongo/cursor/builder.rb +18 -0
  46. data/lib/mongo/cursor.rb +76 -21
  47. data/lib/mongo/database/view.rb +11 -6
  48. data/lib/mongo/database.rb +16 -6
  49. data/lib/mongo/dbref.rb +9 -9
  50. data/lib/mongo/{bulk_write/unordered_bulk_write.rb → error/closed_stream.rb} +12 -21
  51. data/lib/mongo/{bulk_write/ordered_bulk_write.rb → error/extra_file_chunk.rb} +13 -27
  52. data/lib/mongo/error/file_not_found.rb +37 -0
  53. data/lib/mongo/error/invalid_file.rb +2 -2
  54. data/lib/mongo/error/invalid_file_revision.rb +37 -0
  55. data/lib/mongo/error/invalid_uri.rb +5 -4
  56. data/lib/mongo/error/invalid_write_concern.rb +35 -0
  57. data/lib/mongo/error/missing_file_chunk.rb +38 -0
  58. data/lib/mongo/error/operation_failure.rb +33 -2
  59. data/lib/mongo/error/unchangeable_collection_option.rb +38 -0
  60. data/lib/mongo/error/unexpected_chunk_length.rb +39 -0
  61. data/lib/mongo/error.rb +8 -0
  62. data/lib/mongo/grid/file/chunk.rb +9 -9
  63. data/lib/mongo/grid/file/{metadata.rb → info.rb} +41 -39
  64. data/lib/mongo/grid/file.rb +12 -9
  65. data/lib/mongo/grid/fs_bucket.rb +448 -0
  66. data/lib/mongo/grid/stream/read.rb +208 -0
  67. data/lib/mongo/grid/stream/write.rb +187 -0
  68. data/lib/mongo/grid/stream.rb +64 -0
  69. data/lib/mongo/grid.rb +2 -1
  70. data/lib/mongo/index/view.rb +7 -4
  71. data/lib/mongo/index.rb +5 -0
  72. data/lib/mongo/loggable.rb +34 -57
  73. data/lib/mongo/logger.rb +16 -78
  74. data/lib/mongo/monitoring/command_log_subscriber.rb +38 -14
  75. data/lib/mongo/monitoring/event/command_started.rb +2 -1
  76. data/lib/mongo/monitoring/event/command_succeeded.rb +24 -2
  77. data/lib/mongo/monitoring/event/secure.rb +58 -0
  78. data/lib/mongo/monitoring/event.rb +1 -0
  79. data/lib/mongo/monitoring/publishable.rb +22 -12
  80. data/lib/mongo/monitoring.rb +1 -5
  81. data/lib/mongo/operation/commands/aggregate/result.rb +89 -0
  82. data/lib/mongo/operation/commands/aggregate.rb +64 -0
  83. data/lib/mongo/operation/commands/collections_info/result.rb +41 -0
  84. data/lib/mongo/operation/{read → commands}/collections_info.rb +5 -3
  85. data/lib/mongo/operation/commands/command.rb +47 -0
  86. data/lib/mongo/operation/commands/find/result.rb +62 -0
  87. data/lib/mongo/operation/commands/find.rb +27 -0
  88. data/lib/mongo/operation/commands/get_more/result.rb +62 -0
  89. data/lib/mongo/operation/commands/get_more.rb +27 -0
  90. data/lib/mongo/operation/{read → commands}/indexes.rb +9 -6
  91. data/lib/mongo/operation/{list_collections → commands/list_collections}/result.rb +1 -21
  92. data/lib/mongo/operation/{read → commands}/list_collections.rb +4 -32
  93. data/lib/mongo/operation/{list_indexes → commands/list_indexes}/result.rb +1 -21
  94. data/lib/mongo/operation/{read → commands}/list_indexes.rb +3 -33
  95. data/lib/mongo/operation/commands/map_reduce/result.rb +119 -0
  96. data/lib/mongo/operation/commands/map_reduce.rb +49 -0
  97. data/lib/mongo/operation/commands/parallel_scan/result.rb +64 -0
  98. data/lib/mongo/operation/commands/parallel_scan.rb +52 -0
  99. data/lib/mongo/operation/commands/user_query.rb +71 -0
  100. data/lib/mongo/operation/commands/users_info/result.rb +38 -0
  101. data/lib/mongo/operation/commands/users_info.rb +48 -0
  102. data/lib/mongo/operation/commands.rb +26 -0
  103. data/lib/mongo/operation/executable.rb +4 -68
  104. data/lib/mongo/operation/kill_cursors.rb +3 -3
  105. data/lib/mongo/operation/object_id_generator.rb +36 -0
  106. data/lib/mongo/operation/read/get_more.rb +2 -22
  107. data/lib/mongo/operation/read/query/result.rb +40 -0
  108. data/lib/mongo/operation/read/query.rb +4 -21
  109. data/lib/mongo/operation/read.rb +0 -4
  110. data/lib/mongo/operation/{read_preferrable.rb → read_preference.rb} +3 -2
  111. data/lib/mongo/operation/result.rb +43 -1
  112. data/lib/mongo/operation/specifiable.rb +59 -1
  113. data/lib/mongo/operation/write/bulk/bulkable.rb +83 -0
  114. data/lib/mongo/operation/write/bulk/delete/result.rb +67 -0
  115. data/lib/mongo/operation/write/bulk/delete.rb +71 -0
  116. data/lib/mongo/operation/write/bulk/insert/result.rb +129 -0
  117. data/lib/mongo/operation/write/bulk/insert.rb +96 -0
  118. data/lib/mongo/operation/write/bulk/legacy_mergable.rb +87 -0
  119. data/lib/mongo/operation/write/bulk/mergable.rb +71 -0
  120. data/lib/mongo/operation/write/bulk/update/result.rb +174 -0
  121. data/lib/mongo/operation/write/bulk/update.rb +81 -0
  122. data/lib/mongo/operation/write/bulk.rb +6 -3
  123. data/lib/mongo/operation/write/command/create_index.rb +0 -1
  124. data/lib/mongo/operation/write/command/create_user.rb +0 -1
  125. data/lib/mongo/operation/write/command/delete.rb +3 -3
  126. data/lib/mongo/operation/write/command/drop_index.rb +0 -1
  127. data/lib/mongo/operation/write/command/insert.rb +4 -3
  128. data/lib/mongo/operation/write/command/remove_user.rb +0 -1
  129. data/lib/mongo/operation/write/command/update.rb +6 -4
  130. data/lib/mongo/operation/write/command/update_user.rb +0 -1
  131. data/lib/mongo/operation/write/command/writable.rb +13 -18
  132. data/lib/mongo/operation/write/create_index.rb +4 -27
  133. data/lib/mongo/operation/write/create_user.rb +4 -30
  134. data/lib/mongo/operation/write/delete.rb +6 -29
  135. data/lib/mongo/operation/write/drop_index.rb +3 -3
  136. data/lib/mongo/operation/write/gle.rb +49 -0
  137. data/lib/mongo/operation/write/idable.rb +24 -2
  138. data/lib/mongo/operation/write/insert.rb +2 -24
  139. data/lib/mongo/operation/write/remove_user.rb +4 -27
  140. data/lib/mongo/operation/write/update.rb +13 -36
  141. data/lib/mongo/operation/write/update_user.rb +4 -30
  142. data/lib/mongo/operation/write/write_command_enabled.rb +53 -0
  143. data/lib/mongo/operation/write.rb +2 -0
  144. data/lib/mongo/operation.rb +33 -5
  145. data/lib/mongo/options/mapper.rb +26 -2
  146. data/lib/mongo/options/redacted.rb +156 -0
  147. data/lib/mongo/options.rb +1 -0
  148. data/lib/mongo/protocol/bit_vector.rb +11 -9
  149. data/lib/mongo/protocol/delete.rb +78 -3
  150. data/lib/mongo/protocol/get_more.rb +59 -2
  151. data/lib/mongo/protocol/insert.rb +73 -1
  152. data/lib/mongo/protocol/kill_cursors.rb +66 -4
  153. data/lib/mongo/protocol/message.rb +44 -20
  154. data/lib/mongo/protocol/query.rb +153 -65
  155. data/lib/mongo/protocol/reply.rb +92 -1
  156. data/lib/mongo/protocol/serializers.rb +49 -40
  157. data/lib/mongo/protocol/update.rb +93 -1
  158. data/lib/mongo/retryable.rb +101 -0
  159. data/lib/mongo/server/connectable.rb +28 -8
  160. data/lib/mongo/server/connection.rb +52 -10
  161. data/lib/mongo/server/connection_pool/queue.rb +15 -0
  162. data/lib/mongo/server/connection_pool.rb +12 -15
  163. data/lib/mongo/server/description/features.rb +4 -2
  164. data/lib/mongo/server/description.rb +39 -3
  165. data/lib/mongo/server/monitor/connection.rb +49 -28
  166. data/lib/mongo/server/monitor.rb +3 -14
  167. data/lib/mongo/server.rb +31 -4
  168. data/lib/mongo/server_selector/selectable.rb +58 -32
  169. data/lib/mongo/server_selector.rb +19 -10
  170. data/lib/mongo/socket/ssl.rb +4 -1
  171. data/lib/mongo/socket/tcp.rb +2 -2
  172. data/lib/mongo/socket/unix.rb +5 -8
  173. data/lib/mongo/socket.rb +11 -4
  174. data/lib/mongo/uri.rb +245 -139
  175. data/lib/mongo/version.rb +1 -1
  176. data/lib/mongo/write_concern.rb +21 -6
  177. data/lib/mongo.rb +4 -4
  178. data/mongo.gemspec +1 -2
  179. data/spec/mongo/address/unix_spec.rb +1 -1
  180. data/spec/mongo/address_spec.rb +25 -0
  181. data/spec/mongo/auth/ldap/conversation_spec.rb +43 -0
  182. data/spec/mongo/auth/user/view_spec.rb +26 -1
  183. data/spec/mongo/bulk_write/ordered_combiner_spec.rb +284 -0
  184. data/spec/mongo/bulk_write/unordered_combiner_spec.rb +239 -0
  185. data/spec/mongo/bulk_write_spec.rb +385 -161
  186. data/spec/mongo/client_spec.rb +193 -23
  187. data/spec/mongo/cluster/topology/replica_set_spec.rb +2 -0
  188. data/spec/mongo/collection/view/aggregation_spec.rb +65 -0
  189. data/spec/mongo/collection/view/builder/find_command_spec.rb +167 -0
  190. data/spec/mongo/collection/view/builder/flags_spec.rb +106 -0
  191. data/spec/mongo/collection/view/builder/modifiers_spec.rb +210 -0
  192. data/spec/mongo/collection/view/builder/op_query_spec.rb +154 -0
  193. data/spec/mongo/collection/view/explainable_spec.rb +1 -2
  194. data/spec/mongo/collection/view/immutable_spec.rb +54 -0
  195. data/spec/mongo/collection/view/map_reduce_spec.rb +104 -9
  196. data/spec/mongo/collection/view/readable_spec.rb +109 -112
  197. data/spec/mongo/collection/view_spec.rb +119 -487
  198. data/spec/mongo/collection_spec.rb +1002 -33
  199. data/spec/mongo/command_monitoring_spec.rb +64 -0
  200. data/spec/mongo/connection_string_spec.rb +115 -0
  201. data/spec/mongo/cursor/builder/get_more_command_spec.rb +160 -0
  202. data/spec/mongo/cursor/builder/op_get_more_spec.rb +52 -0
  203. data/spec/mongo/cursor_spec.rb +10 -60
  204. data/spec/mongo/database_spec.rb +81 -12
  205. data/spec/mongo/dbref_spec.rb +4 -4
  206. data/spec/mongo/grid/file/chunk_spec.rb +6 -6
  207. data/spec/mongo/grid/file/{metadata_spec.rb → info_spec.rb} +29 -17
  208. data/spec/mongo/grid/file_spec.rb +8 -8
  209. data/spec/mongo/grid/fs_bucket_spec.rb +1020 -0
  210. data/spec/mongo/grid/stream/read_spec.rb +275 -0
  211. data/spec/mongo/grid/stream/write_spec.rb +440 -0
  212. data/spec/mongo/grid/stream_spec.rb +48 -0
  213. data/spec/mongo/gridfs_spec.rb +50 -0
  214. data/spec/mongo/index/view_spec.rb +41 -0
  215. data/spec/mongo/logger_spec.rb +0 -40
  216. data/spec/mongo/monitoring/command_log_subscriber_spec.rb +76 -0
  217. data/spec/mongo/monitoring/event/command_started_spec.rb +26 -0
  218. data/spec/mongo/monitoring/event/command_succeeded_spec.rb +26 -0
  219. data/spec/mongo/monitoring/event/secure_spec.rb +57 -0
  220. data/spec/mongo/operation/{aggregate → commands/aggregate}/result_spec.rb +1 -1
  221. data/spec/mongo/operation/commands/aggregate_spec.rb +69 -0
  222. data/spec/mongo/operation/{read → commands}/collections_info_spec.rb +1 -1
  223. data/spec/mongo/operation/{command_spec.rb → commands/command_spec.rb} +1 -19
  224. data/spec/mongo/operation/{read → commands}/indexes_spec.rb +1 -1
  225. data/spec/mongo/operation/{map_reduce_spec.rb → commands/map_reduce_spec.rb} +1 -19
  226. data/spec/mongo/operation/kill_cursors_spec.rb +1 -17
  227. data/spec/mongo/operation/read/get_more_spec.rb +0 -16
  228. data/spec/mongo/operation/read/query_spec.rb +19 -16
  229. data/spec/mongo/operation/{read_preferrable_spec.rb → read_preference_spec.rb} +11 -11
  230. data/spec/mongo/operation/result_spec.rb +19 -0
  231. data/spec/mongo/operation/write/bulk/{bulk_delete_spec.rb → delete_spec.rb} +17 -28
  232. data/spec/mongo/operation/write/bulk/{bulk_insert_spec.rb → insert_spec.rb} +1 -12
  233. data/spec/mongo/operation/write/bulk/{bulk_update_spec.rb → update_spec.rb} +7 -18
  234. data/spec/mongo/operation/write/command/delete_spec.rb +18 -9
  235. data/spec/mongo/operation/write/command/insert_spec.rb +18 -9
  236. data/spec/mongo/operation/write/command/update_spec.rb +18 -9
  237. data/spec/mongo/operation/write/delete_spec.rb +3 -3
  238. data/spec/mongo/operation/write/insert_spec.rb +0 -11
  239. data/spec/mongo/operation/write/update_spec.rb +6 -6
  240. data/spec/mongo/options/redacted_spec.rb +350 -0
  241. data/spec/mongo/protocol/delete_spec.rb +4 -4
  242. data/spec/mongo/protocol/get_more_spec.rb +4 -4
  243. data/spec/mongo/protocol/insert_spec.rb +3 -3
  244. data/spec/mongo/protocol/kill_cursors_spec.rb +8 -6
  245. data/spec/mongo/protocol/query_spec.rb +21 -7
  246. data/spec/mongo/protocol/update_spec.rb +5 -5
  247. data/spec/mongo/retryable_spec.rb +221 -0
  248. data/spec/mongo/server/connection_pool/queue_spec.rb +16 -0
  249. data/spec/mongo/server/connection_pool_spec.rb +42 -6
  250. data/spec/mongo/server/connection_spec.rb +86 -1
  251. data/spec/mongo/server/description/features_spec.rb +25 -0
  252. data/spec/mongo/server/description_spec.rb +42 -0
  253. data/spec/mongo/server/monitor_spec.rb +44 -0
  254. data/spec/mongo/server_discovery_and_monitoring_spec.rb +25 -59
  255. data/spec/mongo/server_selection_rtt_spec.rb +37 -57
  256. data/spec/mongo/server_selection_spec.rb +5 -3
  257. data/spec/mongo/server_selector/nearest_spec.rb +35 -27
  258. data/spec/mongo/server_selector/primary_preferred_spec.rb +32 -30
  259. data/spec/mongo/server_selector/primary_spec.rb +21 -14
  260. data/spec/mongo/server_selector/secondary_preferred_spec.rb +28 -26
  261. data/spec/mongo/server_selector/secondary_spec.rb +24 -22
  262. data/spec/mongo/server_selector_spec.rb +87 -24
  263. data/spec/mongo/server_spec.rb +78 -15
  264. data/spec/mongo/socket/ssl_spec.rb +101 -57
  265. data/spec/mongo/socket/unix_spec.rb +52 -0
  266. data/spec/mongo/uri_spec.rb +271 -59
  267. data/spec/mongo/write_concern_spec.rb +126 -0
  268. data/spec/spec_helper.rb +29 -23
  269. data/spec/support/authorization.rb +4 -5
  270. data/spec/support/command_monitoring/bulkWrite.yml +73 -0
  271. data/spec/support/command_monitoring/command.yml +42 -0
  272. data/spec/support/command_monitoring/deleteMany.yml +55 -0
  273. data/spec/support/command_monitoring/deleteOne.yml +55 -0
  274. data/spec/support/command_monitoring/find.yml +268 -0
  275. data/spec/support/command_monitoring/insertMany.yml +81 -0
  276. data/spec/support/command_monitoring/insertOne.yml +51 -0
  277. data/spec/support/command_monitoring/updateMany.yml +67 -0
  278. data/spec/support/command_monitoring/updateOne.yml +95 -0
  279. data/spec/support/command_monitoring.rb +373 -0
  280. data/spec/support/connection_string.rb +228 -0
  281. data/spec/support/connection_string_tests/invalid-uris.yml +193 -0
  282. data/spec/support/connection_string_tests/valid-auth.yml +256 -0
  283. data/spec/support/connection_string_tests/valid-host_identifiers.yml +121 -0
  284. data/spec/support/connection_string_tests/valid-options.yml +30 -0
  285. data/spec/support/connection_string_tests/valid-unix_socket-absolute.yml +197 -0
  286. data/spec/support/connection_string_tests/valid-unix_socket-relative.yml +213 -0
  287. data/spec/support/connection_string_tests/valid-warnings.yml +55 -0
  288. data/spec/support/crud/read.rb +14 -10
  289. data/spec/support/crud/write.rb +36 -9
  290. data/spec/support/crud.rb +10 -2
  291. data/spec/support/gridfs.rb +637 -0
  292. data/spec/support/gridfs_tests/delete.yml +157 -0
  293. data/spec/support/gridfs_tests/download.yml +210 -0
  294. data/spec/support/gridfs_tests/download_by_name.yml +113 -0
  295. data/spec/support/gridfs_tests/upload.yml +158 -0
  296. data/spec/support/matchers.rb +2 -2
  297. data/spec/support/sdam/rs/equal_electionids.yml +1 -2
  298. data/spec/support/sdam/rs/new_primary_new_electionid.yml +0 -3
  299. data/spec/support/sdam/rs/primary_mismatched_me.yml +37 -0
  300. data/spec/support/sdam/rs/primary_to_no_primary_mismatched_me.yml +75 -0
  301. data/spec/support/sdam/rs/rsother_discovered.yml +24 -3
  302. data/spec/support/sdam/rs/secondary_mismatched_me.yml +37 -0
  303. data/spec/support/sdam/rs/stepdown_change_set_name.yml +59 -0
  304. data/spec/support/sdam/single/direct_connection_rsarbiter.yml +1 -1
  305. data/spec/support/sdam/single/direct_connection_rsprimary.yml +1 -1
  306. data/spec/support/sdam/single/direct_connection_rssecondary.yml +1 -1
  307. data/spec/support/sdam/single/direct_connection_slave.yml +1 -1
  308. data/spec/support/sdam/single/direct_connection_standalone.yml +1 -1
  309. data/spec/support/sdam/single/not_ok_response.yml +0 -1
  310. data/spec/support/server_discovery_and_monitoring.rb +3 -1
  311. data/spec/support/server_selection.rb +3 -1
  312. data/spec/support/shared/bulk_write.rb +192 -0
  313. data/spec/support/shared/protocol.rb +5 -5
  314. data/spec/support/shared/server_selector.rb +78 -13
  315. data/spec/support/travis.rb +1 -1
  316. data.tar.gz.sig +0 -0
  317. metadata +211 -72
  318. metadata.gz.sig +0 -0
  319. data/lib/mongo/bulk_write/bulk_writable.rb +0 -252
  320. data/lib/mongo/bulk_write/deletable.rb +0 -57
  321. data/lib/mongo/bulk_write/insertable.rb +0 -49
  322. data/lib/mongo/bulk_write/replacable.rb +0 -58
  323. data/lib/mongo/bulk_write/updatable.rb +0 -69
  324. data/lib/mongo/grid/fs.rb +0 -146
  325. data/lib/mongo/operation/aggregate/result.rb +0 -103
  326. data/lib/mongo/operation/aggregate.rb +0 -108
  327. data/lib/mongo/operation/command.rb +0 -61
  328. data/lib/mongo/operation/map_reduce/result.rb +0 -122
  329. data/lib/mongo/operation/map_reduce.rb +0 -95
  330. data/lib/mongo/operation/parallel_scan/result.rb +0 -72
  331. data/lib/mongo/operation/parallel_scan.rb +0 -76
  332. data/lib/mongo/operation/write/bulk/bulk_delete/result.rb +0 -75
  333. data/lib/mongo/operation/write/bulk/bulk_delete.rb +0 -145
  334. data/lib/mongo/operation/write/bulk/bulk_insert/result.rb +0 -130
  335. data/lib/mongo/operation/write/bulk/bulk_insert.rb +0 -132
  336. data/lib/mongo/operation/write/bulk/bulk_mergable.rb +0 -67
  337. data/lib/mongo/operation/write/bulk/bulk_update/result.rb +0 -174
  338. data/lib/mongo/operation/write/bulk/bulk_update.rb +0 -154
  339. data/lib/mongo/operation/write/bulk/legacy_bulk_mergable.rb +0 -83
  340. data/spec/mongo/grid/fs_spec.rb +0 -160
  341. data/spec/mongo/loggable_spec.rb +0 -63
  342. data/spec/mongo/operation/aggregate_spec.rb +0 -127
@@ -0,0 +1,448 @@
1
+ # Copyright (C) 2014-2015 MongoDB, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module Mongo
16
+ module Grid
17
+
18
+ # Represents a view of the GridFS in the database.
19
+ #
20
+ # @since 2.0.0
21
+ class FSBucket
22
+ extend Forwardable
23
+
24
+ # The default root prefix.
25
+ #
26
+ # @since 2.0.0
27
+ DEFAULT_ROOT = 'fs'.freeze
28
+
29
+ # The specification for the chunks collection index.
30
+ #
31
+ # @since 2.0.0
32
+ CHUNKS_INDEX = { :files_id => 1, :n => 1 }.freeze
33
+
34
+ # The specification for the files collection index.
35
+ #
36
+ # @since 2.1.0
37
+ FILES_INDEX = { filename: 1, uploadDate: 1 }.freeze
38
+
39
+ # @return [ Collection ] chunks_collection The chunks collection.
40
+ #
41
+ # @since 2.0.0
42
+ attr_reader :chunks_collection
43
+
44
+ # @return [ Database ] database The database.
45
+ #
46
+ # @since 2.0.0
47
+ attr_reader :database
48
+
49
+ # @return [ Collection ] files_collection The files collection.
50
+ #
51
+ # @since 2.0.0
52
+ attr_reader :files_collection
53
+
54
+ # @return [ Hash ] options The FSBucket options.
55
+ #
56
+ # @since 2.1.0
57
+ attr_reader :options
58
+
59
+ # Get client from the database.
60
+ #
61
+ # @since 2.1.0
62
+ def_delegators :database,
63
+ :client
64
+
65
+ # Find files collection documents matching a given selector.
66
+ #
67
+ # @example Find files collection documents by a filename.
68
+ # fs.find(filename: 'file.txt')
69
+ #
70
+ # @param [ Hash ] selector The selector to use in the find.
71
+ # @param [ Hash ] options The options for the find.
72
+ #
73
+ # @option options [ Integer ] :batch_size The number of documents returned in each batch
74
+ # of results from MongoDB.
75
+ # @option options [ Integer ] :limit The max number of docs to return from the query.
76
+ # @option options [ true, false ] :no_cursor_timeout The server normally times out idle
77
+ # cursors after an inactivity period (10 minutes) to prevent excess memory use.
78
+ # Set this option to prevent that.
79
+ # @option options [ Integer ] :skip The number of docs to skip before returning results.
80
+ # @option options [ Hash ] :sort The key and direction pairs by which the result set
81
+ # will be sorted.
82
+ #
83
+ # @return [ CollectionView ] The collection view.
84
+ #
85
+ # @since 2.1.0
86
+ def find(selector = nil, options = {})
87
+ files_collection.find(selector, options.merge(read: read_preference))
88
+ end
89
+
90
+ # Find a file in the GridFS.
91
+ #
92
+ # @example Find a file by its id.
93
+ # fs.find_one(_id: id)
94
+ #
95
+ # @example Find a file by its filename.
96
+ # fs.find_one(filename: 'test.txt')
97
+ #
98
+ # @param [ Hash ] selector The selector.
99
+ #
100
+ # @return [ Grid::File ] The file.
101
+ #
102
+ # @since 2.0.0
103
+ def find_one(selector = nil)
104
+ file_info = files_collection.find(selector).first
105
+ return nil unless file_info
106
+ chunks = chunks_collection.find(:files_id => file_info[:_id]).sort(:n => 1)
107
+ Grid::File.new(chunks.to_a, file_info)
108
+ end
109
+
110
+ # Insert a single file into the GridFS.
111
+ #
112
+ # @example Insert a single file.
113
+ # fs.insert_one(file)
114
+ #
115
+ # @param [ Grid::File ] file The file to insert.
116
+ #
117
+ # @return [ BSON::ObjectId ] The file id.
118
+ #
119
+ # @since 2.0.0
120
+ def insert_one(file)
121
+ @indexes ||= ensure_indexes!
122
+ chunks_collection.insert_many(file.chunks)
123
+ files_collection.insert_one(file.info)
124
+ file.id
125
+ end
126
+
127
+ # Create the GridFS.
128
+ #
129
+ # @example Create the GridFS.
130
+ # Grid::FSBucket.new(database)
131
+ #
132
+ # @param [ Database ] database The database the files reside in.
133
+ # @param [ Hash ] options The GridFS options.
134
+ #
135
+ # @option options [ String ] :fs_name The prefix for the files and chunks
136
+ # collections.
137
+ # @option options [ String ] :bucket_name The prefix for the files and chunks
138
+ # collections.
139
+ # @option options [ Integer ] :chunk_size Override the default chunk
140
+ # size.
141
+ # @option options [ String ] :write The write concern.
142
+ # @option options [ String ] :read The read preference.
143
+ #
144
+ # @since 2.0.0
145
+ def initialize(database, options = {})
146
+ @database = database
147
+ @options = options
148
+ @chunks_collection = database[chunks_name]
149
+ @files_collection = database[files_name]
150
+ end
151
+
152
+ # Get the prefix for the GridFS
153
+ #
154
+ # @example Get the prefix.
155
+ # fs.prefix
156
+ #
157
+ # @return [ String ] The GridFS prefix.
158
+ #
159
+ # @since 2.0.0
160
+ def prefix
161
+ @options[:fs_name] || @options[:bucket_name]|| DEFAULT_ROOT
162
+ end
163
+
164
+ # Remove a single file from the GridFS.
165
+ #
166
+ # @example Remove a file from the GridFS.
167
+ # fs.delete_one(file)
168
+ #
169
+ # @param [ Grid::File ] file The file to remove.
170
+ #
171
+ # @return [ Result ] The result of the remove.
172
+ #
173
+ # @since 2.0.0
174
+ def delete_one(file)
175
+ delete(file.id)
176
+ end
177
+
178
+ # Remove a single file, identified by its id from the GridFS.
179
+ #
180
+ # @example Remove a file from the GridFS.
181
+ # fs.delete(id)
182
+ #
183
+ # @param [ BSON::ObjectId, Object ] id The id of the file to remove.
184
+ #
185
+ # @return [ Result ] The result of the remove.
186
+ #
187
+ # @raise [ Error::FileNotFound ] If the file is not found.
188
+ #
189
+ # @since 2.1.0
190
+ def delete(id)
191
+ result = files_collection.find(:_id => id).delete_one
192
+ chunks_collection.find(:files_id => id).delete_many
193
+ raise Error::FileNotFound.new(id, :id) if result.n == 0
194
+ result
195
+ end
196
+
197
+ # Opens a stream from which a file can be downloaded, specified by id.
198
+ #
199
+ # @example Open a stream from which a file can be downloaded.
200
+ # fs.open_download_stream(id)
201
+ #
202
+ # @param [ BSON::ObjectId, Object ] id The id of the file to read.
203
+ #
204
+ # @return [ Stream::Read ] The stream to read from.
205
+ #
206
+ # @yieldparam [ Hash ] The read stream.
207
+ #
208
+ # @since 2.1.0
209
+ def open_download_stream(id)
210
+ read_stream(id).tap do |stream|
211
+ if block_given?
212
+ yield stream
213
+ stream.close
214
+ end
215
+ end
216
+ end
217
+
218
+ # Downloads the contents of the file specified by id and writes them to
219
+ # the destination io object.
220
+ #
221
+ # @example Download the file and write it to the io object.
222
+ # fs.download_to_stream(id, io)
223
+ #
224
+ # @param [ BSON::ObjectId, Object ] id The id of the file to read.
225
+ # @param [ IO ] io The io object to write to.
226
+ #
227
+ # @since 2.1.0
228
+ def download_to_stream(id, io)
229
+ open_download_stream(id) do |stream|
230
+ stream.each do |chunk|
231
+ io << chunk
232
+ end
233
+ end
234
+ end
235
+
236
+ # Opens a stream from which the application can read the contents of the stored file
237
+ # specified by filename and the revision in options.
238
+ #
239
+ # Revision numbers are defined as follows:
240
+ # 0 = the original stored file
241
+ # 1 = the first revision
242
+ # 2 = the second revision
243
+ # etc…
244
+ # -2 = the second most recent revision
245
+ # -1 = the most recent revision
246
+ #
247
+ # @example Open a stream to download the most recent revision.
248
+ # fs.open_download_stream_by_name('some-file.txt')
249
+ #
250
+ # # @example Open a stream to download the original file.
251
+ # fs.open_download_stream_by_name('some-file.txt', revision: 0)
252
+ #
253
+ # @example Open a stream to download the second revision of the stored file.
254
+ # fs.open_download_stream_by_name('some-file.txt', revision: 2)
255
+ #
256
+ # @param [ String ] filename The file's name.
257
+ # @param [ Hash ] opts Options for the download.
258
+ #
259
+ # @option opts [ Integer ] :revision The revision number of the file to download.
260
+ # Defaults to -1, the most recent version.
261
+ #
262
+ # @return [ Stream::Read ] The stream to read from.
263
+ #
264
+ # @raise [ Error::FileNotFound ] If the file is not found.
265
+ # @raise [ Error::InvalidFileRevision ] If the requested revision is not found for the file.
266
+ #
267
+ # @yieldparam [ Hash ] The read stream.
268
+ #
269
+ # @since 2.1.0
270
+ def open_download_stream_by_name(filename, opts = {}, &block)
271
+ revision = opts.fetch(:revision, -1)
272
+ if revision < 0
273
+ skip = revision.abs - 1
274
+ sort = { 'uploadDate' => Mongo::Index::DESCENDING }
275
+ else
276
+ skip = revision
277
+ sort = { 'uploadDate' => Mongo::Index::ASCENDING }
278
+ end
279
+ file_doc = files_collection.find({ filename: filename} ,
280
+ projection: { _id: 1 },
281
+ sort: sort,
282
+ skip: skip,
283
+ limit: -1).first
284
+ unless file_doc
285
+ raise Error::FileNotFound.new(filename, :filename) unless opts[:revision]
286
+ raise Error::InvalidFileRevision.new(filename, opts[:revision])
287
+ end
288
+ open_download_stream(file_doc[:_id], &block)
289
+ end
290
+
291
+ # Downloads the contents of the stored file specified by filename and by the
292
+ # revision in options and writes the contents to the destination io object.
293
+ #
294
+ # Revision numbers are defined as follows:
295
+ # 0 = the original stored file
296
+ # 1 = the first revision
297
+ # 2 = the second revision
298
+ # etc…
299
+ # -2 = the second most recent revision
300
+ # -1 = the most recent revision
301
+ #
302
+ # @example Download the most recent revision.
303
+ # fs.download_to_stream_by_name('some-file.txt', io)
304
+ #
305
+ # # @example Download the original file.
306
+ # fs.download_to_stream_by_name('some-file.txt', io, revision: 0)
307
+ #
308
+ # @example Download the second revision of the stored file.
309
+ # fs.download_to_stream_by_name('some-file.txt', io, revision: 2)
310
+ #
311
+ # @param [ String ] filename The file's name.
312
+ # @param [ IO ] io The io object to write to.
313
+ # @param [ Hash ] opts Options for the download.
314
+ #
315
+ # @option opts [ Integer ] :revision The revision number of the file to download.
316
+ # Defaults to -1, the most recent version.
317
+ #
318
+ # @raise [ Error::FileNotFound ] If the file is not found.
319
+ # @raise [ Error::InvalidFileRevision ] If the requested revision is not found for the file.
320
+ #
321
+ # @since 2.1.0
322
+ def download_to_stream_by_name(filename, io, opts = {})
323
+ download_to_stream(open_download_stream_by_name(filename, opts).file_id, io)
324
+ end
325
+
326
+ # Opens an upload stream to GridFS to which the contents of a user file came be written.
327
+ #
328
+ # @example Open a stream to which the contents of a file came be written.
329
+ # fs.open_upload_stream('a-file.txt')
330
+ #
331
+ # @param [ String ] filename The filename of the file to upload.
332
+ # @param [ Hash ] opts The options for the write stream.
333
+ #
334
+ # @option opts [ Integer ] :chunk_size Override the default chunk size.
335
+ # @option opts [ Hash ] :write The write concern.
336
+ # @option opts [ Hash ] :metadata User data for the 'metadata' field of the files
337
+ # collection document.
338
+ # @option opts [ String ] :content_type The content type of the file.
339
+ # Deprecated, please use the metadata document instead.
340
+ # @option opts [ Array<String> ] :aliases A list of aliases.
341
+ # Deprecated, please use the metadata document instead.
342
+ #
343
+ # @return [ Stream::Write ] The write stream.
344
+ #
345
+ # @yieldparam [ Hash ] The write stream.
346
+ #
347
+ # @since 2.1.0
348
+ def open_upload_stream(filename, opts = {})
349
+ write_stream(filename, opts).tap do |stream|
350
+ if block_given?
351
+ yield stream
352
+ stream.close
353
+ end
354
+ end
355
+ end
356
+
357
+ # Uploads a user file to a GridFS bucket.
358
+ # Reads the contents of the user file from the source stream and uploads it as chunks in the
359
+ # chunks collection. After all the chunks have been uploaded, it creates a files collection
360
+ # document for the filename in the files collection.
361
+ #
362
+ # @example Open a stream to which the contents of a file came be written.
363
+ # fs.open_upload_stream('a-file.txt')
364
+ #
365
+ # @param [ String ] filename The filename of the file to upload.
366
+ # @param [ IO ] io The source io stream to upload from.
367
+ # @param [ Hash ] opts The options for the write stream.
368
+ #
369
+ # @option opts [ Integer ] :chunk_size Override the default chunk size.
370
+ # @option opts [ Hash ] :write The write concern.
371
+ # @option opts [ Hash ] :metadata User data for the 'metadata' field of the files
372
+ # collection document.
373
+ # @option opts [ String ] :content_type The content type of the file. Deprecated, please
374
+ # use the metadata document instead.
375
+ # @option opts [ Array<String> ] :aliases A list of aliases. Deprecated, please use the
376
+ # metadata document instead.
377
+ #
378
+ # @return [ BSON::ObjectId ] The ObjectId file id.
379
+ #
380
+ # @since 2.1.0
381
+ def upload_from_stream(filename, io, opts = {})
382
+ open_upload_stream(filename, opts) do |stream|
383
+ begin
384
+ stream.write(io)
385
+ rescue IOError
386
+ begin
387
+ stream.abort
388
+ rescue Error::OperationFailure
389
+ end
390
+ raise
391
+ end
392
+ end.file_id
393
+ end
394
+
395
+ # Get the read preference.
396
+ #
397
+ # @example Get the read preference.
398
+ # fs.read_preference
399
+ #
400
+ # @return [ Mongo::ServerSelector ] The read preference.
401
+ #
402
+ # @since 2.1.0
403
+ def read_preference
404
+ @read_preference ||= @options[:read] ?
405
+ ServerSelector.get(Options::Redacted.new((@options[:read] || {}).merge(client.options))) :
406
+ database.read_preference
407
+ end
408
+
409
+ # Get the write concern.
410
+ #
411
+ # @example Get the write concern.
412
+ # stream.write_concern
413
+ #
414
+ # @return [ Mongo::WriteConcern ] The write concern.
415
+ #
416
+ # @since 2.1.0
417
+ def write_concern
418
+ @write_concern ||= @options[:write] ? WriteConcern.get(@options[:write]) :
419
+ database.write_concern
420
+ end
421
+
422
+ private
423
+
424
+ def read_stream(id)
425
+ Stream.get(self, Stream::READ_MODE, { file_id: id }.merge!(options))
426
+ end
427
+
428
+ def write_stream(filename, opts)
429
+ Stream.get(self, Stream::WRITE_MODE, { filename: filename }.merge!(options).merge!(opts))
430
+ end
431
+
432
+ def chunks_name
433
+ "#{prefix}.#{Grid::File::Chunk::COLLECTION}"
434
+ end
435
+
436
+ def files_name
437
+ "#{prefix}.#{Grid::File::Info::COLLECTION}"
438
+ end
439
+
440
+ def ensure_indexes!
441
+ if files_collection.find({}, limit: 1, projection: { _id: 1 }).first.nil?
442
+ chunks_collection.indexes.create_one(FSBucket::CHUNKS_INDEX, :unique => true)
443
+ files_collection.indexes.create_one(FSBucket::FILES_INDEX)
444
+ end
445
+ end
446
+ end
447
+ end
448
+ end
@@ -0,0 +1,208 @@
1
+ # Copyright (C) 2014-2015 MongoDB, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module Mongo
16
+ module Grid
17
+ class FSBucket
18
+
19
+ module Stream
20
+ # A stream that reads files from the FSBucket.
21
+ #
22
+ # @since 2.1.0
23
+ class Read
24
+ include Enumerable
25
+
26
+ # @return [ FSBucket ] fs The fs bucket from which this stream reads.
27
+ #
28
+ # @since 2.1.0
29
+ attr_reader :fs
30
+
31
+ # @return [ Hash ] options The stream options.
32
+ #
33
+ # @since 2.1.0
34
+ attr_reader :options
35
+
36
+ # @return [ BSON::ObjectId, Object ] file_id The id of the file being read.
37
+ #
38
+ # @since 2.1.0
39
+ attr_reader :file_id
40
+
41
+ # Create a stream for reading files from the FSBucket.
42
+ #
43
+ # @example Create the stream.
44
+ # Stream::Read.new(fs, options)
45
+ #
46
+ # @param [ FSBucket ] fs The GridFS bucket object.
47
+ # @param [ Hash ] options The read stream options.
48
+ #
49
+ # @since 2.1.0
50
+ def initialize(fs, options)
51
+ @fs = fs
52
+ @options = options.dup
53
+ @file_id = @options.delete(:file_id)
54
+ @open = true
55
+ end
56
+
57
+ # Iterate through chunk data streamed from the FSBucket.
58
+ #
59
+ # @example Iterate through the chunk data.
60
+ # stream.each do |data|
61
+ # buffer << data
62
+ # end
63
+ #
64
+ # @return [ Enumerator ] The enumerator.
65
+ #
66
+ # @raise [ Error::MissingFileChunk ] If a chunk is found out of sequence.
67
+ #
68
+ # @yieldparam [ Hash ] Each chunk of file data.
69
+ #
70
+ # @since 2.1.0
71
+ def each
72
+ ensure_readable!
73
+ num_chunks = (file_info.length + file_info.chunk_size - 1) / file_info.chunk_size
74
+ view.each_with_index.reduce(0) do |length_read, (doc, index)|
75
+ chunk = Grid::File::Chunk.new(doc)
76
+ validate!(index, num_chunks, chunk, length_read)
77
+ data = Grid::File::Chunk.assemble([ chunk ])
78
+ yield data
79
+ length_read += data.size
80
+ end if block_given?
81
+ view.to_enum
82
+ end
83
+
84
+ # Read all file data.
85
+ #
86
+ # @example Read the file data.
87
+ # stream.read
88
+ #
89
+ # @return [ String ] The file data.
90
+ #
91
+ # @raise [ Error::MissingFileChunk ] If a chunk is found out of sequence.
92
+ #
93
+ # @since 2.1.0
94
+ def read
95
+ to_a.join
96
+ end
97
+
98
+ # Close the read stream.
99
+ #
100
+ # @example Close the stream.
101
+ # stream.close
102
+ #
103
+ # @return [ BSON::ObjectId, Object ] The file id.
104
+ #
105
+ # @raise [ Error::ClosedStream ] If the stream is already closed.
106
+ #
107
+ # @since 2.1.0
108
+ def close
109
+ ensure_open!
110
+ view.close_query
111
+ @open = false
112
+ file_id
113
+ end
114
+
115
+ # Is the stream closed.
116
+ #
117
+ # @example Is the stream closd.
118
+ # stream.closed?
119
+ #
120
+ # @return [ true, false ] Whether the stream is closed.
121
+ #
122
+ # @since 2.1.0
123
+ def closed?
124
+ !@open
125
+ end
126
+
127
+ # Get the read preference used when streaming.
128
+ #
129
+ # @example Get the read preference.
130
+ # stream.read_preference
131
+ #
132
+ # @return [ Mongo::ServerSelector ] The read preference.
133
+ #
134
+ # @since 2.1.0
135
+ def read_preference
136
+ @read_preference ||= @options[:read] ?
137
+ ServerSelector.get(Options::Redacted.new((@options[:read] || {}).merge(fs.options))) :
138
+ fs.read_preference
139
+ end
140
+
141
+ # Get the files collection file information document for the file being read.
142
+ #
143
+ # @example Get the file info document.
144
+ # stream.file_info
145
+ #
146
+ # @return [ Hash ] The file info document.
147
+ #
148
+ # @since 2.1.0
149
+ def file_info
150
+ doc = fs.files_collection.find(_id: file_id).first
151
+ if doc
152
+ @file_info ||= File::Info.new(Options::Mapper.transform(doc, File::Info::MAPPINGS.invert))
153
+ end
154
+ end
155
+
156
+ private
157
+
158
+ def ensure_open!
159
+ raise Error::ClosedStream.new if closed?
160
+ end
161
+
162
+ def ensure_file_info!
163
+ raise Error::FileNotFound.new(file_id, :id) unless file_info
164
+ end
165
+
166
+ def ensure_readable!
167
+ ensure_open!
168
+ ensure_file_info!
169
+ end
170
+
171
+ def view
172
+ @view ||= fs.chunks_collection.find({ :files_id => file_id }, options).read(read_preference).sort(:n => 1)
173
+ end
174
+
175
+ def validate!(index, num_chunks, chunk, length_read)
176
+ validate_n!(index, chunk)
177
+ validate_length!(index, num_chunks, chunk, length_read)
178
+ end
179
+
180
+ def raise_unexpected_chunk_length!(chunk)
181
+ close
182
+ raise Error::UnexpectedChunkLength.new(file_info.chunk_size, chunk)
183
+ end
184
+
185
+ def validate_length!(index, num_chunks, chunk, length_read)
186
+ if num_chunks > 0 && chunk.data.data.size > 0
187
+ raise Error::ExtraFileChunk.new unless index < num_chunks
188
+ if index == num_chunks - 1
189
+ unless chunk.data.data.size + length_read == file_info.length
190
+ raise_unexpected_chunk_length!(chunk)
191
+ end
192
+ elsif chunk.data.data.size != file_info.chunk_size
193
+ raise_unexpected_chunk_length!(chunk)
194
+ end
195
+ end
196
+ end
197
+
198
+ def validate_n!(index, chunk)
199
+ unless index == chunk.n
200
+ close
201
+ raise Error::MissingFileChunk.new(index, chunk)
202
+ end
203
+ end
204
+ end
205
+ end
206
+ end
207
+ end
208
+ end