mongo 1.1.1 → 1.1.2

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.
@@ -3,7 +3,7 @@
3
3
  $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
4
4
 
5
5
  module Mongo
6
- VERSION = "1.1.1"
6
+ VERSION = "1.1.2"
7
7
  end
8
8
 
9
9
  module Mongo
@@ -14,20 +14,27 @@
14
14
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
15
  # See the License for the specific language governing permissions and
16
16
  # limitations under the License.
17
- # ++
18
17
 
19
18
  module Mongo
20
19
 
21
20
  # A named collection of documents in a database.
22
21
  class Collection
23
22
 
24
- attr_reader :db, :name, :pk_factory, :hint
23
+ attr_reader :db, :name, :pk_factory, :hint, :safe
25
24
 
26
25
  # Initialize a collection object.
27
26
  #
28
27
  # @param [DB] db a MongoDB database instance.
29
28
  # @param [String, Symbol] name the name of the collection.
30
29
  #
30
+ # @option options [:create_pk] :pk (BSON::ObjectId) A primary key factory to use
31
+ # other than the default BSON::ObjectId.
32
+ #
33
+ # @option options [Boolean, Hash] :safe (false) Set the default safe-mode options
34
+ # for insert, update, and remove method called on this Collection instance. If no
35
+ # value is provided, the default value set on this instance's DB will be used. This
36
+ # default can be overridden for any invocation of insert, update, or remove.
37
+ #
31
38
  # @raise [InvalidNSName]
32
39
  # if collection name is empty, contains '$', or starts or ends with '.'
33
40
  #
@@ -37,7 +44,7 @@ module Mongo
37
44
  # @return [Collection]
38
45
  #
39
46
  # @core collections constructor_details
40
- def initialize(db, name, pk_factory=nil)
47
+ def initialize(db, name, options={})
41
48
  case name
42
49
  when Symbol, String
43
50
  else
@@ -56,10 +63,21 @@ module Mongo
56
63
  raise Mongo::InvalidNSName, "collection names must not start or end with '.'"
57
64
  end
58
65
 
66
+ if options.respond_to?(:create_pk) || !options.is_a?(Hash)
67
+ warn "The method for specifying a primary key factory on a Collection has changed.\n" +
68
+ "Please specify it as an option (e.g., :pk => PkFactory)."
69
+ pk_factory = options
70
+ else
71
+ pk_factory = nil
72
+ end
73
+
59
74
  @db, @name = db, name
60
75
  @connection = @db.connection
61
76
  @logger = @connection.logger
62
- @pk_factory = pk_factory || BSON::ObjectId
77
+ unless pk_factory
78
+ @safe = options.has_key?(:safe) ? options[:safe] : @db.safe
79
+ end
80
+ @pk_factory = pk_factory || options[:pk] || BSON::ObjectId
63
81
  @hint = nil
64
82
  end
65
83
 
@@ -108,7 +126,10 @@ module Mongo
108
126
  #
109
127
  # @param [Hash] selector
110
128
  # a document specifying elements which must be present for a
111
- # document to be included in the result set.
129
+ # document to be included in the result set. Note that in rare cases,
130
+ # (e.g., with $near queries), the order of keys will matter. To preserve
131
+ # key order on a selector, use an instance of BSON::OrderedHash (only applies
132
+ # to Ruby 1.8).
112
133
  #
113
134
  # @option opts [Array, Hash] :fields field names that should be returned in the result
114
135
  # set ("_id" will always be included). By limiting results to a certain subset of fields,
@@ -120,12 +141,12 @@ module Mongo
120
141
  # @option opts [Array] :sort an array of [key, direction] pairs to sort by. Direction should
121
142
  # be specified as Mongo::ASCENDING (or :ascending / :asc) or Mongo::DESCENDING (or :descending / :desc)
122
143
  # @option opts [String, Array, OrderedHash] :hint hint for query optimizer, usually not necessary if using MongoDB > 1.1
