mongo 2.1.0.beta → 2.1.0.rc0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (253) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/Rakefile +2 -2
  5. data/lib/mongo.rb +2 -3
  6. data/lib/mongo/address.rb +7 -5
  7. data/lib/mongo/address/unix.rb +2 -2
  8. data/lib/mongo/auth/ldap/conversation.rb +6 -2
  9. data/lib/mongo/auth/scram/conversation.rb +8 -2
  10. data/lib/mongo/auth/user/view.rb +21 -0
  11. data/lib/mongo/bulk_write.rb +155 -23
  12. data/lib/mongo/bulk_write/combineable.rb +51 -0
  13. data/lib/mongo/bulk_write/ordered_combiner.rb +55 -0
  14. data/lib/mongo/bulk_write/result.rb +61 -8
  15. data/lib/mongo/bulk_write/result_combiner.rb +117 -0
  16. data/lib/mongo/bulk_write/transformable.rb +117 -0
  17. data/lib/mongo/bulk_write/unordered_combiner.rb +52 -0
  18. data/lib/mongo/bulk_write/validatable.rb +62 -0
  19. data/lib/mongo/client.rb +7 -3
  20. data/lib/mongo/cluster.rb +3 -3
  21. data/lib/mongo/cluster/topology/replica_set.rb +8 -6
  22. data/lib/mongo/cluster/topology/unknown.rb +5 -2
  23. data/lib/mongo/collection.rb +75 -4
  24. data/lib/mongo/collection/view.rb +1 -2
  25. data/lib/mongo/collection/view/aggregation.rb +13 -8
  26. data/lib/mongo/collection/view/immutable.rb +6 -6
  27. data/lib/mongo/collection/view/iterable.rb +13 -4
  28. data/lib/mongo/collection/view/map_reduce.rb +22 -17
  29. data/lib/mongo/collection/view/readable.rb +121 -70
  30. data/lib/mongo/cursor.rb +5 -1
  31. data/lib/mongo/database.rb +3 -3
  32. data/lib/mongo/database/view.rb +1 -1
  33. data/lib/mongo/error.rb +7 -0
  34. data/lib/mongo/{bulk_write/unordered_bulk_write.rb → error/closed_stream.rb} +12 -21
  35. data/lib/mongo/{bulk_write/ordered_bulk_write.rb → error/extra_file_chunk.rb} +13 -27
  36. data/lib/mongo/error/file_not_found.rb +37 -0
  37. data/lib/mongo/error/invalid_file.rb +2 -2
  38. data/lib/mongo/error/invalid_file_revision.rb +37 -0
  39. data/lib/mongo/error/invalid_uri.rb +5 -4
  40. data/lib/mongo/error/missing_file_chunk.rb +38 -0
  41. data/lib/mongo/error/operation_failure.rb +1 -1
  42. data/lib/mongo/error/unchangeable_collection_option.rb +38 -0
  43. data/lib/mongo/error/unexpected_chunk_length.rb +39 -0
  44. data/lib/mongo/grid.rb +2 -1
  45. data/lib/mongo/grid/file.rb +12 -9
  46. data/lib/mongo/grid/file/chunk.rb +6 -6
  47. data/lib/mongo/grid/file/{metadata.rb → info.rb} +41 -39
  48. data/lib/mongo/grid/fs_bucket.rb +441 -0
  49. data/lib/mongo/grid/stream.rb +64 -0
  50. data/lib/mongo/grid/stream/read.rb +208 -0
  51. data/lib/mongo/grid/stream/write.rb +187 -0
  52. data/lib/mongo/index/view.rb +1 -1
  53. data/lib/mongo/loggable.rb +34 -57
  54. data/lib/mongo/logger.rb +16 -78
  55. data/lib/mongo/monitoring.rb +1 -5
  56. data/lib/mongo/monitoring/command_log_subscriber.rb +35 -17
  57. data/lib/mongo/monitoring/event/command_succeeded.rb +20 -1
  58. data/lib/mongo/monitoring/publishable.rb +22 -12
  59. data/lib/mongo/operation.rb +3 -6
  60. data/lib/mongo/operation/commands.rb +24 -0
  61. data/lib/mongo/operation/{aggregate.rb → commands/aggregate.rb} +3 -41
  62. data/lib/mongo/operation/{aggregate → commands/aggregate}/result.rb +0 -0
  63. data/lib/mongo/operation/commands/collections_info.rb +66 -0
  64. data/lib/mongo/operation/{command.rb → commands/command.rb} +2 -18
  65. data/lib/mongo/operation/commands/indexes.rb +70 -0
  66. data/lib/mongo/operation/commands/list_collections.rb +54 -0
  67. data/lib/mongo/operation/commands/list_collections/result.rb +112 -0
  68. data/lib/mongo/operation/commands/list_indexes.rb +56 -0
  69. data/lib/mongo/operation/commands/list_indexes/result.rb +115 -0
  70. data/lib/mongo/operation/{map_reduce.rb → commands/map_reduce.rb} +3 -41
  71. data/lib/mongo/operation/{map_reduce → commands/map_reduce}/result.rb +0 -0
  72. data/lib/mongo/operation/{parallel_scan.rb → commands/parallel_scan.rb} +3 -23
  73. data/lib/mongo/operation/{parallel_scan → commands/parallel_scan}/result.rb +0 -0
  74. data/lib/mongo/operation/commands/user_query.rb +69 -0
  75. data/lib/mongo/operation/commands/users_info.rb +53 -0
  76. data/lib/mongo/operation/commands/users_info/result.rb +36 -0
  77. data/lib/mongo/operation/executable.rb +4 -68
  78. data/lib/mongo/operation/kill_cursors.rb +3 -3
  79. data/lib/mongo/operation/read.rb +0 -4
  80. data/lib/mongo/operation/read/get_more.rb +2 -22
  81. data/lib/mongo/operation/read/query.rb +2 -21
  82. data/lib/mongo/operation/{read_preferrable.rb → read_preference.rb} +3 -2
  83. data/lib/mongo/operation/specifiable.rb +24 -0
  84. data/lib/mongo/operation/write.rb +2 -0
  85. data/lib/mongo/operation/write/bulk.rb +6 -3
  86. data/lib/mongo/operation/write/bulk/bulkable.rb +82 -0
  87. data/lib/mongo/operation/write/bulk/delete.rb +71 -0
  88. data/lib/mongo/operation/write/bulk/delete/result.rb +74 -0
  89. data/lib/mongo/operation/write/bulk/insert.rb +96 -0
  90. data/lib/mongo/operation/write/bulk/insert/result.rb +129 -0
  91. data/lib/mongo/operation/write/bulk/legacy_mergable.rb +87 -0
  92. data/lib/mongo/operation/write/bulk/mergable.rb +71 -0
  93. data/lib/mongo/operation/write/bulk/update.rb +81 -0
  94. data/lib/mongo/operation/write/bulk/update/result.rb +174 -0
  95. data/lib/mongo/operation/write/command/create_index.rb +0 -1
  96. data/lib/mongo/operation/write/command/create_user.rb +0 -1
  97. data/lib/mongo/operation/write/command/delete.rb +0 -1
  98. data/lib/mongo/operation/write/command/drop_index.rb +0 -1
  99. data/lib/mongo/operation/write/command/insert.rb +0 -1
  100. data/lib/mongo/operation/write/command/remove_user.rb +0 -1
  101. data/lib/mongo/operation/write/command/update.rb +0 -1
  102. data/lib/mongo/operation/write/command/update_user.rb +0 -1
  103. data/lib/mongo/operation/write/command/writable.rb +13 -18
  104. data/lib/mongo/operation/write/create_index.rb +4 -27
  105. data/lib/mongo/operation/write/create_user.rb +4 -30
  106. data/lib/mongo/operation/write/delete.rb +5 -28
  107. data/lib/mongo/operation/write/drop_index.rb +3 -3
  108. data/lib/mongo/operation/write/gle.rb +48 -0
  109. data/lib/mongo/operation/write/idable.rb +5 -0
  110. data/lib/mongo/operation/write/insert.rb +2 -24
  111. data/lib/mongo/operation/write/remove_user.rb +4 -27
  112. data/lib/mongo/operation/write/update.rb +4 -32
  113. data/lib/mongo/operation/write/update_user.rb +4 -30
  114. data/lib/mongo/operation/write/write_command_enabled.rb +53 -0
  115. data/lib/mongo/options/mapper.rb +4 -2
  116. data/lib/mongo/protocol/delete.rb +68 -3
  117. data/lib/mongo/protocol/get_more.rb +54 -2
  118. data/lib/mongo/protocol/insert.rb +59 -1
  119. data/lib/mongo/protocol/kill_cursors.rb +53 -4
  120. data/lib/mongo/protocol/message.rb +12 -12
  121. data/lib/mongo/protocol/query.rb +139 -65
  122. data/lib/mongo/protocol/reply.rb +69 -1
  123. data/lib/mongo/protocol/update.rb +70 -1
  124. data/lib/mongo/server/connection.rb +11 -3
  125. data/lib/mongo/server/description.rb +29 -0
  126. data/lib/mongo/server/description/features.rb +2 -1
  127. data/lib/mongo/server/monitor.rb +2 -2
  128. data/lib/mongo/server_selector.rb +14 -10
  129. data/lib/mongo/server_selector/selectable.rb +24 -22
  130. data/lib/mongo/socket.rb +6 -3
  131. data/lib/mongo/socket/tcp.rb +2 -2
  132. data/lib/mongo/socket/unix.rb +5 -8
  133. data/lib/mongo/uri.rb +243 -139
  134. data/lib/mongo/version.rb +1 -1
  135. data/spec/mongo/address/unix_spec.rb +1 -1
  136. data/spec/mongo/address_spec.rb +25 -0
  137. data/spec/mongo/auth/ldap/conversation_spec.rb +43 -0
  138. data/spec/mongo/auth/user/view_spec.rb +26 -1
  139. data/spec/mongo/bulk_write/ordered_combiner_spec.rb +271 -0
  140. data/spec/mongo/bulk_write/unordered_combiner_spec.rb +239 -0
  141. data/spec/mongo/bulk_write_spec.rb +332 -166
  142. data/spec/mongo/client_spec.rb +25 -0
  143. data/spec/mongo/cluster/topology/replica_set_spec.rb +2 -0
  144. data/spec/mongo/collection/view/aggregation_spec.rb +65 -0
  145. data/spec/mongo/collection/view/immutable_spec.rb +103 -0
  146. data/spec/mongo/collection/view/map_reduce_spec.rb +98 -3
  147. data/spec/mongo/collection/view/readable_spec.rb +17 -30
  148. data/spec/mongo/collection/view_spec.rb +233 -7
  149. data/spec/mongo/collection_spec.rb +360 -18
  150. data/spec/mongo/command_monitoring_spec.rb +51 -0
  151. data/spec/mongo/connection_string_spec.rb +137 -0
  152. data/spec/mongo/database_spec.rb +27 -11
  153. data/spec/mongo/grid/file/chunk_spec.rb +5 -5
  154. data/spec/mongo/grid/file/{metadata_spec.rb → info_spec.rb} +29 -17
  155. data/spec/mongo/grid/file_spec.rb +8 -8
  156. data/spec/mongo/grid/fs_bucket_spec.rb +1020 -0
  157. data/spec/mongo/grid/stream/read_spec.rb +275 -0
  158. data/spec/mongo/grid/stream/write_spec.rb +440 -0
  159. data/spec/mongo/grid/stream_spec.rb +48 -0
  160. data/spec/mongo/gridfs_spec.rb +50 -0
  161. data/spec/mongo/logger_spec.rb +0 -40
  162. data/spec/mongo/monitoring/command_log_subscriber_spec.rb +76 -0
  163. data/spec/mongo/operation/{aggregate_spec.rb → commands/aggregate_spec.rb} +0 -42
  164. data/spec/mongo/operation/{read → commands}/collections_info_spec.rb +1 -1
  165. data/spec/mongo/operation/{command_spec.rb → commands/command_spec.rb} +0 -0
  166. data/spec/mongo/operation/{read → commands}/indexes_spec.rb +1 -1
  167. data/spec/mongo/operation/{map_reduce_spec.rb → commands/map_reduce_spec.rb} +0 -18
  168. data/spec/mongo/operation/kill_cursors_spec.rb +1 -1
  169. data/spec/mongo/operation/{read_preferrable_spec.rb → read_preference_spec.rb} +11 -11
  170. data/spec/mongo/operation/write/bulk/{bulk_delete_spec.rb → delete_spec.rb} +1 -12
  171. data/spec/mongo/operation/write/bulk/{bulk_insert_spec.rb → insert_spec.rb} +1 -12
  172. data/spec/mongo/operation/write/bulk/{bulk_update_spec.rb → update_spec.rb} +1 -12
  173. data/spec/mongo/operation/write/insert_spec.rb +0 -11
  174. data/spec/mongo/protocol/kill_cursors_spec.rb +5 -3
  175. data/spec/mongo/server/description_spec.rb +42 -0
  176. data/spec/mongo/server/monitor_spec.rb +21 -0
  177. data/spec/mongo/server_discovery_and_monitoring_spec.rb +1 -0
  178. data/spec/mongo/server_selection_spec.rb +3 -3
  179. data/spec/mongo/server_selector/nearest_spec.rb +34 -27
  180. data/spec/mongo/server_selector/primary_preferred_spec.rb +31 -30
  181. data/spec/mongo/server_selector/primary_spec.rb +14 -13
  182. data/spec/mongo/server_selector/secondary_preferred_spec.rb +27 -26
  183. data/spec/mongo/server_selector/secondary_spec.rb +23 -22
  184. data/spec/mongo/server_selector_spec.rb +87 -24
  185. data/spec/mongo/socket/unix_spec.rb +52 -0
  186. data/spec/mongo/uri_spec.rb +251 -39
  187. data/spec/spec_helper.rb +11 -4
  188. data/spec/support/authorization.rb +4 -5
  189. data/spec/support/command_monitoring.rb +365 -0
  190. data/spec/support/command_monitoring/bulkWrite.yml +73 -0
  191. data/spec/support/command_monitoring/command.yml +42 -0
  192. data/spec/support/command_monitoring/deleteMany.yml +55 -0
  193. data/spec/support/command_monitoring/deleteOne.yml +55 -0
  194. data/spec/support/command_monitoring/find.yml +219 -0
  195. data/spec/support/command_monitoring/insertMany.yml +81 -0
  196. data/spec/support/command_monitoring/insertOne.yml +51 -0
  197. data/spec/support/command_monitoring/updateMany.yml +67 -0
  198. data/spec/support/command_monitoring/updateOne.yml +95 -0
  199. data/spec/support/connection_string.rb +228 -0
  200. data/spec/support/connection_string_tests/invalid-uris.yml +193 -0
  201. data/spec/support/connection_string_tests/valid-auth.yml +256 -0
  202. data/spec/support/connection_string_tests/valid-host_identifiers.yml +121 -0
  203. data/spec/support/connection_string_tests/valid-options.yml +30 -0
  204. data/spec/support/connection_string_tests/valid-unix_socket-absolute.yml +197 -0
  205. data/spec/support/connection_string_tests/valid-unix_socket-relative.yml +213 -0
  206. data/spec/support/connection_string_tests/valid-warnings.yml +55 -0
  207. data/spec/support/crud.rb +3 -1
  208. data/spec/support/crud/read.rb +14 -10
  209. data/spec/support/crud/write.rb +36 -9
  210. data/spec/support/gridfs.rb +637 -0
  211. data/spec/support/gridfs_tests/delete.yml +157 -0
  212. data/spec/support/gridfs_tests/download.yml +210 -0
  213. data/spec/support/gridfs_tests/download_by_name.yml +113 -0
  214. data/spec/support/gridfs_tests/upload.yml +158 -0
  215. data/spec/support/sdam/rs/equal_electionids.yml +1 -2
  216. data/spec/support/sdam/rs/new_primary_new_electionid.yml +0 -3
  217. data/spec/support/sdam/rs/primary_mismatched_me.yml +37 -0
  218. data/spec/support/sdam/rs/primary_to_no_primary_mismatched_me.yml +75 -0
  219. data/spec/support/sdam/rs/secondary_mismatched_me.yml +37 -0
  220. data/spec/support/sdam/single/direct_connection_rsarbiter.yml +1 -1
  221. data/spec/support/sdam/single/direct_connection_rsprimary.yml +1 -1
  222. data/spec/support/sdam/single/direct_connection_rssecondary.yml +1 -1
  223. data/spec/support/sdam/single/direct_connection_slave.yml +1 -1
  224. data/spec/support/sdam/single/direct_connection_standalone.yml +1 -1
  225. data/spec/support/sdam/single/not_ok_response.yml +0 -1
  226. data/spec/support/server_discovery_and_monitoring.rb +3 -1
  227. data/spec/support/server_selection.rb +3 -1
  228. data/spec/support/shared/bulk_write.rb +192 -0
  229. data/spec/support/shared/server_selector.rb +21 -12
  230. metadata +147 -57
  231. metadata.gz.sig +0 -0
  232. data/lib/mongo/bulk_write/bulk_writable.rb +0 -252
  233. data/lib/mongo/bulk_write/deletable.rb +0 -57
  234. data/lib/mongo/bulk_write/insertable.rb +0 -49
  235. data/lib/mongo/bulk_write/replacable.rb +0 -58
  236. data/lib/mongo/bulk_write/updatable.rb +0 -69
  237. data/lib/mongo/grid/fs.rb +0 -146
  238. data/lib/mongo/operation/list_collections/result.rb +0 -114
  239. data/lib/mongo/operation/list_indexes/result.rb +0 -118
  240. data/lib/mongo/operation/read/collections_info.rb +0 -68
  241. data/lib/mongo/operation/read/indexes.rb +0 -69
  242. data/lib/mongo/operation/read/list_collections.rb +0 -76
  243. data/lib/mongo/operation/read/list_indexes.rb +0 -78
  244. data/lib/mongo/operation/write/bulk/bulk_delete.rb +0 -145
  245. data/lib/mongo/operation/write/bulk/bulk_delete/result.rb +0 -75
  246. data/lib/mongo/operation/write/bulk/bulk_insert.rb +0 -132
  247. data/lib/mongo/operation/write/bulk/bulk_insert/result.rb +0 -130
  248. data/lib/mongo/operation/write/bulk/bulk_mergable.rb +0 -67
  249. data/lib/mongo/operation/write/bulk/bulk_update.rb +0 -154
  250. data/lib/mongo/operation/write/bulk/bulk_update/result.rb +0 -174
  251. data/lib/mongo/operation/write/bulk/legacy_bulk_mergable.rb +0 -83
  252. data/spec/mongo/grid/fs_spec.rb +0 -160
  253. data/spec/mongo/loggable_spec.rb +0 -63
