Class: XGen::Mongo::Driver::DB

Inherits:
Object
  • Object
show all
Defined in:
lib/mongo/db.rb

Overview

A Mongo database.

Constant Summary collapse

SYSTEM_NAMESPACE_COLLECTION =
"system.namespaces"
SYSTEM_INDEX_COLLECTION =
"system.indexes"
SYSTEM_PROFILE_COLLECTION =
"system.profile"
SYSTEM_USER_COLLECTION =
"system.users"
SYSTEM_COMMAND_COLLECTION =
"$cmd"

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(db_name, nodes, options = {}) ⇒ DB

Instances of DB are normally obtained by calling Mongo#db.

db_name

The database name

nodes

An array of [host, port] pairs. See Mongo#new, which offers a more flexible way of defining nodes.

options

A hash of options.

Options:

:strict

If true, collections must exist to be accessed and must not exist to be created. See #collection and #create_collection.

:pk

A primary key factory object that must respond to :create_pk, which should take a hash and return a hash which merges the original hash with any primary key fields the factory wishes to inject. (NOTE: if the object already has a primary key, the factory should not inject a new key; this means that the object is being used in a repsert but it already exists.) The idea here is that when ever a record is inserted, the :pk object’s create_pk method will be called and the new hash returned will be inserted.

:slave_ok

Only used if nodes contains only one host/port. If false, when connecting to that host/port we check to see if the server is the master. If it is not, an error is thrown.

:auto_reconnect

If the connection gets closed (for example, we have a server pair and saw the “not master” error, which closes the connection), then automatically try to reconnect to the master or to the single server we have been given. Defaults to false.

When a DB object first connects to a pair, it will find the master instance and connect to that one. On socket error or if we recieve a “not master” error, we again find the master of the pair.



116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/mongo/db.rb', line 116

def initialize(db_name, nodes, options={})
  raise "Invalid DB name \"#{db_name}\" (must be non-nil, non-zero-length, and can not contain \".\")" if !db_name || (db_name && db_name.length > 0 && db_name.include?("."))
  @name, @nodes = db_name, nodes
  @strict = options[:strict]
  @pk_factory = options[:pk]
  @slave_ok = options[:slave_ok] && @nodes.length == 1 # only OK if one node
  @auto_reconnect = options[:auto_reconnect]
  @semaphore = Object.new
  @semaphore.extend Mutex_m
  @socket = nil
  connect_to_master
end

Instance Attribute Details

#hostObject (readonly)

Host to which we are currently connected.



54
55
56
# File 'lib/mongo/db.rb', line 54

def host
  @host
end

#nameObject (readonly)

The name of the database.



51
52
53
# File 'lib/mongo/db.rb', line 51

def name
  @name
end

#nodesObject (readonly)

An array of [host, port] pairs.



59
60
61
# File 'lib/mongo/db.rb', line 59

def nodes
  @nodes
end

#pk_factoryObject

A primary key factory object (or nil). See the README.doc file or DB#new for details.



69
70
71
# File 'lib/mongo/db.rb', line 69

def pk_factory
  @pk_factory
end

#portObject (readonly)

Port to which we are currently connected.



56
57
58
# File 'lib/mongo/db.rb', line 56

def port
  @port
end

#socketObject (readonly)

The database’s socket. For internal (and Cursor) use only.



62
63
64
# File 'lib/mongo/db.rb', line 62

def socket
  @socket
end

#strict=(value) ⇒ Object (writeonly)

Strict mode enforces collection existence checks. When true, asking for a collection that does not exist or trying to create a collection that already exists raises an error.

Strict mode is off (false) by default. Its value can be changed at any time.



45
46
47
# File 'lib/mongo/db.rb', line 45

def strict=(value)
  @strict = value
end

Instance Method Details

#adminObject



225
226
227
# File 'lib/mongo/db.rb', line 225

def admin
  Admin.new(self)
end

#authenticate(username, password) ⇒ Object