123
- # @option opts [Boolean] :snapshot ('false') if true, snapshot mode will be used for this query.
144
+ # @option opts [Boolean] :snapshot (false) if true, snapshot mode will be used for this query.
124
145
  # Snapshot mode assures no duplicates are returned, or objects missed, which were preset at both the start and
125
146
  # end of the query's execution. For details see http://www.mongodb.org/display/DOCS/How+to+do+Snapshotting+in+the+Mongo+Database
126
147
  # @option opts [Boolean] :batch_size (100) the number of documents to returned by the database per GETMORE operation. A value of 0
127
148
  # will let the database server decide how many results to returns. This option can be ignored for most use cases.
128
- # @option opts [Boolean] :timeout ('true') when +true+, the returned cursor will be subject to
149
+ # @option opts [Boolean] :timeout (true) when +true+, the returned cursor will be subject to
129
150
  # the normal cursor timeout behavior of the mongod process. When +false+, the returned cursor will never timeout. Note
130
151
  # that disabling timeout will only work when #find is invoked with a block. This is to prevent any inadvertant failure to
131
152
  # close the cursor, as the cursor is explicitly closed when block code finishes.
@@ -144,13 +165,12 @@ module Mongo
144
165
  limit = opts.delete(:limit) || 0
145
166
  sort = opts.delete(:sort)
146
167
  hint = opts.delete(:hint)
147
- snapshot = opts.delete(:snapshot)
168
+ snapshot = opts.delete(:snapshot)
148
169
  batch_size = opts.delete(:batch_size)
170
+ timeout = (opts.delete(:timeout) == false) ? false : true
149
171
 
150
- if opts[:timeout] == false && !block_given?
151
- raise ArgumentError, "Timeout can be set to false only when #find is invoked with a block."
152
- else
153
- timeout = opts.delete(:timeout) || false
172
+ if timeout == false && !block_given?
173
+ raise ArgumentError, "Collection#find must be invoked with a block when timeout is disabled."
154
174
  end
155
175
 
156
176
  if hint
@@ -166,7 +186,7 @@ module Mongo
166
186
 
167
187
  if block_given?
168
188
  yield cursor
169
- cursor.close()
189
+ cursor.close
170
190
  nil
171
191
  else
172
192
  cursor
@@ -242,8 +262,10 @@ module Mongo
242
262
  # @option opts [Boolean, Hash] :safe (+false+)
243
263
  # run the operation in safe mode, which run a getlasterror command on the
244
264
  # database to report any assertion. In addition, a hash can be provided to
245
- # run an fsync and/or wait for replication of the insert (>= 1.5.1). See the options
246
- # for DB#error.
265
+ # run an fsync and/or wait for replication of the insert (>= 1.5.1). Safe
266
+ # options provided here will override any safe options set on this collection,
267
+ # its database object, or the current connection. See the options on
268
+ # for DB#get_last_error.
247
269
  #
248
270
  # @see DB#remove for options that can be passed to :safe.
249
271
  #
@@ -251,7 +273,8 @@ module Mongo
251
273
  def insert(doc_or_docs, options={})
252
274
  doc_or_docs = [doc_or_docs] unless doc_or_docs.is_a?(Array)
253
275
  doc_or_docs.collect! { |doc| @pk_factory.create_pk(doc) }
254
- result = insert_documents(doc_or_docs, @name, true, options[:safe])
276
+ safe = options.has_key?(:safe) ? options[:safe] : @safe
277
+ result = insert_documents(doc_or_docs, @name, true, safe)
255
278
  result.size > 1 ? result : result.first
256
279
  end
257
280
  alias_method :<<, :insert
@@ -262,10 +285,11 @@ module Mongo
262
285
  # If specified, only matching documents will be removed.
263
286
  #
264
287
  # @option opts [Boolean, Hash] :safe (+false+)
265
- # run the operation in safe mode, which run a getlasterror command on the
288
+ # run the operation in safe mode, which will run a getlasterror command on the
266
289
  # database to report any assertion. In addition, a hash can be provided to
