mongo 2.0.6 → 2.1.0.beta

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 (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.