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


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.



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

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, field_or_spec) ⇒ 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.



419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
# File 'lib/mongo/db.rb', line 419

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



477
478
479
480
481
482
483
484
485
486
487
# File 'lib/mongo/db.rb', line 477

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.



379
380
381
382
383
384
385
# File 'lib/mongo/db.rb', line 379

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



462
463
464
# File 'lib/mongo/db.rb', line 462

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.



396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
# File 'lib/mongo/db.rb', line 396

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.



441
442
443
444
445
446
447
448
449
# File 'lib/mongo/db.rb', line 441

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)


467
468
469
470
# File 'lib/mongo/db.rb', line 467

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.



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

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

#receive_full(length) ⇒ Object



304
305
306
307
308
309
310
311
312
# File 'lib/mongo/db.rb', line 304

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.



337
338
339
340
341
# File 'lib/mongo/db.rb', line 337

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.



345
346
347
348
349
# File 'lib/mongo/db.rb', line 345

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.



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

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.



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

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.



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

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

#send_to_db(message) ⇒ Object



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

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