267
- # run an fsync and/or wait for replication of the remove (>= 1.5.1). See the options
268
- # for DB#get_last_error.
290
+ # run an fsync and/or wait for replication of the remove (>= 1.5.1). Safe
291
+ # options provided here will override any safe options set on this collection,
292
+ # its database, or the current connection. See the options for DB#get_last_error for more details.
269
293
  #
270
294
  # @example remove all documents from the 'users' collection:
271
295
  # users.remove
@@ -284,14 +308,15 @@ module Mongo
284
308
  # @core remove remove-instance_method
285
309
  def remove(selector={}, opts={})
286
310
  # Initial byte is 0.
311
+ safe = opts.has_key?(:safe) ? opts[:safe] : @safe
287
312
  message = BSON::ByteBuffer.new("\0\0\0\0")
288
313
  BSON::BSON_RUBY.serialize_cstr(message, "#{@db.name}.#{@name}")
289
314
  message.put_int(0)
290
315
  message.put_binary(BSON::BSON_CODER.serialize(selector, false, true).to_s)
291
316
 
292
317
  @logger.debug("MONGODB #{@db.name}['#{@name}'].remove(#{selector.inspect})") if @logger
293
- if opts[:safe]
294
- @connection.send_message_with_safe_check(Mongo::Constants::OP_DELETE, message, @db.name, nil, opts[:safe])
318
+ if safe
319
+ @connection.send_message_with_safe_check(Mongo::Constants::OP_DELETE, message, @db.name, nil, safe)
295
320
  # the return value of send_message_with_safe_check isn't actually meaningful --
296
321
  # only the fact that it didn't raise an error is -- so just return true
297
322
  true
@@ -317,11 +342,14 @@ module Mongo
317
342
  # @option opts [Boolean] :safe (+false+)
318
343
  # If true, check that the save succeeded. OperationFailure
319
344
  # will be raised on an error. Note that a safe check requires an extra
320
- # round-trip to the database.
345
+ # round-trip to the database. Safe options provided here will override any safe
346
+ # options set on this collection, its database object, or the current collection.
347
+ # See the options for DB#get_last_error for details.
321
348
  #
322
349
  # @core update update-instance_method
323
350
  def update(selector, document, options={})
324
351
  # Initial byte is 0.
352
+ safe = options.has_key?(:safe) ? options[:safe] : @safe
325
353
  message = BSON::ByteBuffer.new("\0\0\0\0")
326
354
  BSON::BSON_RUBY.serialize_cstr(message, "#{@db.name}.#{@name}")
327
355
  update_options = 0
@@ -331,8 +359,8 @@ module Mongo
331
359
  message.put_binary(BSON::BSON_CODER.serialize(selector, false, true).to_s)
332
360
  message.put_binary(BSON::BSON_CODER.serialize(document, false, true).to_s)
333
361
  @logger.debug("MONGODB #{@db.name}['#{@name}'].update(#{selector.inspect}, #{document.inspect})") if @logger
334
- if options[:safe]
335
- @connection.send_message_with_safe_check(Mongo::Constants::OP_UPDATE, message, @db.name, nil, options[:safe])
362
+ if safe
363
+ @connection.send_message_with_safe_check(Mongo::Constants::OP_UPDATE, message, @db.name, nil, safe)
336
364
  else
337
365
  @connection.send_message(Mongo::Constants::OP_UPDATE, message, nil)
338
366
  end
@@ -355,10 +383,10 @@ module Mongo
355
383
  # @option opts [Boolean] :unique (false) if true, this index will enforce a uniqueness constraint.
356
384
  # @option opts [Boolean] :background (false) indicate that the index should be built in the background. This
357
385
  # feature is only available in MongoDB >= 1.3.2.
358
- # @option opts [Boolean] :dropDups If creating a unique index on a collection with pre-existing records,
386
+ # @option opts [Boolean] :drop_dups (nil) If creating a unique index on a collection with pre-existing records,
359
387
  # this option will keep the first document the database indexes and drop all subsequent with duplicate values.
