mongo 2.0.6 → 2.1.0.beta

Sign up to get free protection for your applications and to get access to all the features.
Files changed (119) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/lib/mongo.rb +2 -0
  5. data/lib/mongo/bulk_write.rb +1 -0
  6. data/lib/mongo/bulk_write/bulk_writable.rb +87 -31
  7. data/lib/mongo/bulk_write/deletable.rb +8 -7
  8. data/lib/mongo/bulk_write/insertable.rb +4 -3
  9. data/lib/mongo/bulk_write/ordered_bulk_write.rb +6 -6
  10. data/lib/mongo/bulk_write/replacable.rb +4 -3
  11. data/lib/mongo/bulk_write/result.rb +138 -0
  12. data/lib/mongo/bulk_write/unordered_bulk_write.rb +5 -8
  13. data/lib/mongo/bulk_write/updatable.rb +8 -7
  14. data/lib/mongo/client.rb +36 -4
  15. data/lib/mongo/cluster.rb +39 -4
  16. data/lib/mongo/cluster/topology/replica_set.rb +20 -4
  17. data/lib/mongo/cluster/topology/sharded.rb +1 -1
  18. data/lib/mongo/collection.rb +282 -29
  19. data/lib/mongo/collection/view/aggregation.rb +32 -4
  20. data/lib/mongo/collection/view/iterable.rb +2 -1
  21. data/lib/mongo/collection/view/map_reduce.rb +3 -1
  22. data/lib/mongo/collection/view/readable.rb +89 -14
  23. data/lib/mongo/collection/view/writable.rb +11 -5
  24. data/lib/mongo/cursor.rb +11 -3
  25. data/lib/mongo/dbref.rb +113 -0
  26. data/lib/mongo/error.rb +6 -2
  27. data/lib/mongo/error/parser.rb +1 -1
  28. data/lib/mongo/event/description_changed.rb +1 -1
  29. data/lib/mongo/grid/file.rb +1 -1
  30. data/lib/mongo/grid/fs.rb +2 -5
  31. data/lib/mongo/monitoring.rb +199 -0
  32. data/lib/mongo/monitoring/command_log_subscriber.rb +88 -0
  33. data/lib/mongo/monitoring/event.rb +17 -0
  34. data/lib/mongo/monitoring/event/command_failed.rb +96 -0
  35. data/lib/mongo/monitoring/event/command_started.rb +88 -0
  36. data/lib/mongo/monitoring/event/command_succeeded.rb +96 -0
  37. data/lib/mongo/monitoring/publishable.rb +96 -0
  38. data/lib/mongo/operation.rb +1 -0
  39. data/lib/mongo/operation/executable.rb +1 -1
  40. data/lib/mongo/operation/parallel_scan.rb +76 -0
  41. data/lib/mongo/operation/parallel_scan/result.rb +72 -0
  42. data/lib/mongo/operation/specifiable.rb +18 -0
  43. data/lib/mongo/operation/write/bulk/bulk_delete.rb +1 -1
  44. data/lib/mongo/operation/write/bulk/bulk_insert.rb +1 -1
  45. data/lib/mongo/operation/write/bulk/bulk_mergable.rb +2 -2
  46. data/lib/mongo/operation/write/bulk/bulk_update.rb +1 -1
  47. data/lib/mongo/operation/write/bulk/bulk_update/result.rb +13 -1
  48. data/lib/mongo/protocol/delete.rb +8 -13
  49. data/lib/mongo/protocol/get_more.rb +13 -13
  50. data/lib/mongo/protocol/insert.rb +8 -13
  51. data/lib/mongo/protocol/kill_cursors.rb +7 -11
  52. data/lib/mongo/protocol/query.rb +58 -20
  53. data/lib/mongo/protocol/reply.rb +12 -0
  54. data/lib/mongo/protocol/update.rb +13 -14
  55. data/lib/mongo/server.rb +23 -2
  56. data/lib/mongo/server/connectable.rb +0 -22
  57. data/lib/mongo/server/connection.rb +29 -0
  58. data/lib/mongo/server/description.rb +23 -1
  59. data/lib/mongo/server/monitor.rb +17 -1
  60. data/lib/mongo/server/monitor/connection.rb +24 -0
  61. data/lib/mongo/socket/ssl.rb +28 -16
  62. data/lib/mongo/socket/tcp.rb +1 -1
  63. data/lib/mongo/socket/unix.rb +1 -1
  64. data/lib/mongo/uri.rb +12 -5
  65. data/lib/mongo/version.rb +1 -1
  66. data/spec/mongo/auth/cr_spec.rb +9 -1
  67. data/spec/mongo/auth/ldap_spec.rb +9 -1
  68. data/spec/mongo/auth/scram_spec.rb +9 -1
  69. data/spec/mongo/auth/x509_spec.rb +9 -1
  70. data/spec/mongo/{bulk/bulk_write_spec.rb → bulk_write_spec.rb} +15 -15
  71. data/spec/mongo/client_spec.rb +42 -0
  72. data/spec/mongo/cluster/topology/replica_set_spec.rb +16 -9
  73. data/spec/mongo/cluster/topology/sharded_spec.rb +11 -3
  74. data/spec/mongo/cluster/topology/single_spec.rb +12 -4
  75. data/spec/mongo/cluster_spec.rb +55 -10
  76. data/spec/mongo/collection/view/aggregation_spec.rb +123 -1
  77. data/spec/mongo/collection/view/explainable_spec.rb +1 -1
  78. data/spec/mongo/collection/view/map_reduce_spec.rb +1 -1
  79. data/spec/mongo/collection/view/readable_spec.rb +251 -6
  80. data/spec/mongo/collection/view/writable_spec.rb +4 -4
  81. data/spec/mongo/collection/view_spec.rb +233 -71
  82. data/spec/mongo/collection_spec.rb +905 -9
  83. data/spec/mongo/crud_spec.rb +2 -2
  84. data/spec/mongo/cursor_spec.rb +3 -3
  85. data/spec/mongo/dbref_spec.rb +149 -0
  86. data/spec/mongo/monitoring_spec.rb +168 -0
  87. data/spec/mongo/operation/map_reduce_spec.rb +1 -1
  88. data/spec/mongo/operation/write/bulk/bulk_delete_spec.rb +1 -1
  89. data/spec/mongo/operation/write/bulk/bulk_insert_spec.rb +2 -2
  90. data/spec/mongo/operation/write/bulk/bulk_update_spec.rb +1 -1
  91. data/spec/mongo/operation/write/delete_spec.rb +1 -1
  92. data/spec/mongo/operation/write/insert_spec.rb +2 -2
  93. data/spec/mongo/operation/write/update_spec.rb +1 -1
  94. data/spec/mongo/protocol/query_spec.rb +0 -29
  95. data/spec/mongo/server/connection_pool_spec.rb +18 -6
  96. data/spec/mongo/server/connection_spec.rb +12 -4
  97. data/spec/mongo/server/description_spec.rb +7 -3
  98. data/spec/mongo/server/monitor_spec.rb +30 -0
  99. data/spec/mongo/server_discovery_and_monitoring_spec.rb +11 -4
  100. data/spec/mongo/server_selection_spec.rb +14 -6
  101. data/spec/mongo/server_spec.rb +27 -8
  102. data/spec/mongo/socket/ssl_spec.rb +94 -8
  103. data/spec/mongo/uri_spec.rb +25 -9
  104. data/spec/spec_helper.rb +29 -20
  105. data/spec/support/authorization.rb +19 -4
  106. data/spec/support/certificates/client.pem +4 -4
  107. data/spec/support/crud/read.rb +9 -10
  108. data/spec/support/crud/write.rb +24 -20
  109. data/spec/support/sdam/rs/equal_electionids.yml +45 -0
  110. data/spec/support/sdam/rs/new_primary_new_electionid.yml +98 -0
  111. data/spec/support/sdam/rs/null_election_id.yml +144 -0
  112. data/spec/support/sdam/rs/primary_disconnect_electionid.yml +124 -0
  113. data/spec/support/sdam/sharded/mongos_disconnect.yml +104 -0
  114. data/spec/support/server_discovery_and_monitoring.rb +19 -2
  115. data/spec/support/shared/bulk_write.rb +26 -22
  116. data/spec/support/shared/server_selector.rb +2 -1
  117. metadata +31 -7
  118. metadata.gz.sig +0 -0
  119. data/lib/mongo/error/invalid_uri_option.rb +0 -38
