Class: Mongo::Collection

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

Overview

A named collection of documents in a database.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(db, name, pk_factory = nil) ⇒ Collection

Initialize a collection object.

Parameters:

  • db (DB)

    a MongoDB database instance.

  • name (String, Symbol)

    the name of the collection.

Raises:

  • (InvalidName)

    if collection name is empty, contains ‘$’, or starts or ends with ‘.’

  • (TypeError)

    if collection name is not a string or symbol



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/mongo/collection.rb', line 38

def initialize(db, name, pk_factory=nil)
  case name
  when Symbol, String
  else
    raise TypeError, "new_name must be a string or symbol"
  end

  name = name.to_s

  if name.empty? or name.include? ".."
    raise InvalidName, "collection names cannot be empty"
  end
  if name.include? "$"
    raise InvalidName, "collection names must not contain '$'" unless name =~ /((^\$cmd)|(oplog\.\$main))/
  end
  if name.match(/^\./) or name.match(/\.$/)
    raise InvalidName, "collection names must not start or end with '.'"
  end

  @db, @name  = db, name
  @connection = @db.connection
  @pk_factory = pk_factory || ObjectID
  @hint = nil
end

Instance Attribute Details

#dbObject (readonly)

Returns the value of attribute db.



22
23
24
# File 'lib/mongo/collection.rb', line 22

def db
  @db
end

#hintObject

Returns the value of attribute hint.



22
23
24
# File 'lib/mongo/collection.rb', line 22

def hint
  @hint
end

#nameObject (readonly)

Returns the value of attribute name.



22
23
24
# File 'lib/mongo/collection.rb', line 22

def name
  @name
end

#pk_factoryObject (readonly)

Returns the value of attribute pk_factory.



22
23
24
# File 'lib/mongo/collection.rb', line 22

def pk_factory
  @pk_factory
end

Instance Method Details

#[](name) ⇒ Collection

Return a sub-collection of this collection by name. If ‘users’ is a collection, then ‘users.comments’ is a sub-collection of users.

Parameters:

  • name (String)

    the collection to return

Returns:

Raises:

  • (InvalidName)

    if passed an invalid collection name



74
75
76
77
78
# File 'lib/mongo/collection.rb', line 74

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

#countInteger Also known as: size

Get the number of documents in this collection.

Returns:

  • (Integer)


561
562
563
# File 'lib/mongo/collection.rb', line 561

def count
  find().count()
end

#create_index(field_or_spec, unique = false) ⇒ String

Create a new index.

Parameters:

  • field_or_spec (String, Array)

    should be either a single field name or an array of

    field name, direction

    pairs. Directions should be specified as Mongo::ASCENDING or Mongo::DESCENDING.

  • unique (Boolean) (defaults to: false)

    if true, this index will enforce a uniqueness constraint.

Returns:

  • (String)

    the name of the index created.



328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
# File 'lib/mongo/collection.rb', line 328