360
- # @option opts [Integer] :min specify the minimum longitude and latitude for a geo index.
361
- # @option opts [Integer] :max specify the maximum longitude and latitude for a geo index.
388
+ # @option opts [Integer] :min (nil) specify the minimum longitude and latitude for a geo index.
389
+ # @option opts [Integer] :max (nil) specify the maximum longitude and latitude for a geo index.
362
390
  #
363
391
  # @example Creating a compound index:
364
392
  # @posts.create_index([['subject', Mongo::ASCENDING], ['created_at', Mongo::DESCENDING]])
@@ -378,7 +406,7 @@ module Mongo
378
406
  #
379
407
  # @core indexes create_index-instance_method
380
408
  def create_index(spec, opts={})
381
- opts.assert_valid_keys(:min, :max, :name, :background, :unique, :dropDups)
409
+ opts[:dropDups] = opts.delete(:drop_dups) if opts[:drop_dups]
382
410
  field_spec = BSON::OrderedHash.new
383
411
  if spec.is_a?(String) || spec.is_a?(Symbol)
384
412
  field_spec[spec.to_s] = 1
@@ -404,10 +432,17 @@ module Mongo
404
432
  :key => field_spec
405
433
  }
406
434
  selector.merge!(opts)
407
- begin
408
- response = insert_documents([selector], Mongo::DB::SYSTEM_INDEX_COLLECTION, false, true)
409
- rescue Mongo::OperationFailure
410
- raise Mongo::OperationFailure, "Failed to create index #{selector.inspect} with the following errors: #{response}"
435
+
436
+ insert_documents([selector], Mongo::DB::SYSTEM_INDEX_COLLECTION, false, false)
437
+ response = @db.get_last_error
438
+
439
+ if response['err']
440
+ if response['code'] == 11000 && selector[:dropDups]
441
+ # NOP. If the user is intentionally dropping dups, we can ignore duplicate key errors.
442
+ else
443
+ raise Mongo::OperationFailure, "Failed to create index #{selector.inspect} with the following error: " +
444
+ "#{response['err']}"
445
+ end
411
446
  end
412
447
  name
413
448
  end
@@ -38,7 +38,8 @@ module Mongo
38
38
  MONGODB_URI_MATCHER = /(([-_.\w\d]+):([-_\w\d]+)@)?([-.\w\d]+)(:([\w\d]+))?(\/([-\d\w]+))?/
39
39
  MONGODB_URI_SPEC = "mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/database]"
40
40
 
41
- attr_reader :logger, :size, :host, :port, :nodes, :auths, :sockets, :checked_out, :primary, :secondaries, :arbiters
41
+ attr_reader :logger, :size, :host, :port, :nodes, :auths, :sockets, :checked_out, :primary, :secondaries, :arbiters,
42
+ :safe
42
43
 
43
44
  # Counter for generating unique request ids.
44
45
  @@current_request_id = 0
@@ -61,9 +62,15 @@ module Mongo
61
62
  # @param [String, Hash] host.
62
63
  # @param [Integer] port specify a port number here if only one host is being specified.
63
64
  #
65
+ # @option options [Boolean, Hash] :safe (false) Set the default safe-mode options
66
+ # propogated to DB objects instantiated off of this Connection. This
67
+ # default can be overridden upon instantiation of any DB by explicity setting a :safe value
68
+ # on initialization.
64
69
  # @option options [Boolean] :slave_ok (false) Must be set to +true+ when connecting
65
70
  # to a single, slave node.
66
71
  # @option options [Logger, #debug] :logger (nil) Logger instance to receive driver operation log.
72
+ # @option options [String] :name (nil) The name of the replica set to connect to. An exception will be
73
+ # raised if unable to connect to a replica set with this name.
67
74
  # @option options [Integer] :pool_size (1) The maximum number of socket connections that can be
68
75
  # opened to the database.
69
76
  # @option options [Float] :timeout (5.0) When all of the connections to the pool are checked out,
@@ -83,6 +90,9 @@ module Mongo
83
90
  #
84
91
  # @see http://www.mongodb.org/display/DOCS/Replica+Pairs+in+Ruby Replica pairs in Ruby
85
92
  #