@@ -13,11 +13,12 @@
13
13
  # limitations under the License.
14
14
 
15
15
  module Mongo
16
-
17
16
  module BulkWrite
18
17
 
18
+ # Encapsulates behaviour around an unordered bulk write operation.
19
+ #
20
+ # @since 2.0.0
19
21
  class UnorderedBulkWrite
20
-
21
22
  include BulkWritable
22
23
 
23
24
  private
@@ -35,12 +36,8 @@ module Mongo
35
36
  end
36
37
 
37
38
  def finalize
38
- @results.tap do |results|
39
- if results[:write_errors] || results[:write_concern_errors]
40
- raise Error::BulkWriteError.new(results)
41
- end
42
- end
39
+ Result.new(@results).validate!
43
40
  end
44
41
  end
45
42
  end
46
- end
43
+ end
@@ -46,23 +46,24 @@ module Mongo
46
46
  end
47
47
  end
48
48
 
49
- def update(ops, type, server)
49
+ def update(ops, type, server, operation_id)
50
50
  Operation::Write::BulkUpdate.new(
51
51
  :updates => updates(ops, type),
52
52
  :db_name => database.name,
53
53
  :coll_name => @collection.name,
54
54
  :write_concern => write_concern,
55
- :ordered => ordered?
55
+ :ordered => ordered?,
56
+ :operation_id => operation_id
56
57
  ).execute(server.context)
