Class: Mongo::Collection

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

Overview

A named collection of documents in a database.

Constant Summary

Constants included from Conversions

Mongo::Conversions::ASCENDING_CONVERSION, Mongo::Conversions::DESCENDING_CONVERSION

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Conversions

#array_as_sort_parameters, #formatted_sort_clause, #sort_value, #string_as_sort_parameters

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



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

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.



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

def db
  @db
end

#hintObject

Returns the value of attribute hint.



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

def hint
  @hint
end

#nameObject (readonly)

Returns the value of attribute name.



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

def name
  @name
end

#pk_factoryObject (readonly)

Returns the value of attribute pk_factory.



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

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



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

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)


612
613
614
# File 'lib/mongo/collection.rb', line 612

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.



348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
# File 'lib/mongo/collection.rb', line 348

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 }
  insert_documents([sel], Mongo::DB::SYSTEM_INDEX_COLLECTION, false)
  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:



554
555
556
557
558
559
560
561
562
# File 'lib/mongo/collection.rb', line 554

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.



381
382
383
# File 'lib/mongo/collection.rb', line 381

def drop
  @db.drop_collection(@name)
end

#drop_index(name) ⇒ Object

Drop a specified index.

Parameters:

  • name (String)


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

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

#drop_indexesObject

Drop all indexes.



373
374
375
376
377
378
# File 'lib/mongo/collection.rb', line 373

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



130
131
132
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
# File 'lib/mongo/collection.rb', line 130

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_modify(opts = {}) ⇒ Hash

Find and Modify (or Remove) This command is useful to atomically change an object and then get back the results

Parameters:

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

  • :remove (Boolean) — default: false

    set to a true to remove the object before returning

  • :update (Hash) — default: {}

    a modifier object that updates the document

  • :new (Boolean) — default: false

    if true, return the modified object rather than the original. Ignored for remove.

Returns:

  • (Hash)

    The document from the database

See Also:



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

def find_modify(opts={})
  hash = OrderedHash.new
  hash['findandmodify'] = self.name
  
  sort = opts.delete(:sort)
  hash[:sort] = formatted_sort_clause(sort) if sort

  hash.merge! opts

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

  document = result['value']
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.



206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/mongo/collection.rb', line 206

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, command = false, finalize = 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.

  • command (Boolean) (defaults to: false)

    if true, run the group as a command instead of in an eval. Note: Running group as eval has been DEPRECATED.

Returns:

  • (Array)

    the grouped items.



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
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
# File 'lib/mongo/collection.rb', line 436

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

  if command

    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
    if finalize
      finalize = Code.new(finalize) unless finalize.is_a?(Code)
      group_command['group']['finalize'] = finalize
    end

    result = @db.command group_command

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

  else

    warn "Collection#group must now be run as a command; you can do this by passing 'true' as the command argument."

    raise OperationFailure, ":finalize can be specified only when " +
      "group is run as a command (set command param to true)" if finalize

    raise OperationFailure, "key must be an array of fields to group by. If you want to pass a key function, 
      run group as a command by passing 'true' as the command argument." unless key.is_a? Array || key.nil?

    case reduce
    when Code
      scope = reduce.scope
    else
      scope = {}
    end
    scope.merge!({
                   "ns" => @name,
                   "keys" => key,
                   "condition" => condition,
                   "initial" => initial })

  group_function = "function () {\nvar c = db[ns].find(condition);\nvar map = new Map();\nvar reduce_function = \#{reduce};\nwhile (c.hasNext()) {\n    var obj = c.next();\n\n    var key = {};\n    for (var i = 0; i < keys.length; i++) {\n        var k = keys[i];\n        key[k] = obj[k];\n    }\n\n    var aggObj = map.get(key);\n    if (aggObj == null) {\n        var newObj = Object.extend({}, key);\n        aggObj = Object.extend(newObj, initial);\n        map.put(key, aggObj);\n    }\n    reduce_function(obj, aggObj);\n}\nreturn {\"result\": map.values()};\n}\n"
    @db.eval(Code.new(group_function, scope))["result"]
  end
end

#index_informationHash

Get information on the indexes for this collection.

Returns:

  • (Hash)

    a hash where the keys are index names.



597
598
599
# File 'lib/mongo/collection.rb', line 597

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.



256
257
258
259
260
261
# File 'lib/mongo/collection.rb', line 256

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:



404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
# File 'lib/mongo/collection.rb', line 404

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.



605
606
607
# File 'lib/mongo/collection.rb', line 605

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:



283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
# File 'lib/mongo/collection.rb', line 283

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

  if opts[:safe]
    @connection.send_message_with_safe_check(Mongo::Constants::OP_DELETE, message,
      "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.



572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
# File 'lib/mongo/collection.rb', line 572

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.



233
234
235
236
237
238
239
240
241
# File 'lib/mongo/collection.rb', line 233

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



320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
# File 'lib/mongo/collection.rb', line 320

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).to_a)
  message.put_array(BSON.serialize(document, false).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