@@ -160,17 +160,17 @@ module Mongo
160
160
  # Chunks.split(data)
161
161
  #
162
162
  # @param [ String ] data The raw bytes.
163
- # @param [ Metadata ] metadata The file metadata.
163
+ # @param [ File::Info ] file_info The files collection file doc.
164
164
  #
165
165
  # @return [ Array<Chunk> ] The chunks of the data.
166
166
  #
167
167
  # @since 2.0.0
168
- def split(data, metadata)
169
- chunks, index, n = [], 0, 0
168
+ def split(data, file_info, offset = 0)
169
+ chunks, index, n = [], 0, offset
170
170
  while index < data.length
171
- bytes = data.slice(index, metadata.chunk_size)
172
- metadata.md5.update(bytes)
173
- chunk = Chunk.new(:data => BSON::Binary.new(bytes), :files_id => metadata.id, :n => n)
171
+ bytes = data.slice(index, file_info.chunk_size)
172
+ file_info.md5.update(bytes)
173
+ chunk = Chunk.new(:data => BSON::Binary.new(bytes), :files_id => file_info.id, :n => n)
174
174
  chunks.push(chunk)
175
175
  index += bytes.length
176
176
  n += 1
@@ -16,10 +16,10 @@ module Mongo
16
16
  module Grid