Returns true if username has password in SYSTEM_USER_COLLECTION. name is username, password is plaintext password.



157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/mongo/db.rb', line 157

def authenticate(username, password)
  doc = db_command(:getnonce => 1)
  raise "error retrieving nonce: #{doc}" unless ok?(doc)
  nonce = doc['nonce']

  auth = OrderedHash.new
  auth['authenticate'] = 1
  auth['user'] = username
  auth['nonce'] = nonce
  auth['key'] = Digest::MD5.hexdigest("#{nonce}#{username}#{hash_password(username, password)}")
  ok?(db_command(auth))
end

#auto_reconnect?Boolean

Returns:

  • (Boolean)


65
# File 'lib/mongo/db.rb', line 65

def auto_reconnect?; @auto_reconnect; end

#closeObject

Close the connection to the database.



311
312
313
314
315
316
317
# File 'lib/mongo/db.rb', line 311

def close
  if @socket
    s = @socket
    @socket = nil
    s.close
  end
end

#collection(name) ⇒ Object

Return a collection. If strict is false, will return existing or new collection. If strict is true, will raise an error if collection name does not already exists.



232
233
234
235
# File 'lib/mongo/db.rb', line 232

def collection(name)
  return Collection.new(self, name) if !strict? || collection_names.include?(full_coll_name(name))
  raise "Collection #{name} doesn't exist. Currently in strict mode."
end

#collection_namesObject

Returns an array of collection names. Each name is of the form “database_name.collection_name”.



178
179
180
181
182
# File 'lib/mongo/db.rb', line 178

def collection_names
  names = collections_info.collect { |doc| doc['name'] || '' }
  names.delete('')
  names
end

#collections_info(coll_name = nil) ⇒ Object

Returns a cursor over query result hashes. Each hash contains a ‘name’ string and optionally an ‘options’ hash. If coll_name is specified, an array of length 1 is returned.



187
188
189
190
191
# File 'lib/mongo/db.rb', line 187

def collections_info(coll_name=nil)
  selector = {}
  selector[:name] = full_coll_name(coll_name) if coll_name
  query(Collection.new(self, SYSTEM_NAMESPACE_COLLECTION), Query.new(selector))
end

#connect_to_masterObject



129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/mongo/db.rb', line 129

def connect_to_master
  close if @socket
  @host = @port = nil
  @nodes.detect { |hp|
    @host, @port = *hp
    begin
      @socket = TCPSocket.new(@host, @port)
      @socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)

      # Check for master. Can't call master? because it uses mutex,
      # which may already be in use during this call.
      semaphore_is_locked = @semaphore.locked?
      @semaphore.unlock if semaphore_is_locked
      is_master = master?
      @semaphore.lock if semaphore_is_locked

      break if @slave_ok || is_master
    rescue SocketError, SystemCallError, IOError => ex
      close if @socket
    end
    @socket
  }
  raise "error: failed to connect to any given host:port" unless @socket
end

#connected?Boolean

Returns:

  • (Boolean)


319
320
321
# File 'lib/mongo/db.rb', line 319

def connected?
  @socket != nil
end

#count(collection_name, selector = {}) ⇒ Object

Return the number of records in collection_name that match selector. If selector is nil or an empty hash, returns the count of all records. Normally called by Collection#count.



387
388
389
390
391
392
393
394
395
# File 'lib/mongo/db.rb', line 387

def count(collection_name, selector={})
  oh = OrderedHash.new
  oh[:count] = collection_name
  oh[:query] = selector || {}
  doc = db_command(oh)
  return doc['n'].to_i if ok?(doc)
  return 0 if doc['errmsg'] == "ns missing"
  raise "Error with count command: #{doc.inspect}"
end

#create_collection(name, options = {}) ⇒ Object

Create a collection. If strict is false, will return existing or new collection. If strict is true, will raise an error if collection name already exists.

Options is an optional hash:

:capped

Boolean. If not specified, capped is false.

:size

If capped is true, specifies the maximum number of bytes. If false, specifies the initial extent of the collection.