def create_index(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 = generate_index_names(field_h)
  sel  = {
    :name   => name,
    :ns     => "#{@db.name}.#{@name}",
    :key    => field_h,
    :unique => unique }
  begin
    insert_documents([sel], Mongo::DB::SYSTEM_INDEX_COLLECTION, false, true)
  rescue Mongo::OperationFailure
    raise Mongo::OperationFailure, "Failed to create index #{sel.inspect}."
  end
  name
end

#distinct(key, query = nil) ⇒ Array

Return a list of distinct values for key across all documents in the collection. The key may use dot notation to reach into an embedded object.

Examples:

Saving zip codes and ages and returning distinct results.

@collection.save({:zip => 10010, :name => {:age => 27}})
@collection.save({:zip => 94108, :name => {:age => 24}})
@collection.save({:zip => 10010, :name => {:age => 27}})
@collection.save({:zip => 99701, :name => {:age => 24}})
@collection.save({:zip => 94108, :name => {:age => 27}})

@collection.distinct(:zip)
  [10010, 94108, 99701]
@collection.distinct("name.age")
  [27, 24]

# You may also pass a document selector as the second parameter
# to limit the documents over which distinct is run:
@collection.distinct("name.age", {"name.age" => {"$gt" => 24}})
  [27]

Parameters:

  • key (String, Symbol, OrderedHash)

    or hash to group by.

  • query (Hash) (defaults to: nil)

    a selector for limiting the result set over which to group.

Returns:

  • (Array)

    an array of distinct values.

Raises:



501
502
503
504
505
506
507
508
509
# File 'lib/mongo/collection.rb', line 501

def distinct(key, query=nil)
  raise MongoArgumentError unless [String, Symbol].include?(key.class)
  command = OrderedHash.new
  command[:distinct] = @name
  command[:key]      = key.to_s
  command[:query]    = query

  @db.command(command)["values"]
end

#dropObject

Drop the entire collection. USE WITH CAUTION.



369
370
371
# File 'lib/mongo/collection.rb', line 369

def drop
  @db.drop_collection(@name)
end

#drop_index(name) ⇒ Object

Drop a specified index.

Parameters:

  • name (String)


354
355
356
# File 'lib/mongo/collection.rb', line 354

def drop_index(name)
  @db.drop_index(@name, name)
end

#drop_indexesObject

Drop all indexes.



361
362
363
364
365
366
# File 'lib/mongo/collection.rb', line 361

def drop_indexes

  # Note: calling drop_indexes with no args will drop them all.
  @db.drop_index(@name, '*')

end

#find(selector = {}, opts = {}) ⇒ Object

Query the database.

The selector argument is a prototype document that all results must match. For example:

collection.find({"hello" => "world"})

only matches documents that have a key “hello” with value “world”. Matches can have other keys *in addition* to “hello”.

If given an optional block find will yield a Cursor to that block, close the cursor, and then return nil. This guarantees that partially evaluated cursors will be closed. If given no block find returns a cursor.

Parameters:

  • selector (Hash) (defaults to: {})

    a document specifying elements which must be present for a document to be included in the result set.

  • opts (Hash) (defaults to: {})

    a customizable set of options

Options Hash (opts):

  • :fields (Array)

    field names that should be returned in the result set (“_id” will always be included). By limiting results to a certain subset of fields, you can cut down on network traffic and decoding time.

  • :skip (Integer)

    number of documents to skip from the beginning of the result set

  • :limit (Integer)

    maximum number of documents to return

  • :sort (Array)

    an array of [key, direction] pairs to sort by. Direction should be specified as Mongo::ASCENDING (or :ascending / :asc) or Mongo::DESCENDING (or :descending / :desc)

  • :hint (String, Array, OrderedHash)

    hint for query optimizer, usually not necessary if using MongoDB > 1.1

  • :snapshot (Boolean) — default: 'false'

    if true, snapshot mode will be used for this query. Snapshot mode assures no duplicates are returned, or objects missed, which were preset at both the start and end of the query’s execution. For details see www.mongodb.org/display/DOCS/How+to+do+Snapshotting+in+the+Mongo+Database

  • :timeout (Boolean) — default: 'true'

    when true, the returned cursor will be subject to the normal cursor timeout behavior of the mongod process. When false, the returned cursor will never timeout. Note that disabling timeout will only work when #find is invoked with a block. This is to prevent any inadvertant failure to close the cursor, as the cursor is explicitly closed when block code finishes.

Raises:

  • (ArgumentError)

    if timeout is set to false and find is not invoked in a block

  • (RuntimeError)

    if given unknown options



133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/mongo/collection.rb', line 133

def find(selector={}, opts={})
  fields = opts.delete(:fields)
  fields = ["_id"] if fields && fields.empty?
  skip   = opts.delete(:skip) || skip || 0
  limit  = opts.delete(:limit) || 0
  sort   = opts.delete(:sort)
  hint   = opts.delete(:hint)
  snapshot = opts.delete(:snapshot)
  if opts[:timeout] == false && !block_given?
    raise ArgumentError, "Timeout can be set to false only when #find is invoked with a block."
  end
  timeout = block_given? ? (opts.delete(:timeout) || true) : true
  if hint
    hint = normalize_hint_fields(hint)
  else
    hint = @hint        # assumed to be normalized already
  end
  raise RuntimeError, "Unknown options [#{opts.inspect}]" unless opts.empty?

  cursor = Cursor.new(self, :selector => selector, :fields => fields, :skip => skip, :limit => limit,
                      :order => sort, :hint => hint, :snapshot => snapshot, :timeout => timeout)
  if block_given?
    yield cursor
    cursor.close()
    nil
  else
    cursor
  end
end

#find_one(spec_or_object_id = nil, opts = {}) ⇒ OrderedHash, Nil

Return a single object from the database.

Parameters:

  • spec_or_object_id (Hash, ObjectID, Nil) (defaults to: nil)

    a hash specifying elements which must be present for a document to be included in the result set or an instance of ObjectID to be used as the value for an _id query. If nil, an empty selector, {}, will be used.

  • opts (Hash) (defaults to: {})

    a customizable set of options

Options Hash (opts):

  • any (Hash)

    valid options that can be send to Collection#find

Returns:

  • (OrderedHash, Nil)

    a single document or nil if no result is found.

Raises:

  • (TypeError)

    if the argument is of an improper type.



178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/mongo/collection.rb', line 178

def find_one(spec_or_object_id=nil, opts={})
  spec = case spec_or_object_id
         when nil
           {}
         when ObjectID
           {:_id => spec_or_object_id}
         when Hash
           spec_or_object_id
         else
           raise TypeError, "spec_or_object_id must be an instance of ObjectID or Hash, or nil"
         end
  find(spec, opts.merge(:limit => -1)).next_document
end

#group(key, condition, initial, reduce, finalize = nil, deprecated = nil) ⇒ Array

Perform a group aggregation.

Parameters:

  • :key (Array, String, Code, Nil)

    either 1) an array of fields to group by, 2) a javascript function to generate the key object, or 3) nil.

  • condition (Hash)

    an optional document specifying a query to limit the documents over which group is run.

  • initial (Hash)

    initial value of the aggregation counter object

  • reduce (String, Code)

    aggregation function, in JavaScript

  • finalize (String, Code) (defaults to: nil)

    optional. a JavaScript function that receives and modifies

    each of the resultant grouped objects. Available only when group is run with command set to true.

  • deprecated (Nil) (defaults to: nil)

    this param in a placeholder for a deprecated param. It will be removed in the next release.