17
17
  class File
18
18
 
19
- # Encapsulates behaviour around GridFS file metadata.
19
+ # Encapsulates behaviour around GridFS files collection file document.
20
20
  #
21
21
  # @since 2.0.0
22
- class Metadata
22
+ class Info
23
23
 
24
24
  # Name of the files collection.
25
25
  #
@@ -35,8 +35,10 @@ module Mongo
35
35
  :filename => :filename,
36
36
  :_id => :_id,
37
37
  :md5 => :md5,
38
+ :length => :length,
38
39
  :metadata => :metadata,
39
- :upload_date => :uploadDate
40
+ :upload_date => :uploadDate,
41
+ :aliases => :aliases
40
42
  }.freeze
41
43
 
42
44
  # Default content type for stored files.
@@ -44,13 +46,13 @@ module Mongo
44
46
  # @since 2.0.0
45
47
  DEFAULT_CONTENT_TYPE = 'binary/octet-stream'.freeze
46
48
 
47
- # @return [ BSON::Document ] document The file metadata document.
49
+ # @return [ BSON::Document ] document The files collection document.
48
50
  attr_reader :document
49
51
 
50
- # Is this metadata equal to another?
52
+ # Is this file information document equal to another?
51
53
  #
52
- # @example Check metadata equality.
53
- # metadata == other
54
+ # @example Check file information document equality.
55
+ # file_info == other
54
56
  #