:max

Max number of records in a capped collection. Optional.



206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
# File 'lib/mongo/db.rb', line 206

def create_collection(name, options={})
  # First check existence
  if collection_names.include?(full_coll_name(name))
    if strict?
      raise "Collection #{name} already exists. Currently in strict mode."
    else
      return Collection.new(self, name)
    end
  end

  # Create new collection
  oh = OrderedHash.new
  oh[:create] = name
  doc = db_command(oh.merge(options || {}))
  ok = doc['ok']
  return Collection.new(self, name) if ok.kind_of?(Numeric) && (ok.to_i == 1 || ok.to_i == 0)
  raise "Error creating collection: #{doc.inspect}"
end

#create_index(collection_name, field_or_spec, unique = false) ⇒ Object

Create a new index on collection_name. field_or_spec should be either a single field name or a Array of [field name, direction] pairs. Directions should be specified as XGen::Mongo::ASCENDING or XGen::Mongo::DESCENDING. Normally called by Collection#create_index. If unique is true the index will enforce a uniqueness constraint.



449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
# File 'lib/mongo/db.rb', line 449

def create_index(collection_name, field_or_spec, unique=false)
  field_h = OrderedHash.new
  if field_or_spec.is_a?(String) || field_or_spec.is_a?(Symbol)
    field_h[field_or_spec.to_s] = 1
  else
    field_or_spec.each { |f| field_h[f[0].to_s] = f[1] }
  end
  name = gen_index_name(field_h)
  sel = {
    :name => name,
    :ns => full_coll_name(collection_name),
    :key => field_h,
    :unique => unique
  }
  @semaphore.synchronize {
    send_to_db(InsertMessage.new(@name, SYSTEM_INDEX_COLLECTION, false, sel))
  }
  name
end

#db_command(selector) ⇒ Object

DB commands need to be ordered, so selector must be an OrderedHash (or a Hash with only one element). What DB commands really need is that the “command” key be first.

Do not call this. Intended for driver use only.



514
515
516
517
518
519
520
521
522
523
524
# File 'lib/mongo/db.rb', line 514

def db_command(selector)
  if !selector.kind_of?(OrderedHash)
    if !selector.kind_of?(Hash) || selector.keys.length > 1
      raise "db_command must be given an OrderedHash when there is more than one key"
    end
  end

  q = Query.new(selector)
  q.number_to_return = 1
  query(Collection.new(self, SYSTEM_COMMAND_COLLECTION), q).next_object
end

#dereference(dbref) ⇒ Object

Dereference a DBRef, getting the document it points to.



398
399
400
# File 'lib/mongo/db.rb', line 398

def dereference(dbref)
  collection(dbref.namespace).find_first("_id" => dbref.object_id)
end

#drop_collection(name) ⇒ Object

Drop collection name. Returns true on success or if the collection does not exist, false otherwise.



239
240
241
242
243
# File 'lib/mongo/db.rb', line 239

def drop_collection(name)
  return true unless collection_names.include?(full_coll_name(name))

  ok?(db_command(:drop => name))
end

#drop_index(collection_name, name) ⇒ Object

Drop index name from collection_name. Normally called from Collection#drop_index or Collection#drop_indexes.



421
422
423
424
425
426
427
# File 'lib/mongo/db.rb', line 421

def drop_index(collection_name, name)
  oh = OrderedHash.new
  oh[:deleteIndexes] = collection_name
  oh[:index] = name
  doc = db_command(oh)
  raise "Error with drop_index command: #{doc.inspect}" unless ok?(doc)
end

#errorObject

Returns the error message from the most recently executed database operation for this connection, or nil if there was no error.

Note: as of this writing, errors are only detected on the db server for certain kinds of operations (writes). The plan is to change this so that all operations will set the error if needed.



251
252
253
254
255
# File 'lib/mongo/db.rb', line 251

def error
  doc = db_command(:getlasterror => 1)
  raise "error retrieving last error: #{doc}" unless ok?(doc)
  doc['err']