Returns:

  • (Array)

    the grouped items.



426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
# File 'lib/mongo/collection.rb', line 426

def group(key, condition, initial, reduce, finalize=nil, deprecated=nil)

  # Warn of changed API post eval deprecation.
  if finalize == true || finalize == false || deprecated
    warn "The API for Collection#group has changed. 'Finalize' is now the fifth parameter, " +
      "since it's no longer necessary to specify whether #group is run as a command. " +
      "See http://api.mongodb.org/ruby/current/Mongo/Collection.html#group-instance_method for details."
  end

  reduce = Code.new(reduce) unless reduce.is_a?(Code)

  group_command = {
    "group" => {
      "ns"      => @name,
      "$reduce" => reduce,
      "cond"    => condition,
      "initial" => initial
    }
  }

  unless key.nil?
    if key.is_a? Array
      key_type = "key"
      key_value = {}
      key.each { |k| key_value[k] = 1 }
    else
      key_type  = "$keyf"
      key_value = key.is_a?(Code) ? key : Code.new(key)
    end

    group_command["group"][key_type] = key_value
  end

  # only add finalize if specified
  # check to see if users have sent the finalizer as the last argument.
  finalize = deprecated if deprecated.is_a?(String) || deprecated.is_a?(Code)
  finalize = Code.new(finalize) if finalize.is_a?(String)
  if finalize.is_a?(Code)
    group_command['group']['finalize'] = finalize
  end

  result = @db.command group_command

  if result["ok"] == 1
    result["retval"]
  else
    raise OperationFailure, "group command failed: #{result['errmsg']}"
  end
