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.



292
293
294
295
296
297
298
# File 'lib/mongo/db.rb', line 292

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 => ex
      close if @socket
    end
    @socket
  }
  raise "error: failed to connect to any given host:port" unless @socket
end

#connected?Boolean

Returns:

  • (Boolean)


300
301
302
# File 'lib/mongo/db.rb', line 300

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.



358
359
360
361
362
363
364
365
# File 'lib/mongo/db.rb', line 358

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)
  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, index_name, fields) ⇒ Object

Create a new index on collection_name named index_name. fields should be an array of field names. Normally called by Collection#create_index.



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

def create_index(collection_name, index_name, fields)
  sel = {:name => index_name, :ns => full_coll_name(collection_name)}
  field_h = {}
  fields.each { |f| field_h[f] = 1 }
  sel[:key] = field_h
  @semaphore.synchronize {
    send_to_db(InsertMessage.new(@name, SYSTEM_INDEX_COLLECTION, sel))
  }
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.



456
457
458
459
460
461
462
463
464
465
466
# File 'lib/mongo/db.rb', line 456

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

#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
244
245
# File 'lib/mongo/db.rb', line 239

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

  coll = collection(name)
  coll.drop_indexes     # Mongo requires that we drop indexes manually
  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.



369
370
371
372
373
374
375
# File 'lib/mongo/db.rb', line 369

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.



253
254
255
256
257
# File 'lib/mongo/db.rb', line 253

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)


265
266
267
# File 'lib/mongo/db.rb', line 265

def error?
  error != nil
end

#full_coll_name(collection_name) ⇒ Object



441
442
443
# File 'lib/mongo/db.rb', line 441

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

#index_information(collection_name) ⇒ Object

Return an array of hashes, one for each index on collection_name. Normally called by Collection#index_information. Each hash contains:

:name

Index name

:keys

Hash whose keys are the names of the fields that make up the key and values are integers.

:ns

Namespace; same as collection_name.



386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
# File 'lib/mongo/db.rb', line 386

def index_information(collection_name)
  sel = {:ns => full_coll_name(collection_name)}
  query(Collection.new(self, SYSTEM_INDEX_COLLECTION), Query.new(sel)).collect { |row|
    h = {:name => row['name']}
    raise "Name of index on return from db was nil. Coll = #{full_coll_name(collection_name)}" unless h[:name]

    h[:keys] = row['key']
    raise "Keys for index on return from db was nil. Coll = #{full_coll_name(collection_name)}" unless h[:keys]

    h[:ns] = row['ns']
    raise "Namespace for index on return from db was nil. Coll = #{full_coll_name(collection_name)}" unless h[:ns]
    h[:ns].sub!(/.*\./, '')
    raise "Error: ns != collection" unless h[:ns] == collection_name

    h
  }
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.



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

def insert_into_db(collection_name, objects)
  @semaphore.synchronize {
    objects.collect { |o|
      o = @pk_factory.create_pk(o) if @pk_factory
      send_to_db(InsertMessage.new(@name, collection_name, o))
      o
    }
  }
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.



279
280
281
282
283
284
285
286
287
288
289
# File 'lib/mongo/db.rb', line 279

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)


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

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)


446
447
448
449
# File 'lib/mongo/db.rb', line 446

def ok?(doc)
  ok = doc['ok']
  ok.kind_of?(Numeric) && ok.to_i == 1
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.



314
315
316
# File 'lib/mongo/db.rb', line 314

def query(collection, query)
  Cursor.new(self, collection, query)
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.



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

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.



335
336
337
338
339
# File 'lib/mongo/db.rb', line 335

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.



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

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

#send_message(msg) ⇒ Object

Send a MsgMessage to the database.



305
306
307
# File 'lib/mongo/db.rb', line 305

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.



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

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

#send_to_db(message) ⇒ Object



430
431
432
433
434
435
436
437
438
439
# File 'lib/mongo/db.rb', line 430

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