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
# 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
  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



223
224
225
# File 'lib/mongo/db.rb', line 223

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.



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

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



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

def close
  @socket.close if @socket
  @socket = nil
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.



230
231
232
233
# File 'lib/mongo/db.rb', line 230

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



176
177
178
179
180
# File 'lib/mongo/db.rb', line 176

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.



185
186
187
188
189
# File 'lib/mongo/db.rb', line 185

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



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

def connect_to_master
  close if @socket
  @host = @port = nil
  @nodes.detect { |hp|
    @host, @port = *hp
    begin
      @socket = TCPSocket.new(@host, @port)

      # 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)


295
296
297
# File 'lib/mongo/db.rb', line 295

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.



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

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.



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

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.



402
403
404
405
406
407
408
409
410
# File 'lib/mongo/db.rb', line 402

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.



450
451
452
453
454
455
456
457
458
459
460
# File 'lib/mongo/db.rb', line 450

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.



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

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.



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

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

#full_coll_name(collection_name) ⇒ Object



435
436
437
# File 'lib/mongo/db.rb', line 435

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.



381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
# File 'lib/mongo/db.rb', line 381

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.



415
416
417
418
419
420
421
422
423
# File 'lib/mongo/db.rb', line 415

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.



169
170
171
172
# File 'lib/mongo/db.rb', line 169

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.



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

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)


269
270
271
272
273
# File 'lib/mongo/db.rb', line 269

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)


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

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.



309
310
311
# File 'lib/mongo/db.rb', line 309

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.



322
323
324
325
326
# File 'lib/mongo/db.rb', line 322

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.



330
331
332
333
334
# File 'lib/mongo/db.rb', line 330

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.



342
343
344
345
346
347
348
# File 'lib/mongo/db.rb', line 342

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.



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

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.



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

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

#send_to_db(message) ⇒ Object



425
426
427
428
429
430
431
432
433
# File 'lib/mongo/db.rb', line 425

def send_to_db(message)
  connect_to_master if !connected? && @auto_reconnect
  begin
    @socket.print(message.buf.to_s)
  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