end

#index_informationHash

Get information on the indexes for this collection.

Returns:

  • (Hash)

    a hash where the keys are index names.



546
547
548
# File 'lib/mongo/collection.rb', line 546

def index_information
  @db.index_information(@name)
end

#insert(doc_or_docs, options = {}) ⇒ ObjectID, Array Also known as: <<

Insert one or more documents into the collection.

Parameters:

  • doc_or_docs (Hash, Array)

    a document (as a hash) or array of documents to be inserted.

  • opts (Hash)

    a customizable set of options

Returns:

  • (ObjectID, Array)

    the _id of the inserted document or a list of _ids of all inserted documents. Note: the object may have been modified by the database’s PK factory, if it has one.



230
231
232
233
234
235
# File 'lib/mongo/collection.rb', line 230

def insert(doc_or_docs, options={})
  doc_or_docs = [doc_or_docs] unless doc_or_docs.is_a?(Array)
  doc_or_docs.collect! { |doc| @pk_factory.create_pk(doc) }
  result = insert_documents(doc_or_docs, @name, true, options[:safe])
  result.size > 1 ? result : result.first
end

#map_reduce(map, reduce, opts = {}) ⇒ Collection Also known as: mapreduce

Perform a map/reduce operation on the current collection.

Parameters:

  • map (String, Code)

    a map function, written in JavaScript.

  • reduce (String, Code)

    a reduce function, written in JavaScript.

  • opts (Hash) (defaults to: {})

    a customizable set of options

Options Hash (opts):

  • :query (Hash) — default: {}

    a query selector document, like what’s passed to #find, to limit the operation to a subset of the collection.

  • :sort (Array) — default: []

    an array of [key, direction] pairs to sort by. Direction should be specified as Mongo::ASCENDING (or :ascending / :asc) or Mongo::DESCENDING (or :descending / :desc)

  • :limit (Integer) — default: nil

    if passing a query, number of objects to return from the collection.

  • :finalize (String, Code) — default: nil

    a javascript function to apply to the result set after the map/reduce operation has finished.

  • :out (String) — default: nil

    the name of the output collection. If specified, the collection will not be treated as temporary.

  • :keeptemp (Boolean) — default: false

    if true, the generated collection will be persisted. default is false.

  • :verbose (Boolean) — default: false

    if true, provides statistics on job execution time.

Returns:

  • (Collection)

    a collection containing the results of the operation.

See Also:



394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
# File 'lib/mongo/collection.rb', line 394

def map_reduce(map, reduce, opts={})
  map    = Code.new(map) unless map.is_a?(Code)
  reduce = Code.new(reduce) unless reduce.is_a?(Code)

  hash = OrderedHash.new
  hash['mapreduce'] = self.name
  hash['map'] = map
  hash['reduce'] = reduce
  hash.merge! opts

  result = @db.command(hash)
  unless result["ok"] == 1
    raise Mongo::OperationFailure, "map-reduce failed: #{result['errmsg']}"
  end
  @db[result["result"]]
end

#optionsHash

Return a hash containing options that apply to this collection. For all possible keys and values, see DB#create_collection.

Returns:

  • (Hash)

    options that apply to this collection.



554
555
556
# File 'lib/mongo/collection.rb', line 554

def options
  @db.collections_info(@name).next_document['options']
end

#remove(selector = {}, opts = {}) ⇒ True

Remove all documents from this collection.

Examples:

remove all documents from the ‘users’ collection:

users.remove
users.remove({})

remove only documents that have expired:

users.remove({:expire => {"$lte" => Time.now}})

Parameters:

  • selector (Hash) (defaults to: {})

    If specified, only matching documents will be removed.

  • opts (Hash) (defaults to: {})

    a customizable set of options

Options Hash (opts):

  • :safe (Boolean)
    false

    run the operation in safe mode, which

    will call :getlasterror on the database and report any assertions.

Returns:

  • (True)

Raises:



