Class: Mongo::Cursor

Inherits:
Object show all
Includes:
Enumerable, Constants, Conversions, Logging, ReadPreference
Defined in:
lib/mongo/cursor.rb

Overview

A cursor over query results. Returned objects are hashes.

Constant Summary

Constant Summary

Constants included from ReadPreference

ReadPreference::MONGOS_MODES, ReadPreference::READ_PREFERENCES, ReadPreference::SECONDARY_OK_COMMANDS

Constants included from Conversions

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

Constants included from Constants

Mongo::Constants::OP_DELETE, Mongo::Constants::OP_GET_MORE, Mongo::Constants::OP_INSERT, Mongo::Constants::OP_KILL_CURSORS, Mongo::Constants::OP_MSG, Mongo::Constants::OP_QUERY, Mongo::Constants::OP_QUERY_AWAIT_DATA, Mongo::Constants::OP_QUERY_EXHAUST, Mongo::Constants::OP_QUERY_NO_CURSOR_TIMEOUT, Mongo::Constants::OP_QUERY_OPLOG_REPLAY, Mongo::Constants::OP_QUERY_PARTIAL, Mongo::Constants::OP_QUERY_SLAVE_OK, Mongo::Constants::OP_QUERY_TAILABLE, Mongo::Constants::OP_REPLY, Mongo::Constants::OP_UPDATE, Mongo::Constants::REPLY_AWAIT_CAPABLE, Mongo::Constants::REPLY_CURSOR_NOT_FOUND, Mongo::Constants::REPLY_QUERY_FAILURE, Mongo::Constants::REPLY_SHARD_CONFIG_STALE

Instance Attribute Summary (collapse)

Instance Method Summary (collapse)

Methods included from ReadPreference

cmd_read_pref, mongos, #read_pool, #read_preference, reroute_cmd_primary?, secondary_ok?, #select_near_pool, #select_pool, #select_secondary_pool, validate

Methods included from Logging

#instrument, instrumenter, instrumenter=, #log, #write_logging_startup_message

Methods included from Conversions

#array_as_sort_parameters, #hash_as_sort_parameters, #sort_value, #string_as_sort_parameters

Constructor Details

- (Cursor) initialize(collection, opts = {})

Create a new cursor.

