mongo 1.1.1 → 1.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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