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

Constants included from ReadPreference

ReadPreference::MONGOS_MODES, ReadPreference::READ_PREFERENCES

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

mongos, #read_pool, #read_preference, #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

Methods included from Enumerable

#each_with_object

Constructor Details

#initialize(collection, opts = {}) ⇒ Cursor

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.



25
26
27
28
29
30
31
32
33
34
35
36
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
# File 'lib/mongo/cursor.rb', line 25

def initialize(collection, opts={})
  @cursor_id  = nil
  @db         = collection.db
  @collection = collection
  @connection = @db.connection
  @logger     = @connection.logger

  # Query selector
  @selector   = opts[:selector] || {}

  # Special operators that form part of $query
  @order      = opts[:order]
  @explain    = opts[:explain]
  @hint       = opts[:hint]
  @snapshot   = opts[:snapshot]
  @max_scan   = opts.fetch(:max_scan, nil)
  @return_key = opts.fetch(:return_key, nil)
  @show_disk_loc = opts.fetch(:show_disk_loc, nil)
  @comment    = opts[:comment]

  # Wire-protocol settings
  @fields     = convert_fields_for_query(opts[:fields])
  @skip       = opts[:skip]     || 0
  @limit      = opts[:limit]    || 0
  @tailable   = opts[:tailable] || false
  @timeout    = opts.fetch(:timeout, true)
  @options    = 0

  # Use this socket for the query
  @socket = opts[:socket]
  @pool   = nil

  @closed       = false
  @query_run    = false

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

  batch_size(opts[:batch_size] || 0)

  @full_collection_name = "#{@collection.db.name}.#{@collection.name}"
  @cache        = []
  @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 @collection.name =~ /^\$cmd/ || @collection.name =~ /^system/
    @command = true
  else
    @command = false
  end
end

Instance Attribute Details

#acceptable_latencyObject (readonly)

Returns the value of attribute acceptable_latency.



11
12
13
# File 'lib/mongo/cursor.rb', line 11

def acceptable_latency
  @acceptable_latency
end

#collectionObject (readonly)

Returns the value of attribute collection.



11
12
13
# File 'lib/mongo/cursor.rb', line 11

def collection
  @collection
end

#commentObject (readonly)

Returns the value of attribute comment.



11
12
13
# File 'lib/mongo/cursor.rb', line 11

def comment
  @comment
end

#cursor_idObject (readonly)

Returns the value of attribute cursor_id.



11
12
13
# File 'lib/mongo/cursor.rb', line 11

def cursor_id
  @cursor_id
end

#fieldsObject (readonly)

Returns the value of attribute fields.



11
12
13
# File 'lib/mongo/cursor.rb', line 11

def fields
  @fields
end

#full_collection_nameObject (readonly)

Returns the value of attribute full_collection_name.



11
12
13
# File 'lib/mongo/cursor.rb', line 11

def full_collection_name
  @full_collection_name
end

#hintObject (readonly)

Returns the value of attribute hint.



11
12
13
# File 'lib/mongo/cursor.rb', line 11

def hint
  @hint
end

#optionsObject (readonly)

Returns the value of attribute options.



11
12
13
# File 'lib/mongo/cursor.rb', line 11

def options
  @options
end

#orderObject (readonly)

Returns the value of attribute order.



11
12
13
# File 'lib/mongo/cursor.rb', line 11

def order
  @order
end

#readObject (readonly)

Returns the value of attribute read.



11
12
13
# File 'lib/mongo/cursor.rb', line 11

def read
  @read
end

#selectorObject (readonly)

Returns the value of attribute selector.



11
12
13
# File 'lib/mongo/cursor.rb', line 11

def selector
  @selector
end

#show_disk_locObject (readonly)

Returns the value of attribute show_disk_loc.



11
12
13
# File 'lib/mongo/cursor.rb', line 11

def show_disk_loc
  @show_disk_loc
end

#snapshotObject (readonly)

Returns the value of attribute snapshot.