end

#error?Boolean

Returns true if an error was caused by the most recently executed database operation.

Note: as of this writing, errors are only detected on the db server for certain kinds of operations (writes). The plan is to change this so that all operations will set the error if needed.

Returns:

  • (Boolean)


263
264
265
# File 'lib/mongo/db.rb', line 263

def error?
  error != nil
end

#eval(code, *args) ⇒ Object

Evaluate a JavaScript expression on MongoDB. code should be a string or Code instance containing a JavaScript expression. Additional arguments will be passed to that expression when it is run on the server.



406
407
408
409
410
411
412
413
414
415
416
417
# File 'lib/mongo/db.rb', line 406

def eval(code, *args)
  if not code.is_a? Code
    code = Code.new(code)
  end

  oh = OrderedHash.new
  oh[:$eval] = code
  oh[:args] = args
  doc = db_command(oh)
  return doc['retval'] if ok?(doc)
  raise "Error with eval command: #{doc.inspect}"
end

#full_coll_name(collection_name) ⇒ Object



499
500
501
# File 'lib/mongo/db.rb', line 499

def full_coll_name(collection_name)
  "#{@name}.#{collection_name}"
end

#index_information(collection_name) ⇒ Object

Get information on the indexes for the collection collection_name. Normally called by Collection#index_information. Returns a hash where the keys are index names (as returned by Collection#create_index and the values are lists of [key, direction] pairs specifying the index (as passed to Collection#create_index).



434
435
436
437
438
439
440
441
# File 'lib/mongo/db.rb', line 434

def index_information(collection_name)
  sel = {:ns => full_coll_name(collection_name)}
  info = {}
  query(Collection.new(self, SYSTEM_INDEX_COLLECTION), Query.new(sel)).each { |index|
    info[index['name']] = index['key'].to_a
  }
  info
end

#insert_into_db(collection_name, objects) ⇒ Object

Insert objects into collection_name. Normally called by Collection#insert. Returns a new array containing objects, possibly modified by @pk_factory.



472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
# File 'lib/mongo/db.rb', line 472

def insert_into_db(collection_name, objects)
  @semaphore.synchronize {
    if @pk_factory
      objects.collect! { |o|
        @pk_factory.create_pk(o)
      }
    else
      objects = objects.collect do |o|
        o[:_id] || o['_id'] ? o : o.merge(:_id => ObjectID.new)
      end
    end
    send_to_db(InsertMessage.new(@name, collection_name, true, *objects))
    objects.collect { |o| o[:_id] || o['_id'] }
  }
end

#logoutObject

Deauthorizes use for this database for this connection.



171
172
173
174
# File 'lib/mongo/db.rb', line 171

def logout
  doc = db_command(:logout => 1)
  raise "error logging out: #{doc.inspect}" unless ok?(doc)
end

#masterObject

Returns a string of the form “host:port” that points to the master database. Works even if this is the master database.



298
299
300
301
302
303
304
305
306
307
308
# File 'lib/mongo/db.rb', line 298

def master
  doc = db_command(:ismaster => 1)
  is_master = doc['ismaster']
  raise "Error retrieving master database: #{doc.inspect}" unless ok?(doc) && is_master.kind_of?(Numeric)
  case is_master.to_i
  when 1
    "#@host:#@port"
  else
    doc['remote']
  end
end

#master?Boolean

Returns true if this database is a master (or is not paired with any other database), false if it is a slave.

Returns:

  • (Boolean)


290
291
292
293
294
# File 'lib/mongo/db.rb', line 290

def master?
  doc = db_command(:ismaster => 1)
  is_master = doc['ismaster']
  ok?(doc) && is_master.kind_of?(Numeric) && is_master.to_i == 1
end

#ok?(doc) ⇒ Boolean

Return true if doc contains an ‘ok’ field with the value 1.

Returns:

  • (Boolean)


504
505
506
507
# File 'lib/mongo/db.rb', line 504