57
58
  end
58
59
 
59
- def update_one(op, server)
60
- update(op[:update_one], __method__, server)
60
+ def update_one(op, server, operation_id)
61
+ update(op[:update_one], __method__, server, operation_id)
61
62
  end
62
63
 
63
- def update_many(op, server)
64
- update(op[:update_many], __method__, server)
64
+ def update_many(op, server, operation_id)
65
+ update(op[:update_many], __method__, server, operation_id)
65
66
  end
66
67
  end
67
68
  end
68
- end
69
+ end
@@ -39,6 +39,9 @@ module Mongo
39
39
  # Delegate command execution to the current database.
40
40
  def_delegators :@database, :command
41
41
 
42
+ # Delegate subscription to monitoring.
43
+ def_delegators :@monitoring, :subscribe, :unsubscribe
44
+
42
45
  # Determine if this client is equivalent to another object.
43
46
  #
44
47
  # @example Check client equality.
@@ -143,14 +146,18 @@ module Mongo
143
146
  # @option options [ String ] :user The user name.
144
147
  # @option options [ Hash ] :write The write concern options. Can be :w =>
145
148
  # Integer, :fsync => Boolean, :j => Boolean.
149
+ # @option options [ true, false ] :monitoring Initializes a client without
150
+ # any default monitoring if false is provided.
146
151
  #
147
152
  # @since 2.0.0
148
153
  def initialize(addresses_or_uri, options = {})
154
+ @monitoring = Monitoring.new(options)
149
155
  if addresses_or_uri.is_a?(::String)
150
156
  create_from_uri(addresses_or_uri, options)
151
157
  else
152
158
  create_from_addresses(addresses_or_uri, options)
153
159
  end
160
+ yield(self) if block_given?
154
161
  end
155
162
 
156
163
  # Get an inspection of the client as a string.
@@ -207,11 +214,12 @@ module Mongo
207
214
  # @since 2.0.0
208
215
  def with(new_options = {})
209
216
  clone.tap do |client|
210
- client.options.update(new_options)
217
+ opts = new_options || {}
218
+ client.options.update(opts)
211
219
  Database.create(client)
212
220
  # We can't use the same cluster if some options that would affect it
213
221
  # have changed.
214
- if cluster_modifying?(new_options)
222
+ if cluster_modifying?(opts)
215
223
  Cluster.create(client)
216
224
  end
217
225
  end
@@ -230,6 +238,30 @@ module Mongo
230
238
  @write_concern ||= WriteConcern.get(options[:write])
231
239
  end
232
240
 
241
+ # Close all connections.
242
+ #
243
+ # @example Disconnect the client.
244
+ # client.close
245
+ #
246
+ # @return [ true ] Always true.
247
+ #
248
+ # @since 2.1.0
249
+ def close
250
+ @cluster.disconnect! and true
251
+ end
252
+
253
+ # Reconnect the client.
254
+ #
255
+ # @example Reconnect the client.
256
+ # client.reconnect
257
+ #
258
+ # @return [ true ] Always true.
259
+ #
260
+ # @since 2.1.0
261
+ def reconnect
262
+ @cluster.reconnect! and true
263
+ end
264
+
233
265
  # Get the names of all databases.