55
57
  # @param [ Object ] other The object to check against.
56
58
  #
@@ -58,14 +60,14 @@ module Mongo
58
60
  #
59
61
  # @since 2.0.0
60
62
  def ==(other)
61
- return false unless other.is_a?(Metadata)
63
+ return false unless other.is_a?(Info)
62
64
  document == other.document
63
65
  end
64
66
 
65
- # Get the BSON type for a metadata document.
67
+ # Get the BSON type for a files information document.
66
68
  #
67
69
  # @example Get the BSON type.
68
- # metadata.bson_type
70
+ # file_info.bson_type
69
71
  #
70
72
  # @return [ Integer ] The BSON type.
71
73
  #
@@ -74,10 +76,10 @@ module Mongo
74
76
  BSON::Hash::BSON_TYPE
75
77
  end
76
78
 
77
- # Get the metadata chunk size.
79
+ # Get the file chunk size.
78
80
  #
79
81
  # @example Get the chunk size.
80
- # metadata.chunk_size
82
+ # file_info.chunk_size
81
83
  #
82
84
  # @return [ Integer ] The chunksize in bytes.
83
85
  #
@@ -86,10 +88,10 @@ module Mongo
86
88
  document[:chunkSize]
87
89
  end
88
90
 
89
- # Get the metadata content type.
91
+ # Get the file information content type.
90
92
  #