11
12
13
# File 'lib/mongo/cursor.rb', line 11

def snapshot
  @snapshot
end

#tag_setsObject (readonly)

Returns the value of attribute tag_sets.



11
12
13
# File 'lib/mongo/cursor.rb', line 11

def tag_sets
  @tag_sets
end

#timeoutObject (readonly)

Returns the value of attribute timeout.



11
12
13
# File 'lib/mongo/cursor.rb', line 11

def timeout
  @timeout
end

#transformerObject (readonly)

Returns the value of attribute transformer.



11
12
13
# File 'lib/mongo/cursor.rb', line 11

def transformer
  @transformer
end

Instance Method Details

#add_option(opt) ⇒ Integer

Add an option to the query options bitfield.

Raises:

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

See Also:



367
368
369
370
371
372
# File 'lib/mongo/cursor.rb', line 367

def add_option(opt)
  check_modifiable

  @options |= opt
  @options
end

#alive?Boolean

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.



97
98
99
# File 'lib/mongo/cursor.rb', line 97

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

#batch_size(size = nil) ⇒ Cursor

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.



246
247
248
249
250
251
252
253
254
255
256
# File 'lib/mongo/cursor.rb', line 246

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

#closeTrue

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.



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

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

#closed?Boolean

Is this cursor closed?



341
342
343
# File 'lib/mongo/cursor.rb', line 341

def closed?
  @closed
end

#count(skip_and_limit = false) ⇒ Integer

Get the size of the result set for this query.

Raises:



163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# File 'lib/mongo/cursor.rb', line 163

def count(skip_and_limit = false)
  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

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

  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

#each { ... } ⇒ Object

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.



269
270
271
272
273
274
275
276
277
278
279
280
281
# File 'lib/mongo/cursor.rb', line 269

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

#explainHash

Get the explain plan for this cursor.



303
304
305
306
307
308
309
310
# File 'lib/mongo/cursor.rb', line 303

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

  explanation
end

#has_next?Boolean

Determine whether this cursor has any remaining results.



152
153
154
# File 'lib/mongo/cursor.rb', line 152

def has_next?
  num_remaining > 0
end

#inspectObject

Clean output for inspect.



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

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

#limit(number_to_return = nil) ⇒ Integer

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.

Raises:



210
211
212
213
214
215
# File 'lib/mongo/cursor.rb', line 210

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

#nextHash, Nil Also known as: next_document

Get the next document specified the cursor options.



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/mongo/cursor.rb', line 104

def next
  if @cache.length == 0
    if @query_run && (@options & OP_QUERY_EXHAUST != 0)
      close
      return nil
    else
      refresh
    end
  end
  doc = @cache.shift

  if doc && doc['$err']
    err = doc['$err']

    # 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, doc['code'], doc)
    end

    raise OperationFailure.new(err, doc['code'], doc)
  end

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

#query_options_hashHash

Get the query options for this Cursor.



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

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

#query_optsInteger

Returns an integer indicating which query options have been selected.

The MongoDB wire protocol.



351
352
353
354
355
# File 'lib/mongo/cursor.rb', line 351

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

#remove_option(opt) ⇒ Integer

Remove an option from the query options bitfield.

Raises:

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

See Also:



384
385
386
387
388
389
# File 'lib/mongo/cursor.rb', line 384

def remove_option(opt)
  check_modifiable

  @options &= ~opt
  @options
end

#rewind!Object

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



139
140
141
142
143
144
145
146
147
# File 'lib/mongo/cursor.rb', line 139

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

#skip(number_to_skip = nil) ⇒ Integer

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.

Raises:



226
227
228
229
230
231
232
# File 'lib/mongo/cursor.rb', line 226

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

  @skip = number_to_skip
  self
end

#sort(order, direction = nil) ⇒ Object

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.

Raises:



193
194
195
196
197
198
# File 'lib/mongo/cursor.rb', line 193

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

#to_aArray

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.



294
295
296
# File 'lib/mongo/cursor.rb', line 294

def to_a
  super
end