def ok?(doc)
  ok = doc['ok']
  ok.kind_of?(Numeric) && ok.to_i == 1
end

#previous_errorObject

Get the most recent error to have occured on this database

Only returns errors that have occured since the last call to DB#reset_error_history - returns nil if there is no such error.



271
272
273
274
275
276
277
278
# File 'lib/mongo/db.rb', line 271

def previous_error
  error = db_command(:getpreverror => 1)
  if error["err"]
    error
  else
    nil
  end
end

#query(collection, query) ⇒ Object

Returns a Cursor over the query results.

Note that the query gets sent lazily; the cursor calls #send_query_message when needed. If the caller never requests an object from the cursor, the query never gets sent.



343
344
345
# File 'lib/mongo/db.rb', line 343

def query(collection, query)
  Cursor.new(self, collection, query)
end

#receive_full(length) ⇒ Object



323
324
325
326
327
328
329
330
331
# File 'lib/mongo/db.rb', line 323

def receive_full(length)
  message = ""
  while message.length < length do
    chunk = @socket.recv(length - message.length)
    raise "connection closed" unless chunk.length > 0
    message += chunk
  end
  message
end

#remove_from_db(collection_name, selector) ⇒ Object

Remove the records that match selector from collection_name. Normally called by Collection#remove or Collection#clear.



356
357
358
359
360
# File 'lib/mongo/db.rb', line 356

def remove_from_db(collection_name, selector)
  @semaphore.synchronize {
    send_to_db(RemoveMessage.new(@name, collection_name, selector))
  }
end

#replace_in_db(collection_name, selector, obj) ⇒ Object Also known as: modify_in_db

Update records in collection_name that match selector by applying obj as an update. Normally called by Collection#replace.



364
365
366
367
368
# File 'lib/mongo/db.rb', line 364

def replace_in_db(collection_name, selector, obj)
  @semaphore.synchronize {
    send_to_db(UpdateMessage.new(@name, collection_name, selector, obj, false))
  }
end

#repsert_in_db(collection_name, selector, obj) ⇒ Object

Update records in collection_name that match selector by applying obj as an update. If no match, inserts (???). Normally called by Collection#repsert.



376
377
378
379
380
381
382
# File 'lib/mongo/db.rb', line 376

def repsert_in_db(collection_name, selector, obj)
  @semaphore.synchronize {
    obj = @pk_factory.create_pk(obj) if @pk_factory
    send_to_db(UpdateMessage.new(@name, collection_name, selector, obj, true))
    obj
  }
end

#reset_error_historyObject

Reset the error history of this database

Calls to DB#previous_error will only return errors that have occurred since the most recent call to this method.



284
285
286
# File 'lib/mongo/db.rb', line 284

def reset_error_history
  db_command(:reseterror => 1)
end

#send_message(msg) ⇒ Object

Send a MsgMessage to the database.



334
335
336
# File 'lib/mongo/db.rb', line 334

def send_message(msg)
  send_to_db(MsgMessage.new(msg))
end

#send_query_message(query_message) ⇒ Object

Used by a Cursor to lazily send the query to the database.



348
349
350
351
352
# File 'lib/mongo/db.rb', line 348

def send_query_message(query_message)
  @semaphore.synchronize {
    send_to_db(query_message)
  }
end

#send_to_db(message) ⇒ Object



488
489
490
491
492
493
494
495
496
497
# File 'lib/mongo/db.rb', line 488

def send_to_db(message)
  connect_to_master if !connected? && @auto_reconnect
  begin
    @socket.print(message.buf.to_s)
    @socket.flush
  rescue => ex
    close
    raise ex
  end
end

#slave_ok?Boolean

Returns:

  • (Boolean)


64
# File 'lib/mongo/db.rb', line 64

def slave_ok?; @slave_ok; end

#strict?Boolean

Returns the value of the strict flag.

Returns:

  • (Boolean)


48
# File 'lib/mongo/db.rb', line 48

def strict?; @strict; end