91
93
  # @example Get the content type.
92
- # metadata.content_type
94
+ # file_info.content_type
93
95
  #
94
96
  # @return [ String ] The content type.
95
97
  #
@@ -98,32 +100,32 @@ module Mongo
98
100
  document[:contentType]
99
101
  end
100
102
 
101
- # Get the metadata filename.
103
+ # Get the filename from the file information.
102
104
  #
103
105
  # @example Get the filename.
104
- # metadata.filename
106
+ # file_info.filename
105
107
  #
106
108
  # @return [ String ] The filename.
107
109
  def filename
108
110
  document[:filename]
109
111
  end
110
112
 
111
- # Get the metadata id.
113
+ # Get the file id from the file information.
112
114
  #
113
- # @example Get the metadata id.
114
- # metadata.id
115
+ # @example Get the file id.
116
+ # file_info.id
115
117
  #
116
- # @return [ BSON::ObjectId ] The metadata id.
118
+ # @return [ BSON::ObjectId ] The file id.
117
119
  #
118
120
  # @since 2.0.0
119
121
  def id
120
122
  document[:_id]
121
123
  end
122
124
 
123
- # Create the new metadata document.
125
+ # Create the new file information document.
124
126
  #
125
- # @example Create the new metadata document.
126
- # Metadata.new(:filename => 'test.txt')
127
+ # @example Create the new file information document.
128
+ # Info.new(:filename => 'test.txt')
127
129
  #
128
130
  # @param [ BSON::Document ] document The document to create from.
129
131
  #
@@ -135,23 +137,23 @@ module Mongo
135
137
 
136
138
  # Get a readable inspection for the object.
137
139
  #
138
- # @example Inspect the metadata.
139
- # metadata.inspect
140
+ # @example Inspect the file information.
141
+ # file_info.inspect
140
142
  #
141
143
  # @return [ String ] The nice inspection.
142
144
  #
143
145
  # @since 2.0.0
144
146
  def inspect
145
- "#<Mongo::Grid::File::Metadata:0x#{object_id} chunk_size=#{chunk_size} " +
147
+ "#<Mongo::Grid::File::Info:0x#{object_id} chunk_size=#{chunk_size} " +
146
148
  "filename=#{filename} content_type=#{content_type} id=#{id} md5=#{md5}>"
147
149
  end
148
150
 
149
151
  # Get the length of the document in bytes.
150
152
  #
151
- # @example Get the length
152
- # metadata.length
153
+ # @example Get the file length from the file information document.
154
+ # file_info.length
153
155
  #
154
- # @return [ Integer ] The length.
156
+ # @return [ Integer ] The file length.
155
157
  #
156
158
  # @since 2.0.0
157
159
  def length
@@ -159,12 +161,12 @@ module Mongo
159
161
  end
160
162
  alias :size :length
161
163
 
162
- # Get the additional metadata.
164
+ # Get the additional metadata from the file information document.
163
165
  #
164
166
  # @example Get additional metadata.
165
- # metadata.metadata
167
+ # file_info.metadata
166
168
  #
167
- # @return [ String ] The additional metadata.
169
+ # @return [ String ] The additional metadata from file information document.
168
170
  #
169
171
  # @since 2.0.0
170
172
  def metadata
@@ -174,7 +176,7 @@ module Mongo
174
176
  # Get the md5 hash.
175
177
  #
176
178
  # @example Get the md5 hash.
177
- # metadata.md5
179
+ # file_info.md5
178
180
  #
179
181
  # @return [ String ] The md5 hash as a string.
180
182
  #
@@ -183,13 +185,13 @@ module Mongo
183
185
  document[:md5] || @client_md5
184
186
  end
185
187
 
186
- # Conver the metadata to BSON for storage.
188
+ # Convert the file information document to BSON for storage.
187
189
  #
188
- # @note If no md5 exists in the metadata (it was loaded from the server
189
- # and is not a new file) then we digest the md5 and set it.
190
+ # @note If no md5 exists in the file information document (it was loaded
191
+ # from the server and is not a new file) then we digest the md5 and set it.
190
192
  #
191
- # @example Convert the metadata to BSON.
192
- # metadata.to_bson
193
+ # @example Convert the file information document to BSON.
194
+ # file_info.to_bson
193
195
  #
194
196
  # @param [ String ] encoded The encoded data to append to.
195
197
  #