Note: cursors are created when executing queries using [Collection#find] and other similar methods. Application developers shouldn't have to create cursors manually.



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/mongo/cursor.rb', line 37

def initialize(collection, opts={})
  opts = opts.dup
  @cursor_id  = opts.delete(:cursor_id)
  @db         = collection.db
  @collection = collection
  @ns         = opts.delete(:ns)
  @connection = @db.connection
  @logger     = @connection.logger

  # Query selector
  @selector   = opts.delete(:selector) || {}

  # Query pre-serialized bson to append
  @bson    = @selector.delete(:bson)

  # Special operators that form part of $query
  @order         = opts.delete(:order)
  @explain       = opts.delete(:explain)
  @hint          = opts.delete(:hint)
  @snapshot      = opts.delete(:snapshot)
  @max_scan      = opts.delete(:max_scan)
  @return_key    = opts.delete(:return_key)
  @show_disk_loc = opts.delete(:show_disk_loc)
  @comment       = opts.delete(:comment)
  @compile_regex = opts.key?(:compile_regex) ? opts.delete(:compile_regex) : true

  # Wire-protocol settings
  @fields   = convert_fields_for_query(opts.delete(:fields))
  @skip     = opts.delete(:skip)     || 0
  @limit    = opts.delete(:limit)    || 0
  @tailable = opts.delete(:tailable)
  @timeout  = opts.key?(:timeout) ? opts.delete(:timeout) : true
  @options  = 0

  # Use this socket for the query
  @socket = opts.delete(:socket)
  @pool   = opts.delete(:pool)

  @closed    = false
  @query_run = false

  @transformer        = opts.delete(:transformer)
  @read               = opts.delete(:read)               || @collection.read
  Mongo::ReadPreference::validate(@read)
  @tag_sets           = opts.delete(:tag_sets)           || @collection.tag_sets
  @acceptable_latency = opts.delete(:acceptable_latency) || @collection.acceptable_latency

  batch_size(opts.delete(:batch_size) || 0)

  @cache                = opts.delete(:first_batch) || []
  @returned             = 0

  if(!@timeout)
    add_option(OP_QUERY_NO_CURSOR_TIMEOUT)
  end
  if(@read != :primary)
    add_option(OP_QUERY_SLAVE_OK)
  end
  if(@tailable)
    add_option(OP_QUERY_TAILABLE)
  end

  # If a cursor_id is provided, this is a cursor for a command
  if @cursor_id
    @command_cursor = true
    @query_run      = true
  end

  if @collection.name =~ /^\$cmd/ || @collection.name =~ /^system/
    @command = true
  else
    @command = false
  end

  @opts = opts
end

Instance Attribute Details

- (Object) acceptable_latency (readonly)

Returns the value of attribute acceptable_latency



25
26
27
# File 'lib/mongo/cursor.rb', line 25

def acceptable_latency
  @acceptable_latency
end

- (Object) collection (readonly)

Returns the value of attribute collection



25
26
27
# File 'lib/mongo/cursor.rb', line 25

def collection
  @collection
end

- (Object) comment (readonly)

Returns the value of attribute comment



25
26
27
# File 'lib/mongo/cursor.rb', line 25

def comment
  @comment
end

- (Object) compile_regex (readonly)

Returns the value of attribute compile_regex



25
26
27
# File 'lib/mongo/cursor.rb', line 25

def compile_regex
  @compile_regex
end

- (Object) cursor_id (readonly)

Returns the value of attribute cursor_id



25
26
27
# File 'lib/mongo/cursor.rb', line 25

def cursor_id
  @cursor_id
end

- (Object) fields (readonly)

Returns the value of attribute fields



25
26
27
# File 'lib/mongo/cursor.rb', line 25

def fields
  @fields
end

- (Object) hint (readonly)

Returns the value of attribute hint



25
26
27
# File 'lib/mongo/cursor.rb', line 25

def hint
  @hint
end

- (Object) options (readonly)

Returns the value of attribute options



25
26
27
# File 'lib/mongo/cursor.rb', line 25

def options
  @options
end

- (Object) order (readonly)

Returns the value of attribute order



25
26
27
# File 'lib/mongo/cursor.rb', line 25

def order
  @order
end

- (Object) read (readonly)

Returns the value of attribute read



25
26
27
# File 'lib/mongo/cursor.rb', line 25

def read
  @read
end

- (Object) selector (readonly)

Returns the value of attribute selector



25
26
27
# File 'lib/mongo/cursor.rb', line 25

def selector
  @selector
end

- (Object) show_disk_loc (readonly)

Returns the value of attribute show_disk_loc



25
26
27
# File 'lib/mongo/cursor.rb', line 25

def show_disk_loc
  @show_disk_loc
end

- (Object) snapshot (readonly)

Returns the value of attribute snapshot



25
26
27
# File 'lib/mongo/cursor.rb', line 25

def snapshot
  @snapshot
end

- (Object) tag_sets (readonly)

Returns the value of attribute tag_sets



25
26
27
# File 'lib/mongo/cursor.rb', line 25

def tag_sets
  @tag_sets
end

- (Object) timeout (readonly)

Returns the value of attribute timeout



25
26
27
# File 'lib/mongo/cursor.rb', line 25

def timeout
  @timeout
end

- (Object) transformer (readonly)

Returns the value of attribute transformer



25
26
27
# File 'lib/mongo/cursor.rb', line 25

def transformer
  @transformer
end

Instance Method Details

- (Integer) add_option(opt)

Add an option to the query options bitfield.

Parameters:

  • opt

    a valid query option

Returns:

  • (Integer)

    the current value of the options bitfield for this cursor.

Raises:

  • InvalidOperation if this method is run after the cursor has bee iterated for the first time.

See Also:



437
438
439
440
441
442
443
444
445
446
447
448
449
450
# File 'lib/mongo/cursor.rb', line 437

def add_option(opt)
  check_modifiable

  if exhaust?(opt)
    if @limit != 0
      raise MongoArgumentError, "Exhaust is incompatible with limit."
    elsif @connection.mongos?
      raise MongoArgumentError, "Exhaust is incompatible with mongos."
    end
  end

  @options |= opt
  @options
end

- (Boolean) alive?

Guess whether the cursor is alive on the server.

Note that this method only checks whether we have a cursor id. The cursor may still have timed out on the server. This will be indicated in the next call to Cursor#next.

Returns:

  • (Boolean)


122
123
124
# File 'lib/mongo/cursor.rb', line 122

def alive?
  @cursor_id && @cursor_id != 0
end

- (Cursor) batch_size(size = nil)

Set the batch size for server responses.

Note that the batch size will take effect only on queries where the number to be returned is greater than 100.

This can not override MongoDB's limit on the amount of data it will return to the client. Depending on server version this can be 4-16mb.

Parameters:

  • size (Integer) (defaults to: nil)

    either 0 or some integer greater than 1. If 0, the server will determine the batch size.

Returns:



317
318
319
320
321
322
323
324
325
326
327
# File 'lib/mongo/cursor.rb', line 317

def batch_size(size=nil)
  return @batch_size unless size
  check_modifiable
  if size < 0 || size == 1
    raise ArgumentError, "Invalid value for batch_size #{size}; must be 0 or > 1."
  else
    @batch_size = @limit != 0 && size > @limit ? @limit : size
  end

  self
end

- (True) close

Close the cursor.

Note: if a cursor is read until exhausted (read until Mongo::Constants::OP_QUERY or Mongo::Constants::OP_GETMORE returns zero for the cursor id), there is no need to close it manually.

Note also: Collection#find takes an optional block argument which can be used to ensure that your cursors get closed.

Returns:

  • (True)


392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
# File 'lib/mongo/cursor.rb', line 392

def close
  if @cursor_id && @cursor_id != 0
    message = BSON::ByteBuffer.new([0, 0, 0, 0])
    message.put_int(1)
    message.put_long(@cursor_id)
    log(:debug, "Cursor#close #{@cursor_id}")
    @connection.send_message(
      Mongo::Constants::OP_KILL_CURSORS,
      message,
      :pool => @pool
    )
  end
  @cursor_id = 0
  @closed    = true
end

- (Boolean) closed?

Is this cursor closed?

Returns:

  • (Boolean)


411
412
413
# File 'lib/mongo/cursor.rb', line 411

def closed?
  @closed
end

- (Integer) count(skip_and_limit = false)

Get the size of the result set for this query.

Parameters:

  • skip_and_limit (Boolean) (defaults to: false)

    whether or not to take skip or limit into account.

Returns:

  • (Integer)

    the number of objects in the result set for this query.

Raises:



200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
# File 'lib/mongo/cursor.rb', line 200

def count(skip_and_limit = false)
  check_command_cursor
  command = BSON::OrderedHash["count",  @collection.name, "query",  @selector]

  if skip_and_limit
    command.merge!(BSON::OrderedHash["limit", @limit]) if @limit != 0
    command.merge!(BSON::OrderedHash["skip", @skip]) if @skip != 0
  end

  if @hint
    hint = @hint.is_a?(String) ? @hint : generate_index_name(@hint)
  end

  command.merge!(BSON::OrderedHash["fields", @fields])
  command.merge!(BSON::OrderedHash["hint", hint]) if hint

  response = @db.command(command, :read => @read, :comment => @comment)
  return response['n'].to_i if Mongo::Support.ok?(response)
  return 0 if response['errmsg'] == "ns missing"
  raise OperationFailure.new("Count failed: #{response['errmsg']}", response['code'], response)
end

- (Object) each { ... }

Iterate over each document in this cursor, yielding it to the given block, if provided. An Enumerator is returned if no block is given.

Iterating over an entire cursor will close it.

Examples:

if 'comments' represents a collection of comments:

comments.find.each do |doc|
  puts doc['user']
end

Yields:

  • passes each document to a block for processing.



340
341
342
343
344
345
346
347
348
349
350
351
352
# File 'lib/mongo/cursor.rb', line 340

def each
  if block_given? || !defined?(Enumerator)
    while doc = self.next
      yield doc
    end
  else
    Enumerator.new do |yielder|
      while doc = self.next
        yielder.yield doc
      end
    end
  end
end

- (Hash) explain

Get the explain plan for this cursor.

Returns:

  • (Hash)

    a document containing the explain plan for this cursor.



372
373
374
375
376
377
378
379
380
# File 'lib/mongo/cursor.rb', line 372

def explain
  check_command_cursor
  c = Cursor.new(@collection,
    query_options_hash.merge(:limit => -@limit.abs, :explain => true))
  explanation = c.next_document
  c.close

  explanation
end

- (Object) full_collection_name



126
127
128
# File 'lib/mongo/cursor.rb', line 126

def full_collection_name
  @ns || "#{@collection.db.name}.#{@collection.name}"
end

- (Boolean) has_next?

Determine whether this cursor has any remaining results.

Returns:

  • (Boolean)


189
190
191
# File 'lib/mongo/cursor.rb', line 189

def has_next?
  num_remaining > 0
end

- (Object) inspect

Clean output for inspect.



489
490
491
492
# File 'lib/mongo/cursor.rb', line 489

def inspect
  "<Mongo::Cursor:0x#{object_id.to_s(16)} namespace='#{full_collection_name}' " +
    "@selector=#{@selector.inspect} @cursor_id=#{@cursor_id}>"
end

- (Integer) limit(number_to_return = nil)

Limit the number of results to be returned by this cursor.

This method overrides any limit specified in the Collection#find method, and only the last limit applied has an effect.

Returns:

  • (Integer)

    the current number_to_return if no parameter is given.

Raises:



251
252
253
254
255
256
257
258
259
260
261
# File 'lib/mongo/cursor.rb', line 251

def limit(number_to_return=nil)
  return @limit unless number_to_return
  check_modifiable

  if (number_to_return != 0) && exhaust?
    raise MongoArgumentError, "Limit is incompatible with exhaust option."
  end

  @limit = number_to_return
  self
end

- (Fixnum, Cursor) max_time_ms(max_time_ms = nil)

Note:

This will only have an effect in MongoDB 2.5+

Instruct the server to abort queries after they exceed the specified wall-clock execution time.

A query that completes in under its time limit will “roll over” remaining time to the first getmore op (which will then “roll over” its remaining time to the second getmore op and so on, until the time limit is hit).

Cursors returned by successful time-limited queries will still obey the default cursor idle timeout (unless the “no cursor idle timeout” flag has been set).

Parameters:

  • max_time_ms (Fixnum) (defaults to: nil)

    max execution time (in milliseconds)

Returns:

  • (Fixnum, Cursor)

    either the current max_time_ms or cursor



297
298
299
300
301
302
303
# File 'lib/mongo/cursor.rb', line 297

def max_time_ms(max_time_ms=nil)
  return @max_time_ms unless max_time_ms
  check_modifiable

  @max_time_ms = max_time_ms
  self
end

- (Hash, Nil) next Also known as: next_document

Get the next document specified the cursor options.

Returns:

  • (Hash, Nil)

    the next document or Nil if no documents remain.



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
162
163
164
165
166
167
168
169
170
# File 'lib/mongo/cursor.rb', line 133

def next
  if @cache.length == 0
    if @query_run && exhaust?
      close
      return nil
    else
      refresh
    end
  end
  doc = @cache.shift

  if doc && (err = doc['errmsg'] || doc['$err']) # assignment
    code = doc['code'] || doc['assertionCode']

    # If the server has stopped being the master (e.g., it's one of a
    # pair but it has died or something like that) then we close that
    # connection. The next request will re-open on master server.
    if err.include?("not master")
      @connection.close
      raise ConnectionFailure.new(err, code, doc)
    end

    # Handle server side operation execution timeout
    if code == 50
      raise ExecutionTimeout.new(err, code, doc)
    end

    raise OperationFailure.new(err, code, doc)
  elsif doc && (write_concern_error = doc['writeConcernError']) # assignment
    raise WriteConcernError.new(write_concern_error['errmsg'], write_concern_error['code'], doc)
  end

  if @transformer.nil?
    doc
  else
    @transformer.call(doc) if doc
  end
end

- (Hash) query_options_hash

Get the query options for this Cursor.

Returns:



472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
# File 'lib/mongo/cursor.rb', line 472

def query_options_hash
  BSON::OrderedHash[
    :selector => @selector,
    :fields   => @fields,
    :skip     => @skip,
    :limit    => @limit,
    :order    => @order,
    :hint     => @hint,
    :snapshot => @snapshot,
    :timeout  => @timeout,
    :max_scan => @max_scan,
    :return_key => @return_key,
    :show_disk_loc => @show_disk_loc,
    :comment  => @comment ]
end

- (Integer) query_opts

Returns an integer indicating which query options have been selected.

The MongoDB wire protocol.



421
422
423
424
425
# File 'lib/mongo/cursor.rb', line 421

def query_opts
  warn "The method Cursor#query_opts has been deprecated " +
    "and will removed in v2.0. Use Cursor#options instead."
  @options
end

- (Integer) remove_option(opt)

Remove an option from the query options bitfield.

Parameters:

  • opt

    a valid query option

Returns:

  • (Integer)

    the current value of the options bitfield for this cursor.

Raises:

  • InvalidOperation if this method is run after the cursor has bee iterated for the first time.

See Also:



462
463
464
465
466
467
# File 'lib/mongo/cursor.rb', line 462

def remove_option(opt)
  check_modifiable

  @options &= ~opt
  @options
end

- (Object) rewind!

Reset this cursor on the server. Cursor options, such as the query string and the values for skip and limit, are preserved.



175
176
177
178
179
180
181
182
183
184
# File 'lib/mongo/cursor.rb', line 175

def rewind!
  check_command_cursor
  close
  @cache.clear
  @cursor_id  = nil
  @closed     = false
  @query_run  = false
  @n_received = nil
  true
end

- (Integer) skip(number_to_skip = nil)

Skips the first number_to_skip results of this cursor. Returns the current number_to_skip if no parameter is given.

This method overrides any skip specified in the Collection#find method, and only the last skip applied has an effect.

Returns:

  • (Integer)

Raises:



272
273
274
275
276
277
278
# File 'lib/mongo/cursor.rb', line 272

def skip(number_to_skip=nil)
  return @skip unless number_to_skip
  check_modifiable

  @skip = number_to_skip
  self
end

- (Object) sort(order, direction = nil)

Sort this cursor's results.

This method overrides any sort order specified in the Collection#find method, and only the last sort applied has an effect.

Parameters:

  • order (Symbol, Array, Hash, OrderedHash)

    either 1) a key to sort by 2) an array of [key, direction] pairs to sort by or 3) a hash of field => direction pairs to sort by. Direction should be specified as Mongo::ASCENDING (or :ascending / :asc) or Mongo::DESCENDING (or :descending / :desc)

Raises:



236
237
238
239
240
241
# File 'lib/mongo/cursor.rb', line 236

def sort(order, direction=nil)
  check_modifiable
  order = [[order, direction]] unless direction.nil?
  @order = order
  self
end

- (Array) to_a

Receive all the documents from this cursor as an array of hashes.

Notes:

If you've already started iterating over the cursor, the array returned by this method contains only the remaining documents. See Cursor#rewind! if you need to reset the cursor.

Use of this method is discouraged - in most cases, it's much more efficient to retrieve documents as you need them by iterating over the cursor.

Returns:

  • (Array)

    an array of documents.



365
366
367
# File 'lib/mongo/cursor.rb', line 365

def to_a
  super
end