234
266
  #
235
267
  # @example Get the database names.
@@ -258,14 +290,14 @@ module Mongo
258
290
 
259
291
  def create_from_addresses(addresses, opts = {})
260
292
  @options = Database::DEFAULT_OPTIONS.merge(opts).freeze
261
- @cluster = Cluster.new(addresses, options)
293
+ @cluster = Cluster.new(addresses, @monitoring, options)
262
294
  @database = Database.new(self, options[:database], options)
263
295
  end
264
296
 
265
297
  def create_from_uri(connection_string, opts = {})
266
298
  uri = URI.new(connection_string)
267
299
  @options = Database::DEFAULT_OPTIONS.merge(uri.client_options.merge(opts)).freeze
268
- @cluster = Cluster.new(uri.servers, options)
300
+ @cluster = Cluster.new(uri.servers, @monitoring, options)
269
301
  @database = Database.new(self, options[:database], options)
270
302
  end
271
303
 
@@ -67,7 +67,7 @@ module Mongo
67
67
  if addition_allowed?(address)
68
68
  log_debug([ "Adding #{address.to_s} to the cluster." ])
69
69
  @update_lock.synchronize { @addresses.push(address) }
70
- server = Server.new(address, self, event_listeners, options)
70
+ server = Server.new(address, self, @monitoring, event_listeners, options)
71
71
  @update_lock.synchronize { @servers.push(server) }
72
72
  server
73
73
  end
@@ -76,16 +76,22 @@ module Mongo
76
76
 
77
77
  # Instantiate the new cluster.
78
78
  #
79
+ # @api private
80
+ #
79
81
  # @example Instantiate the cluster.
80
- # Mongo::Cluster.new(["127.0.0.1:27017"])
82
+ # Mongo::Cluster.new(["127.0.0.1:27017"], monitoring)
83
+ #
84
+ # @note Cluster should never be directly instantiated outside of a Client.
81
85
  #
82
86
  # @param [ Array<String> ] seeds The addresses of the configured servers.
87
+ # @param [ Monitoring ] monitoring The monitoring.
83
88
  # @param [ Hash ] options The options.
84
89
  #
85
90
  # @since 2.0.0
86
- def initialize(seeds, options = {})
91
+ def initialize(seeds, monitoring, options = {})
87
92
  @addresses = []
88
93
  @servers = []
94
+ @monitoring = monitoring
89
95
  @event_listeners = Event::Listeners.new
90
96
  @options = options.freeze
91
97
  @topology = Topology.initial(seeds, options)
@@ -196,6 +202,31 @@ module Mongo
196
202
  topology.servers(servers_list.compact).compact
197
203
  end
198
204
 
205
+ # Disconnect all servers.
206
+ #
207
+ # @example Disconnect the cluster's servers.
208
+ # cluster.disconnect!
209
+ #
210
+ # @return [ true ] Always true.
211
+ #
212
+ # @since 2.1.0
213
+ def disconnect!
214
+ @servers.each { |server| server.disconnect! } and true
215
+ end
216
+
217
+ # Reconnect all servers.
218
+ #
219
+ # @example Reconnect the cluster's servers.
220
+ # cluster.reconnect!
221
+ #
222
+ # @return [ true ] Always true.
223
+ #
224
+ # @since 2.1.0
225
+ def reconnect!
226
+ scan!
227
+ servers.each { |server| server.reconnect! } and true
228
+ end
229
+
199
230
  # Add hosts in a description to the cluster.
200
231
  #
201
232
  # @example Add hosts in a description to the cluster.
@@ -240,7 +271,11 @@ module Mongo
240
271
  #
241
272
  # @since 2.0.0
242
273
  def self.create(client)
243
- cluster = Cluster.new(client.cluster.addresses.map(&:to_s), client.options)
274
+ cluster = Cluster.new(
275
+ client.cluster.addresses.map(&:to_s),
276
+ client.instance_variable_get(:@monitoring).dup,
277
+ client.options
278
+ )
244
279
  client.instance_variable_set(:@cluster, cluster)
245
280
  end
246
281
 
@@ -60,11 +60,14 @@ module Mongo
60
60
  # @return [ ReplicaSet ] The topology.