@@ -204,7 +206,7 @@ module Mongo
204
206
  # Get the upload date.
205
207
  #
206
208
  # @example Get the upload date.
207
- # metadata.upload_date
209
+ # file_info.upload_date
208
210
  #
209
211
  # @return [ Time ] The upload date.
210
212
  #
@@ -0,0 +1,441 @@
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
+
23
+ # The default root prefix.
24
+ #
25
+ # @since 2.0.0
26
+ DEFAULT_ROOT = 'fs'.freeze
27
+
28
+ # The specification for the chunks collection index.
29
+ #
30
+ # @since 2.0.0
31
+ CHUNKS_INDEX = { :files_id => 1, :n => 1 }.freeze
32
+
33
+ # The specification for the files collection index.
34
+ #
35
+ # @since 2.1.0
36
+ FILES_INDEX = { filename: 1, uploadDate: 1 }.freeze
37
+
38
+ # @return [ Collection ] chunks_collection The chunks collection.
39
+ #
40
+ # @since 2.0.0
41
+ attr_reader :chunks_collection
42
+
43
+ # @return [ Database ] database The database.
44
+ #
45
+ # @since 2.0.0
46
+ attr_reader :database
47
+
48
+ # @return [ Collection ] files_collection The files collection.
49
+ #
50
+ # @since 2.0.0
51
+ attr_reader :files_collection
52
+
53
+ # @return [ Hash ] options The FSBucket options.
54
+ #
55
+ # @since 2.1.0
56
+ attr_reader :options
57
+
58
+ # Find files collection documents matching a given selector.
59
+ #
60
+ # @example Find files collection documents by a filename.
61
+ # fs.find(filename: 'file.txt')
62
+ #
63
+ # @param [ Hash ] selector The selector to use in the find.
64
+ # @param [ Hash ] options The options for the find.
65
+ #
66
+ # @option options [ Integer ] :batch_size The number of documents returned in each batch
67
+ # of results from MongoDB.
68
+ # @option options [ Integer ] :limit The max number of docs to return from the query.
69
+ # @option options [ true, false ] :no_cursor_timeout The server normally times out idle
70
+ # cursors after an inactivity period (10 minutes) to prevent excess memory use.
71
+ # Set this option to prevent that.
72
+ # @option options [ Integer ] :skip The number of docs to skip before returning results.
73
+ # @option options [ Hash ] :sort The key and direction pairs by which the result set
74
+ # will be sorted.
75
+ #
76
+ # @return [ CollectionView ] The collection view.
77
+ #
78
+ # @since 2.1.0
79
+ def find(selector = nil, options = {})
80
+ files_collection.find(selector, options.merge(read: read_preference))
81
+ end
82
+
83
+ # Find a file in the GridFS.
84
+ #
85
+ # @example Find a file by its id.
86
+ # fs.find_one(_id: id)
87
+ #
88
+ # @example Find a file by its filename.
89
+ # fs.find_one(filename: 'test.txt')
90
+ #
91
+ # @param [ Hash ] selector The selector.
92
+ #
93
+ # @return [ Grid::File ] The file.
94
+ #
95
+ # @since 2.0.0
96
+ def find_one(selector = nil)
97
+ file_info = files_collection.find(selector).first
98
+ return nil unless file_info
99
+ chunks = chunks_collection.find(:files_id => file_info[:_id]).sort(:n => 1)
100
+ Grid::File.new(chunks.to_a, file_info)
101
+ end
102
+
103
+ # Insert a single file into the GridFS.
104
+ #
105
+ # @example Insert a single file.
106
+ # fs.insert_one(file)
107
+ #
108
+ # @param [ Grid::File ] file The file to insert.
109
+ #
110
+ # @return [ BSON::ObjectId ] The file id.
111
+ #
112
+ # @since 2.0.0
113
+ def insert_one(file)
114
+ @indexes ||= ensure_indexes!
115
+ chunks_collection.insert_many(file.chunks)
116
+ files_collection.insert_one(file.info)
117
+ file.id
118
+ end
119
+
120
+ # Create the GridFS.
121
+ #
122
+ # @example Create the GridFS.
123
+ # Grid::FSBucket.new(database)
124
+ #
125
+ # @param [ Database ] database The database the files reside in.
126
+ # @param [ Hash ] options The GridFS options.
127
+ #
128
+ # @option options [ String ] :fs_name The prefix for the files and chunks
129
+ # collections.
130
+ # @option options [ String ] :bucket_name The prefix for the files and chunks
131
+ # collections.
132
+ # @option options [ Integer ] :chunk_size Override the default chunk
133
+ # size.
134
+ # @option options [ String ] :write The write concern.
135
+ # @option options [ String ] :read The read preference.
136
+ #
137
+ # @since 2.0.0
138
+ def initialize(database, options = {})
139
+ @database = database
140
+ @options = options
141
+ @chunks_collection = database[chunks_name]
142
+ @files_collection = database[files_name]
143
+ end
144
+
145
+ # Get the prefix for the GridFS
146
+ #
147
+ # @example Get the prefix.
148
+ # fs.prefix
149
+ #
150
+ # @return [ String ] The GridFS prefix.
151
+ #
152
+ # @since 2.0.0
153
+ def prefix
154
+ @options[:fs_name] || @options[:bucket_name]|| DEFAULT_ROOT
155
+ end
156
+
157
+ # Remove a single file from the GridFS.
158
+ #
159
+ # @example Remove a file from the GridFS.
160
+ # fs.delete_one(file)
161
+ #
162
+ # @param [ Grid::File ] file The file to remove.
163
+ #
164
+ # @return [ Result ] The result of the remove.
165
+ #
166
+ # @since 2.0.0
167
+ def delete_one(file)
168
+ delete(file.id)
169
+ end
170
+
171
+ # Remove a single file, identified by its id from the GridFS.
172
+ #
173
+ # @example Remove a file from the GridFS.
174
+ # fs.delete(id)
175
+ #
176
+ # @param [ BSON::ObjectId, Object ] id The id of the file to remove.
177
+ #
178
+ # @return [ Result ] The result of the remove.
179
+ #
180
+ # @raise [ Error::FileNotFound ] If the file is not found.
181
+ #
182
+ # @since 2.1.0
183
+ def delete(id)
184
+ result = files_collection.find(:_id => id).delete_one
185
+ chunks_collection.find(:files_id => id).delete_many
186
+ raise Error::FileNotFound.new(id, :id) if result.n == 0
187
+ result
188
+ end
189
+
190
+ # Opens a stream from which a file can be downloaded, specified by id.
191
+ #
192
+ # @example Open a stream from which a file can be downloaded.
193
+ # fs.open_download_stream(id)
194
+ #
195
+ # @param [ BSON::ObjectId, Object ] id The id of the file to read.
196
+ #
197
+ # @return [ Stream::Read ] The stream to read from.
198
+ #
199
+ # @yieldparam [ Hash ] The read stream.
200
+ #
201
+ # @since 2.1.0
202
+ def open_download_stream(id)
203
+ read_stream(id).tap do |stream|
204
+ if block_given?
205
+ yield stream
206
+ stream.close
207
+ end
208
+ end
209
+ end
210
+
211
+ # Downloads the contents of the file specified by id and writes them to
212
+ # the destination io object.
213
+ #
214
+ # @example Download the file and write it to the io object.
215
+ # fs.download_to_stream(id, io)
216
+ #
217
+ # @param [ BSON::ObjectId, Object ] id The id of the file to read.
218
+ # @param [ IO ] io The io object to write to.
219
+ #
220
+ # @since 2.1.0
221
+ def download_to_stream(id, io)
222
+ open_download_stream(id) do |stream|
223
+ stream.each do |chunk|
224
+ io << chunk
225
+ end
226
+ end
227
+ end
228
+
229
+ # Opens a stream from which the application can read the contents of the stored file
230
+ # specified by filename and the revision in options.
231
+ #
232
+ # Revision numbers are defined as follows:
233
+ # 0 = the original stored file
234
+ # 1 = the first revision
235
+ # 2 = the second revision
236
+ # etc…
237
+ # -2 = the second most recent revision
238
+ # -1 = the most recent revision
239
+ #
240
+ # @example Open a stream to download the most recent revision.
241
+ # fs.open_download_stream_by_name('some-file.txt')
242
+ #
243
+ # # @example Open a stream to download the original file.
244
+ # fs.open_download_stream_by_name('some-file.txt', revision: 0)
245
+ #
246
+ # @example Open a stream to download the second revision of the stored file.
247
+ # fs.open_download_stream_by_name('some-file.txt', revision: 2)
248
+ #
249
+ # @param [ String ] filename The file's name.
250
+ # @param [ Hash ] opts Options for the download.
251
+ #
252
+ # @option opts [ Integer ] :revision The revision number of the file to download.
253
+ # Defaults to -1, the most recent version.
254
+ #
255
+ # @return [ Stream::Read ] The stream to read from.
256
+ #
257
+ # @raise [ Error::FileNotFound ] If the file is not found.
258
+ # @raise [ Error::InvalidFileRevision ] If the requested revision is not found for the file.
259
+ #
260
+ # @yieldparam [ Hash ] The read stream.
261
+ #
262
+ # @since 2.1.0
263
+ def open_download_stream_by_name(filename, opts = {}, &block)
264
+ revision = opts.fetch(:revision, -1)
265
+ if revision < 0
266
+ skip = revision.abs - 1
267
+ sort = { 'uploadDate' => Mongo::Index::DESCENDING }
268
+ else
269
+ skip = revision
270
+ sort = { 'uploadDate' => Mongo::Index::ASCENDING }
271
+ end
272
+ file_doc = files_collection.find({ filename: filename} ,
273
+ projection: { _id: 1 },
274
+ sort: sort,
275
+ skip: skip,
276
+ limit: -1).first
277
+ unless file_doc
278
+ raise Error::FileNotFound.new(filename, :filename) unless opts[:revision]
279
+ raise Error::InvalidFileRevision.new(filename, opts[:revision])
280
+ end
281
+ open_download_stream(file_doc[:_id], &block)
282
+ end
283
+
284
+ # Downloads the contents of the stored file specified by filename and by the
285
+ # revision in options and writes the contents to the destination io object.
286
+ #
287
+ # Revision numbers are defined as follows:
288
+ # 0 = the original stored file
289
+ # 1 = the first revision
290
+ # 2 = the second revision
291
+ # etc…
292
+ # -2 = the second most recent revision
293
+ # -1 = the most recent revision
294
+ #
295
+ # @example Download the most recent revision.
296
+ # fs.download_to_stream_by_name('some-file.txt', io)
297
+ #
298
+ # # @example Download the original file.
299
+ # fs.download_to_stream_by_name('some-file.txt', io, revision: 0)
300
+ #
301
+ # @example Download the second revision of the stored file.
302
+ # fs.download_to_stream_by_name('some-file.txt', io, revision: 2)
303
+ #
304
+ # @param [ String ] filename The file's name.
305
+ # @param [ IO ] io The io object to write to.
306
+ # @param [ Hash ] opts Options for the download.
307
+ #
308
+ # @option opts [ Integer ] :revision The revision number of the file to download.
309
+ # Defaults to -1, the most recent version.
310
+ #
311
+ # @raise [ Error::FileNotFound ] If the file is not found.
312
+ # @raise [ Error::InvalidFileRevision ] If the requested revision is not found for the file.
313
+ #
314
+ # @since 2.1.0
315
+ def download_to_stream_by_name(filename, io, opts = {})
316
+ download_to_stream(open_download_stream_by_name(filename, opts).file_id, io)
317
+ end
318
+
319
+ # Opens an upload stream to GridFS to which the contents of a user file came be written.
320
+ #
321
+ # @example Open a stream to which the contents of a file came be written.
322
+ # fs.open_upload_stream('a-file.txt')
323
+ #
324
+ # @param [ String ] filename The filename of the file to upload.
325
+ # @param [ Hash ] opts The options for the write stream.
326
+ #
327
+ # @option opts [ Integer ] :chunk_size Override the default chunk size.
328
+ # @option opts [ Hash ] :write The write concern.
329
+ # @option opts [ Hash ] :metadata User data for the 'metadata' field of the files
330
+ # collection document.
331
+ # @option opts [ String ] :content_type The content type of the file.
332
+ # Deprecated, please use the metadata document instead.
333
+ # @option opts [ Array<String> ] :aliases A list of aliases.
334
+ # Deprecated, please use the metadata document instead.
335
+ #
336
+ # @return [ Stream::Write ] The write stream.
337
+ #
338
+ # @yieldparam [ Hash ] The write stream.
339
+ #
340
+ # @since 2.1.0
341
+ def open_upload_stream(filename, opts = {})
342
+ write_stream(filename, opts).tap do |stream|
343
+ if block_given?
344
+ yield stream
345
+ stream.close
346
+ end
347
+ end
348
+ end
349
+
350
+ # Uploads a user file to a GridFS bucket.
351
+ # Reads the contents of the user file from the source stream and uploads it as chunks in the
352
+ # chunks collection. After all the chunks have been uploaded, it creates a files collection
353
+ # document for the filename in the files collection.
354
+ #
355
+ # @example Open a stream to which the contents of a file came be written.
356
+ # fs.open_upload_stream('a-file.txt')
357
+ #
358
+ # @param [ String ] filename The filename of the file to upload.
359
+ # @param [ IO ] io The source io stream to upload from.
360
+ # @param [ Hash ] opts The options for the write stream.
361
+ #
362
+ # @option opts [ Integer ] :chunk_size Override the default chunk size.
363
+ # @option opts [ Hash ] :write The write concern.
364
+ # @option opts [ Hash ] :metadata User data for the 'metadata' field of the files
365
+ # collection document.
366
+ # @option opts [ String ] :content_type The content type of the file. Deprecated, please
367
+ # use the metadata document instead.
368
+ # @option opts [ Array<String> ] :aliases A list of aliases. Deprecated, please use the
369
+ # metadata document instead.
370
+ #
371
+ # @return [ BSON::ObjectId ] The ObjectId file id.
372
+ #
373
+ # @since 2.1.0
374
+ def upload_from_stream(filename, io, opts = {})
375
+ open_upload_stream(filename, opts) do |stream|
376
+ begin
377
+ stream.write(io)
378
+ rescue IOError
379
+ begin
380
+ stream.abort
381
+ rescue Error::OperationFailure
382
+ end
383
+ raise
384
+ end
385
+ end.file_id
386
+ end
387
+
388
+ # Get the read preference.
389
+ #
390
+ # @example Get the read preference.
391
+ # fs.read_preference
392
+ #
393
+ # @return [ Mongo::ServerSelector ] The read preference.
394
+ #
395
+ # @since 2.1.0
396
+ def read_preference
397
+ @read_preference ||= @options[:read] ?
398
+ ServerSelector.get((@options[:read] || {}).merge(database.options)) :
399
+ database.read_preference
400
+ end
401
+
402
+ # Get the write concern.
403
+ #
404
+ # @example Get the write concern.
405
+ # stream.write_concern
406
+ #
407
+ # @return [ Mongo::WriteConcern ] The write concern.
408
+ #
409
+ # @since 2.1.0
410
+ def write_concern
411
+ @write_concern ||= @options[:write] ? WriteConcern.get(@options[:write]) :
412
+ database.write_concern
413
+ end
414
+
415
+ private
416
+
417
+ def read_stream(id)
418
+ Stream.get(self, Stream::READ_MODE, { file_id: id }.merge!(options))
419
+ end
420
+
421
+ def write_stream(filename, opts)
422
+ Stream.get(self, Stream::WRITE_MODE, { filename: filename }.merge!(options).merge!(opts))
423
+ end
424
+
425
+ def chunks_name
426
+ "#{prefix}.#{Grid::File::Chunk::COLLECTION}"
427
+ end
428
+
429
+ def files_name
430
+ "#{prefix}.#{Grid::File::Info::COLLECTION}"
431
+ end
432
+
433
+ def ensure_indexes!
434
+ if files_collection.find({}, projection: { _id: 1 }).to_a.empty?
435
+ chunks_collection.indexes.create_one(FSBucket::CHUNKS_INDEX, :unique => true)
436
+ files_collection.indexes.create_one(FSBucket::FILES_INDEX)
437
+ end
438
+ end
439
+ end
440
+ end
441
+ end