93
+ # @raise [ReplicaSetConnectionError] This is raised if a replica set name is specified and the
94
+ # driver fails to connect to a replica set with that name.
95
+ #
86
96
  # @core connections
87
97
  def initialize(host=nil, port=nil, options={})
88
98
  @auths = []
@@ -96,6 +106,9 @@ module Mongo
96
106
  # Host and port of current master.
97
107
  @host = @port = nil
98
108
 
109
+ # Replica set name
110
+ @replica_set_name = options[:name]
111
+
99
112
  # Lock for request ids.
100
113
  @id_lock = Mutex.new
101
114
 
@@ -106,6 +119,9 @@ module Mongo
106
119
  # Mutex for synchronizing pool access
107
120
  @connection_mutex = Mutex.new
108
121
 
122
+ # Global safe option. This is false by default.
123
+ @safe = options[:safe] || false
124
+
109
125
  # Create a mutex when a new key, in this case a socket,
110
126
  # is added to the hash.
111
127
  @safe_mutexes = Hash.new { |h, k| h[k] = Mutex.new }
@@ -293,7 +309,7 @@ module Mongo
293
309
  #
294
310
  # @core databases db-instance_method
295
311
  def db(db_name, options={})
296
- DB.new(db_name, self)
312
+ DB.new(db_name, self, options)
297
313
  end
298
314
 
299
315
  # Shortcut for returning a database. Use DB#db to accept options.
@@ -304,7 +320,7 @@ module Mongo
304
320
  #
305
321
  # @core databases []-instance_method
306
322
  def [](db_name)
307
- DB.new(db_name, self)
323
+ DB.new(db_name, self, :safe => @safe)
308
324
  end
309
325
 
310
326
  # Drop a database.
@@ -424,6 +440,7 @@ module Mongo
424
440
  checkin(sock)
425
441
  end
426
442
  if num_received == 1 && (error = docs[0]['err'] || docs[0]['errmsg'])
443
+ close if error == "not master"
427
444
  raise Mongo::OperationFailure, error
428
445
  end
429
446
  [docs, num_received, cursor_id]
@@ -457,7 +474,7 @@ module Mongo
457
474
  # Create a new socket and attempt to connect to master.
458
475
  # If successful, sets host and port to master and returns the socket.
459
476
  #
460
- # If connecting to a replica set, this method will update the
477
+ # If connecting to a replica set, this method will replace the
461
478
  # initially-provided seed list with any nodes known to the set.
462
479
  #
463
480
  # @raise [ConnectionFailure] if unable to connect to any host or port.
@@ -585,8 +602,6 @@ module Mongo
585
602
  #
586
603
  # If a primary node is discovered, we set the the @host and @port and
587
604
  # apply any saved authentication.
588
- #
589
- # TODO: use the 'primary', and 'seconday' fields if we're in a replica set
590
605
  def is_primary?(config)
591
606
  config && (config['ismaster'] == 1 || config['ismaster'] == true) || @slave_ok
592
607
  end
@@ -599,8 +614,9 @@ module Mongo
599
614
 
600
615
  config = self['admin'].command({:ismaster => 1}, :sock => socket)
601
616
 
617
+ check_set_name(config, socket)
602
618
  rescue OperationFailure, SocketError, SystemCallError, IOError => ex
603
- close
619
+ close unless connected?
604
620
  ensure
605
621
  @nodes_tried << node
606
622
  if config
@@ -616,6 +632,21 @@ module Mongo
616
632
  config
617
633
  end
618
634
 
635
+ # Make sure that we're connected to the expected replica set.
636
+ def check_set_name(config, socket)
637
+ if @replica_set_name
638
+ config = self['admin'].command({:replSetGetStatus => 1},
639
+ :sock => socket, :check_response => false)
640
+
641
+ if !Mongo::Support.ok?(config)
642
+ raise ReplicaSetConnectionError, config['errmsg']
643
+ elsif config['set'] != @replica_set_name
644
+ raise ReplicaSetConnectionError,
645
+ "Attempting to connect to replica set '#{config['set']}' but expected '#{@replica_set_name}'"
646
+ end
647
+ end
648
+ end
649
+
619
650
  # Set the specified node as primary, and