61
61
  def elect_primary(description, servers)
62
62
  if description.replica_set_name == replica_set_name
63
- log_debug([ "Server #{description.address.to_s} elected as primary in #{replica_set_name}." ])
64
- servers.each do |server|
65
- if server.primary? && server.address != description.address
66
- server.description.unknown!
63
+ unless detect_stale_primary!(description)
64
+ log_debug([ "Server #{description.address.to_s} elected as primary in #{replica_set_name}." ])
65
+ servers.each do |server|
66
+ if server.primary? && server.address != description.address
67
+ server.description.unknown!
68
+ end
67
69
  end
70
+ update_max_election_id(description)
68
71
  end
69
72
  else
70
73
  log_warn([
@@ -84,6 +87,7 @@ module Mongo
84
87
  # @since 2.0.0
85
88
  def initialize(options, seeds = [])
86
89
  @options = options
90
+ @max_election_id = 0
87
91
  end
88
92
 
89
93
  # A replica set topology is a replica set.
@@ -215,6 +219,18 @@ module Mongo
215
219
 
216
220
  private
217
221
 
222
+ def update_max_election_id(description)
223
+ if description.election_id && description.election_id > @max_election_id
224
+ @max_election_id = description.election_id
225
+ end
226
+ end
227
+
228
+ def detect_stale_primary!(description)
229
+ if description.election_id && description.election_id < @max_election_id
230
+ description.unknown!
231
+ end
232
+ end
233
+
218
234
  def has_primary?(servers)
219
235
  servers.find { |s| s.primary? }
220
236
  end
@@ -184,7 +184,7 @@ module Mongo
184
184
  private
185
185
 
186
186
  def remove_self?(description, server)
187
- description.is_server?(server) && !description.mongos?
187
+ description.is_server?(server) && !(description.mongos? || description.unknown?)
188
188
  end
189
189
  end
190
190
  end
@@ -57,6 +57,23 @@ module Mongo
57
57
  name == other.name && database == other.database && options == other.options
58
58
  end
59
59
 
60
+ # Instantiate a new collection.
61
+ #
62
+ # @example Instantiate a new collection.
63
+ # Mongo::Collection.new(database, 'test')
64
+ #
65
+ # @param [ Mongo::Database ] database The collection's database.
66
+ # @param [ String, Symbol ] name The collection name.
67
+ # @param [ Hash ] options The collection options.
68
+ #
69
+ # @since 2.0.0
70
+ def initialize(database, name, options = {})
71
+ raise Error::InvalidCollectionName.new unless name
72
+ @database = database
73
+ @name = name.to_s.freeze
74
+ @options = options.freeze
75
+ end
76
+
60
77
  # Is the collection capped?
61
78
  #
62
79
  # @example Is the collection capped?
@@ -108,12 +125,96 @@ module Mongo
108
125
  # collection.find
109
126
  #
110
127
  # @param [ Hash ] filter The filter to use in the find.
128
+ # @param [ Hash ] options The options for the find.
129
+ #
130
+ # @option options [ true, false ] :allow_partial_results Allows the query to get partial
131
+ # results if some shards are down.
132
+ # @option options [ Integer ] :batch_size The number of documents returned in each batch
133
+ # of results from MongoDB.
134
+ # @option options [ String ] :comment Associate a comment with the query.
135
+ # @option options [ :tailable, :tailable_await ] :cursor_type The type of cursor to use.
136
+ # @option options [ Integer ] :limit The max number of docs to return from the query.
137
+ # @option options [ Integer ] :max_time_ms The maximum amount of time to allow the query
138
+ # to run in milliseconds.
139
+ # @option options [ Hash ] :modifiers A document containing meta-operators modifying the
140
+ # output or behavior of a query.
141
+ # @option options [ true, false ] :no_cursor_timeout The server normally times out idle
142
+ # cursors after an inactivity period (10 minutes) to prevent excess memory use.
143
+ # Set this option to prevent that.
144
+ # @option options [ true, false ] :oplog_replay Internal replication use only - driver
145
+ # should not set.
146
+ # @option options [ Hash ] :projection The fields to include or exclude from each doc
147
+ # in the result set.
148
+ # @option options [ Integer ] :skip The number of docs to skip before returning results.
149
+ # @option options [ Hash ] :sort The key and direction pairs by which the result set
150
+ # will be sorted.
111
151
  #
112
152
  # @return [ CollectionView ] The collection view.
113
153
  #
114
154
  # @since 2.0.0
115
- def find(filter = nil)
116
- View.new(self, filter || {})
155
+ def find(filter = nil, options = {})
156
+ View.new(self, filter || {}, options)
157
+ end
158
+
159
+ # Perform an aggregation on the collection.
160
+ #
161
+ # @example Perform an aggregation.
162
+ # collection.aggregate([ { "$group" => { "_id" => "$city", "tpop" => { "$sum" => "$pop" }}} ])
163
+ #
164
+ # @param [ Array<Hash> ] pipeline The aggregation pipeline.
165
+ # @param [ Hash ] options The aggregation options.
166
+ #
167
+ # @option options [ true, false ] :allow_disk_use Set to true if disk usage is allowed during
168
+ # the aggregation.
169
+ # @option options [ Integer ] :batch_size The number of documents to return per batch.
170
+ # @option options [ Integer ] :max_time_ms The maximum amount of time in milliseconds to allow the
171
+ # aggregation to run.
172
+ # @option options [ true, false ] :use_cursor Indicates whether the command will request that the server
173
+ # provide results using a cursor.
174
+ #
175
+ # @return [ Aggregation ] The aggregation object.
176
+ #
177
+ # @since 2.1.0
178
+ def aggregate(pipeline, options = {})
179
+ View.new(self, {}).aggregate(pipeline, options)
180
+ end
181
+
182
+ # Get a count of matching documents in the collection.
183
+ #
184
+ # @example Get the count.
185
+ # collection.count(name: 1)
186
+ #
187
+ # @param [ Hash ] filter A filter for matching documents.
188
+ # @param [ Hash ] options The count options.
189
+ #
190
+ # @option options [ Hash ] :hint The index to use.
191
+ # @option options [ Integer ] :limit The maximum number of documents to count.
192
+ # @option options [ Integer ] :max_time_ms The maximum amount of time to allow the command to run.
193
+ # @option options [ Integer ] :skip The number of documents to skip before counting.
194
+ #
195
+ # @return [ Integer ] The document count.
196
+ #
197
+ # @since 2.1.0
198
+ def count(filter = nil, options = {})
199
+ View.new(self, filter || {}).count(options)
200
+ end
201
+
202
+ # Get a list of distinct values for a specific field.
203
+ #
204
+ # @example Get the distinct values.
205
+ # collection.distinct('name')
206
+ #
207
+ # @param [ Symbol, String ] field_name The name of the field.
208
+ # @param [ Hash ] filter The documents from which to retrieve the distinct values.
209
+ # @param [ Hash ] options The distinct command options.
210
+ #
211
+ # @option options [ Integer ] :max_time_ms The maximum amount of time to allow the command to run.
212
+ #
213
+ # @return [ Array<Object> ] The list of distinct values.
214
+ #
215
+ # @since 2.1.0
216
+ def distinct(field_name, filter = nil, options = {})
217
+ View.new(self, filter || {}).distinct(field_name, options)
117
218
  end
118
219
 
119
220
  # Get a view of all indexes for this collection. Can be iterated or has
@@ -131,23 +232,6 @@ module Mongo
131
232
  Index::View.new(self, options)
132
233
  end
133
234
 
134
- # Instantiate a new collection.
135
- #
136
- # @example Instantiate a new collection.
137
- # Mongo::Collection.new(database, 'test')
138
- #
139
- # @param [ Mongo::Database ] database The collection's database.
140
- # @param [ String, Symbol ] name The collection name.
141
- # @param [ Hash ] options The collection options.
142
- #
143
- # @since 2.0.0
144
- def initialize(database, name, options = {})
145
- raise Error::InvalidCollectionName.new unless name
146
- @database = database
147
- @name = name.to_s.freeze
148
- @options = options.freeze
149
- end
150
-
151
235
  # Get a pretty printed string inspection for the collection.
152
236
  #
153
237
  # @example Inspect the collection.
@@ -172,7 +256,13 @@ module Mongo
172
256
  #
173
257
  # @since 2.0.0
174
258
  def insert_one(document, options = {})
175
- insert_many([ document ], options)
259
+ Operation::Write::Insert.new(
260
+ :documents => [ document ],
261
+ :db_name => database.name,
262
+ :coll_name => name,
263
+ :write_concern => write_concern,
264
+ :options => options
265
+ ).execute(next_primary.context)
176
266
  end
177
267
 
178
268
  # Insert the provided documents into the collection.
@@ -187,13 +277,8 @@ module Mongo
187
277
  #
188
278
  # @since 2.0.0
189
279
  def insert_many(documents, options = {})
190
- Operation::Write::Insert.new(
191
- :documents => documents,
192
- :db_name => database.name,
193
- :coll_name => name,
194
- :write_concern => write_concern,
195
- :options => options
196
- ).execute(next_primary.context)
280
+ inserts = documents.map{ |doc| { :insert_one => doc }}
281
+ bulk_write(inserts, options)
197
282
  end
198
283
 
199
284
  # Execute a batch of bulk write operations.
@@ -204,13 +289,181 @@ module Mongo
204
289
  # @param [ Array<Hash> ] operations The operations.
205
290
  # @param [ Hash ] options The options.
206
291
  #
207
- # @return [ BSON::Document ] The result of the operation.
292
+ # @option options [ true, false ] :ordered Whether the operations
293
+ # should be executed in order.
294
+ # @option options [ Hash ] :write_concern The write concern options.
295
+ # Can be :w => Integer, :fsync => Boolean, :j => Boolean.
296
+ #
297
+ # @return [ BulkWrite::Result ] The result of the operation.
208
298
  #
209
299
  # @since 2.0.0
210
- def bulk_write(operations, options)
300
+ def bulk_write(operations, options = {})
211
301
  BulkWrite.get(self, operations, options).execute
212
302
  end
213
303
 
304
+ # Remove a document from the collection.
305
+ #
306
+ # @example Remove a single document from the collection.
307
+ # collection.delete_one
308
+ #
309
+ # @param [ Hash ] filter The filter to use.
310
+ #
311
+ # @return [ Result ] The response from the database.
312
+ #
313
+ # @since 2.1.0
314
+ def delete_one(filter = nil)
315
+ find(filter).delete_one
316
+ end
317
+
318
+ # Remove documents from the collection.
319
+ #
320
+ # @example Remove multiple documents from the collection.
321
+ # collection.delete_many
322
+ #
323
+ # @param [ Hash ] filter The filter to use.
324
+ #
325
+ # @return [ Result ] The response from the database.
326
+ #
327
+ # @since 2.1.0
328
+ def delete_many(filter = nil)
329
+ find(filter).delete_many
330
+ end
331
+
332
+ # Replaces a single document in the collection with the new document.
333
+ #
334
+ # @example Replace a single document.
335
+ # collection.replace_one({ name: 'test' }, { name: 'test1' })
336
+ #
337
+ # @param [ Hash ] filter The filter to use.
338
+ # @param [ Hash ] replacement The replacement document..
339
+ # @param [ Hash ] options The options.
340
+ #
341
+ # @option options [ true, false ] :upsert Whether to upsert if the
342
+ # document doesn't exist.
343
+ #
344
+ # @return [ Result ] The response from the database.
345
+ #
346
+ # @since 2.1.0
347
+ def replace_one(filter, replacement, options = {})
348
+ find(filter).replace_one(replacement, options)
349
+ end
350
+
351
+ # Update documents in the collection.
352
+ #
353
+ # @example Update multiple documents in the collection.
354
+ # collection.update_many({ name: 'test'}, '$set' => { name: 'test1' })
355
+ #
356
+ # @param [ Hash ] filter The filter to use.
357
+ # @param [ Hash ] update The update statement.
358
+ # @param [ Hash ] options The options.
359
+ #
360
+ # @option options [ true, false ] :upsert Whether to upsert if the
361
+ # document doesn't exist.
362
+ #
363
+ # @return [ Result ] The response from the database.
364
+ #
365
+ # @since 2.1.0
366
+ def update_many(filter, update, options = {})
367
+ find(filter).update_many(update, options)
368
+ end
369
+
370
+ # Update a single document in the collection.
371
+ #
372
+ # @example Update a single document in the collection.
373
+ # collection.update_one({ name: 'test'}, '$set' => { name: 'test1'})
374
+ #
375
+ # @param [ Hash ] filter The filter to use.
376
+ # @param [ Hash ] update The update statement.
377
+ # @param [ Hash ] options The options.
378
+ #
379
+ # @option options [ true, false ] :upsert Whether to upsert if the
380
+ # document doesn't exist.
381
+ #
382
+ # @return [ Result ] The response from the database.
383
+ #
384
+ # @since 2.1.0
385
+ def update_one(filter, update, options = {})
386
+ find(filter).update_one(update, options)
387
+ end
388
+
389
+ # Finds a single document in the database via findAndModify and deletes
390
+ # it, returning the original document.
391
+ #
392
+ # @example Find one document and delete it.
393
+ # collection.find_one_and_delete(name: 'test')
394
+ #
395
+ # @param [ Hash ] filter The filter to use.
396
+ # @param [ Hash ] options The options.
397
+ #
398
+ # @option options [ Integer ] :max_time_ms The maximum amount of time to allow the command
399
+ # to run in milliseconds.
400
+ # @option options [ Hash ] :projection The fields to include or exclude in the returned doc.
401
+ # @option options [ Hash ] :sort The key and direction pairs by which the result set
402
+ # will be sorted.
403
+ #
404
+ # @return [ BSON::Document, nil ] The document, if found.
405
+ #
406
+ # @since 2.1.0
407
+ def find_one_and_delete(filter, options = {})
408
+ find(filter, options).find_one_and_delete
409
+ end
410
+
411
+ # Finds a single document via findAndModify and updates it, returning the original doc unless
412
+ # otherwise specified.
413
+ #
414
+ # @example Find a document and update it, returning the original.
415
+ # collection.find_one_and_update({ name: 'test' }, { "$set" => { name: 'test1' }})
416
+ #
417
+ # @example Find a document and update it, returning the updated document.
418
+ # collection.find_one_and_update({ name: 'test' }, { "$set" => { name: 'test1' }}, :return_document => :after)
419
+ #
420
+ # @param [ Hash ] filter The filter to use.
421
+ # @param [ BSON::Document ] update The update statement.
422
+ # @param [ Hash ] options The options.
423
+ #
424
+ # @option options [ Integer ] :max_time_ms The maximum amount of time to allow the command
425
+ # to run in milliseconds.
426
+ # @option options [ Hash ] :projection The fields to include or exclude in the returned doc.
427
+ # @option options [ Hash ] :sort The key and direction pairs by which the result set
428
+ # will be sorted.
429
+ # @option options [ Symbol ] :return_document Either :before or :after.
430
+ # @option options [ true, false ] :upsert Whether to upsert if the document doesn't exist.
431
+ #
432
+ # @return [ BSON::Document ] The document.
433
+ #
434
+ # @since 2.1.0
435
+ def find_one_and_update(filter, update, options = {})
436
+ find(filter, options).find_one_and_update(update, options)
437
+ end
438
+
439
+ # Finds a single document and replaces it, returning the original doc unless
440
+ # otherwise specified.
441
+ #
442
+ # @example Find a document and replace it, returning the original.
443
+ # collection.find_one_and_replace({ name: 'test' }, { name: 'test1' })
444
+ #
445
+ # @example Find a document and replace it, returning the new document.
446
+ # collection.find_one_and_replace({ name: 'test' }, { name: 'test1' }, :return_document => :after)
447
+ #
448
+ # @param [ Hash ] filter The filter to use.
449
+ # @param [ BSON::Document ] replacement The replacement document.
450
+ # @param [ Hash ] options The options.
451
+ #
452
+ # @option options [ Integer ] :max_time_ms The maximum amount of time to allow the command
453
+ # to run in milliseconds.
454
+ # @option options [ Hash ] :projection The fields to include or exclude in the returned doc.
455
+ # @option options [ Hash ] :sort The key and direction pairs by which the result set
456
+ # will be sorted.
457
+ # @option options [ Symbol ] :return_document Either :before or :after.
458
+ # @option options [ true, false ] :upsert Whether to upsert if the document doesn't exist.
459
+ #
460
+ # @return [ BSON::Document ] The document.
461
+ #
462
+ # @since 2.1.0
463
+ def find_one_and_replace(filter, replacement, options = {})
464
+ find(filter, options).find_one_and_update(replacement, options)
465
+ end
466
+
214
467
  # Get the fully qualified namespace of the collection.
215
468
  #
216
469
  # @example Get the fully qualified namespace.