259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
# File 'lib/mongo/collection.rb', line 259

def remove(selector={}, opts={})
  # Initial byte is 0.
  message = ByteBuffer.new([0, 0, 0, 0])
  BSON_RUBY.serialize_cstr(message, "#{@db.name}.#{@name}")
  message.put_int(0)
  message.put_array(BSON.serialize(selector, false, true).to_a)

  if opts[:safe]
    @connection.send_message_with_safe_check(Mongo::Constants::OP_DELETE, message, @db.name,
      "db.#{@db.name}.remove(#{selector.inspect})")
    # the return value of send_message_with_safe_check isn't actually meaningful --
    # only the fact that it didn't raise an error is -- so just return true
    true
  else
    @connection.send_message(Mongo::Constants::OP_DELETE, message,
      "db.#{@db.name}.remove(#{selector.inspect})")
  end
end

#rename(new_name) ⇒ Object

Rename this collection.

Note: If operating in auth mode, the client must be authorized as an admin to perform this operation.

Parameters:

  • new_name (String)

    the new name for this collection

Raises:

  • (InvalidName)

    if new_name is an invalid collection name.



519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
# File 'lib/mongo/collection.rb', line 519

def rename(new_name)
  case new_name
  when Symbol, String
  else
    raise TypeError, "new_name must be a string or symbol"
  end

  new_name = new_name.to_s

  if new_name.empty? or new_name.include? ".."
    raise InvalidName, "collection names cannot be empty"
  end
  if new_name.include? "$"
    raise InvalidName, "collection names must not contain '$'"
  end
  if new_name.match(/^\./) or new_name.match(/\.$/)
    raise InvalidName, "collection names must not start or end with '.'"
  end

  @db.rename_collection(@name, new_name)
end

#save(doc, options = {}) ⇒ ObjectID

Save a document to this collection.

Parameters:

  • doc (Hash)

    the document to be saved. If the document already has an ‘_id’ key, then an update (upsert) operation will be performed, and any existing document with that _id is overwritten. Otherwise an insert operation is performed.

  • opts (Hash)

    a customizable set of options

Returns:

  • (ObjectID)

    the _id of the saved document.



205
206
207
208
209
210
211
212
213
# File 'lib/mongo/collection.rb', line 205

def save(doc, options={})
  if doc.has_key?(:_id) || doc.has_key?('_id')
    id = doc[:_id] || doc['_id']
    update({:_id => id}, doc, :upsert => true, :safe => options.delete(:safe))
    id
  else
    insert(doc, :safe => options.delete(:safe))
  end
end

#update(selector, document, options = {}) ⇒ Object

Update a single document in this collection.

Parameters:

  • selector (Hash)

    a hash specifying elements which must be present for a document to be updated. Note: the update command currently updates only the first document matching the given selector. If you want all matching documents to be updated, be sure to specify :multi => true.

  • document (Hash)

    a hash specifying the fields to be changed in the selected document, or (in the case of an upsert) the document to be inserted

  • [Boolean] (Hash)

    a customizable set of options

  • opts (Hash)

    a customizable set of options



298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
# File 'lib/mongo/collection.rb', line 298

def update(selector, document, options={})
  # Initial byte is 0.
  message = ByteBuffer.new([0, 0, 0, 0])
  BSON_RUBY.serialize_cstr(message, "#{@db.name}.#{@name}")
  update_options  = 0
  update_options += 1 if options[:upsert]
  update_options += 2 if options[:multi]
  message.put_int(update_options)
  message.put_array(BSON.serialize(selector, false, true).to_a)
  message.put_array(BSON.serialize(document, false, true).to_a)
  if options[:safe]
    @connection.send_message_with_safe_check(Mongo::Constants::OP_UPDATE, message, @db.name,
      "db.#{@name}.update(#{selector.inspect}, #{document.inspect})")
  else
    @connection.send_message(Mongo::Constants::OP_UPDATE, message,
      "db.#{@name}.update(#{selector.inspect}, #{document.inspect})")
  end
end