620
651
  # apply any saved authentication credentials.
621
652
  def set_primary(node)
@@ -743,7 +774,7 @@ module Mongo
743
774
  response_to = header.get_int
744
775
  op = header.get_int
745
776
  end
746
-
777
+
747
778
  def receive_and_discard_header(sock)
748
779
  bytes_read = receive_and_discard_message_on_socket(16, sock)
749
780
  unless bytes_read == STANDARD_HEADER_SIZE
@@ -46,7 +46,7 @@ module Mongo
46
46
  @order = options[:order]
47
47
  @hint = options[:hint]
48
48
  @snapshot = options[:snapshot]
49
- @timeout = options[:timeout] || true
49
+ @timeout = options.has_key?(:timeout) ? options[:timeout] : true
50
50
  @explain = options[:explain]
51
51
  @socket = options[:socket]
52
52
  @tailable = options[:tailable] || false
@@ -45,8 +45,8 @@ module Mongo
45
45
  # Returns the value of the +strict+ flag.
46
46
  def strict?; @strict; end
47
47
 
48
- # The name of the database.
49
- attr_reader :name
48
+ # The name of the database and the local safe option.
49
+ attr_reader :name, :safe
50
50
 
51
51
  # The Mongo::Connection instance connecting to the MongoDB server.
52
52
  attr_reader :connection
@@ -65,12 +65,19 @@ module Mongo
65
65
  # fields the factory wishes to inject. (NOTE: if the object already has a primary key,
66
66
  # the factory should not inject a new key).
67
67
  #
68
+ # @option options [Boolean, Hash] :safe (false) Set the default safe-mode options
69
+ # propogated to Collection objects instantiated off of this DB. If no
70
+ # value is provided, the default value set on this instance's Connection object will be used. This
71
+ # default can be overridden upon instantiation of any collection by explicity setting a :safe value
72
+ # on initialization
73
+ #
68
74
  # @core databases constructor_details
69
75
  def initialize(db_name, connection, options={})
70
76
  @name = Mongo::Support.validate_db_name(db_name)
71
77
  @connection = connection
72
78
  @strict = options[:strict]
73
79
  @pk_factory = options[:pk]
80
+ @safe = options.has_key?(:safe) ? options[:safe] : @connection.safe
74
81
  end
75
82
 
76
83
  # Authenticate with the given username and password. Note that mongod
@@ -248,20 +255,26 @@ module Mongo
248
255
  oh = BSON::OrderedHash.new
249
256
  oh[:create] = name
250
257
  doc = command(oh.merge(options || {}))
251
- return Collection.new(self, name, @pk_factory) if ok?(doc)
258
+ return Collection.new(self, name, :pk => @pk_factory) if ok?(doc)
252
259
  raise MongoDBError, "Error creating collection: #{doc.inspect}"
253
260
  end
254
261
 
255
262
  # Get a collection by name.
256
263
  #
257
264
  # @param [String] name the collection name.
265
+ # @param [Hash] options any valid options that can me passed to Collection#new.
258
266
  #
259
267
  # @raise [MongoDBError] if collection does not already exist and we're in +strict+ mode.
260
268
  #
261
269
  # @return [Mongo::Collection]
262
- def collection(name)
263
- return Collection.new(self, name, @pk_factory) if !strict? || collection_names.include?(name)
264
- raise Mongo::MongoDBError, "Collection #{name} doesn't exist. Currently in strict mode."
270
+ def collection(name, options={})
271
+ if strict? && !collection_names.include?(name)
272
+ raise Mongo::MongoDBError, "Collection #{name} doesn't exist. Currently in strict mode."
273
+ else
274
+ options[:safe] = options.has_key?(:safe) ? options[:safe] : @safe
275
+ options.merge!(:pk => @pk_factory) unless options[:pk]
276
+ Collection.new(self, name, options)
277
+ end
265
278
  end
266
279
  alias_method :[], :collection
267
280