mongo 1.3.0 → 1.12.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data/{LICENSE.txt → LICENSE} +1 -1
- data/README.md +122 -271
- data/Rakefile +25 -209
- data/VERSION +1 -0
- data/bin/mongo_console +31 -9
- data/lib/mongo/bulk_write_collection_view.rb +387 -0
- data/lib/mongo/collection.rb +576 -269
- data/lib/mongo/collection_writer.rb +364 -0
- data/lib/mongo/connection/node.rb +249 -0
- data/lib/mongo/connection/pool.rb +340 -0
- data/lib/mongo/connection/pool_manager.rb +320 -0
- data/lib/mongo/connection/sharding_pool_manager.rb +67 -0
- data/lib/mongo/connection/socket/socket_util.rb +37 -0
- data/lib/mongo/connection/socket/ssl_socket.rb +95 -0
- data/lib/mongo/connection/socket/tcp_socket.rb +87 -0
- data/lib/mongo/connection/socket/unix_socket.rb +39 -0
- data/lib/mongo/connection/socket.rb +18 -0
- data/lib/mongo/connection.rb +7 -875
- data/lib/mongo/cursor.rb +403 -117
- data/lib/mongo/db.rb +444 -243
- data/lib/mongo/exception.rb +145 -0
- data/lib/mongo/functional/authentication.rb +455 -0
- data/lib/mongo/functional/logging.rb +85 -0
- data/lib/mongo/functional/read_preference.rb +183 -0
- data/lib/mongo/functional/scram.rb +556 -0
- data/lib/mongo/functional/uri_parser.rb +409 -0
- data/lib/mongo/functional/write_concern.rb +66 -0
- data/lib/mongo/functional.rb +20 -0
- data/lib/mongo/gridfs/grid.rb +30 -24
- data/lib/mongo/gridfs/grid_ext.rb +6 -10
- data/lib/mongo/gridfs/grid_file_system.rb +38 -20
- data/lib/mongo/gridfs/grid_io.rb +84 -75
- data/lib/mongo/gridfs.rb +18 -0
- data/lib/mongo/legacy.rb +140 -0
- data/lib/mongo/mongo_client.rb +697 -0
- data/lib/mongo/mongo_replica_set_client.rb +535 -0
- data/lib/mongo/mongo_sharded_client.rb +159 -0
- data/lib/mongo/networking.rb +372 -0
- data/lib/mongo/{util → utils}/conversions.rb +29 -8
- data/lib/mongo/{util → utils}/core_ext.rb +28 -18
- data/lib/mongo/{util → utils}/server_version.rb +4 -6
- data/lib/mongo/{util → utils}/support.rb +29 -31
- data/lib/mongo/utils/thread_local_variable_manager.rb +25 -0
- data/lib/mongo/utils.rb +19 -0
- data/lib/mongo.rb +51 -50
- data/mongo.gemspec +29 -32
- data/test/functional/authentication_test.rb +39 -0
- data/test/functional/bulk_api_stress_test.rb +133 -0
- data/test/functional/bulk_write_collection_view_test.rb +1198 -0
- data/test/functional/client_test.rb +627 -0
- data/test/functional/collection_test.rb +2175 -0
- data/test/functional/collection_writer_test.rb +83 -0
- data/test/{conversions_test.rb → functional/conversions_test.rb} +47 -3
- data/test/functional/cursor_fail_test.rb +57 -0
- data/test/functional/cursor_message_test.rb +56 -0
- data/test/functional/cursor_test.rb +683 -0
- data/test/functional/db_api_test.rb +835 -0
- data/test/functional/db_connection_test.rb +25 -0
- data/test/functional/db_test.rb +348 -0
- data/test/functional/grid_file_system_test.rb +285 -0
- data/test/{grid_io_test.rb → functional/grid_io_test.rb} +72 -11
- data/test/{grid_test.rb → functional/grid_test.rb} +88 -15
- data/test/functional/pool_test.rb +136 -0
- data/test/functional/safe_test.rb +98 -0
- data/test/functional/ssl_test.rb +29 -0
- data/test/functional/support_test.rb +62 -0
- data/test/functional/timeout_test.rb +60 -0
- data/test/functional/uri_test.rb +446 -0
- data/test/functional/write_concern_test.rb +118 -0
- data/test/helpers/general.rb +50 -0
- data/test/helpers/test_unit.rb +476 -0
- data/test/replica_set/authentication_test.rb +37 -0
- data/test/replica_set/basic_test.rb +189 -0
- data/test/replica_set/client_test.rb +393 -0
- data/test/replica_set/connection_test.rb +138 -0
- data/test/replica_set/count_test.rb +66 -0
- data/test/replica_set/cursor_test.rb +220 -0
- data/test/replica_set/insert_test.rb +157 -0
- data/test/replica_set/max_values_test.rb +151 -0
- data/test/replica_set/pinning_test.rb +105 -0
- data/test/replica_set/query_test.rb +73 -0
- data/test/replica_set/read_preference_test.rb +219 -0
- data/test/replica_set/refresh_test.rb +211 -0
- data/test/replica_set/replication_ack_test.rb +95 -0
- data/test/replica_set/ssl_test.rb +32 -0
- data/test/sharded_cluster/basic_test.rb +203 -0
- data/test/shared/authentication/basic_auth_shared.rb +260 -0
- data/test/shared/authentication/bulk_api_auth_shared.rb +249 -0
- data/test/shared/authentication/gssapi_shared.rb +176 -0
- data/test/shared/authentication/sasl_plain_shared.rb +96 -0
- data/test/shared/authentication/scram_shared.rb +92 -0
- data/test/shared/ssl_shared.rb +235 -0
- data/test/test_helper.rb +53 -94
- data/test/threading/basic_test.rb +120 -0
- data/test/tools/mongo_config.rb +708 -0
- data/test/tools/mongo_config_test.rb +160 -0
- data/test/unit/client_test.rb +381 -0
- data/test/unit/collection_test.rb +89 -53
- data/test/unit/connection_test.rb +282 -32
- data/test/unit/cursor_test.rb +206 -8
- data/test/unit/db_test.rb +55 -13
- data/test/unit/grid_test.rb +43 -16
- data/test/unit/mongo_sharded_client_test.rb +48 -0
- data/test/unit/node_test.rb +93 -0
- data/test/unit/pool_manager_test.rb +111 -0
- data/test/unit/read_pref_test.rb +406 -0
- data/test/unit/read_test.rb +159 -0
- data/test/unit/safe_test.rb +69 -36
- data/test/unit/sharding_pool_manager_test.rb +84 -0
- data/test/unit/write_concern_test.rb +175 -0
- data.tar.gz.sig +3 -0
- metadata +227 -216
- metadata.gz.sig +0 -0
- data/docs/CREDITS.md +0 -123
- data/docs/FAQ.md +0 -116
- data/docs/GridFS.md +0 -158
- data/docs/HISTORY.md +0 -244
- data/docs/RELEASES.md +0 -33
- data/docs/REPLICA_SETS.md +0 -72
- data/docs/TUTORIAL.md +0 -247
- data/docs/WRITE_CONCERN.md +0 -28
- data/lib/mongo/exceptions.rb +0 -71
- data/lib/mongo/gridfs/grid_io_fix.rb +0 -38
- data/lib/mongo/repl_set_connection.rb +0 -342
- data/lib/mongo/test.rb +0 -20
- data/lib/mongo/util/pool.rb +0 -177
- data/lib/mongo/util/uri_parser.rb +0 -185
- data/test/async/collection_test.rb +0 -224
- data/test/async/connection_test.rb +0 -24
- data/test/async/cursor_test.rb +0 -162
- data/test/async/worker_pool_test.rb +0 -99
- data/test/auxillary/1.4_features.rb +0 -166
- data/test/auxillary/authentication_test.rb +0 -68
- data/test/auxillary/autoreconnect_test.rb +0 -41
- data/test/auxillary/fork_test.rb +0 -30
- data/test/auxillary/repl_set_auth_test.rb +0 -58
- data/test/auxillary/slave_connection_test.rb +0 -36
- data/test/auxillary/threaded_authentication_test.rb +0 -101
- data/test/bson/binary_test.rb +0 -15
- data/test/bson/bson_test.rb +0 -649
- data/test/bson/byte_buffer_test.rb +0 -208
- data/test/bson/hash_with_indifferent_access_test.rb +0 -38
- data/test/bson/json_test.rb +0 -17
- data/test/bson/object_id_test.rb +0 -154
- data/test/bson/ordered_hash_test.rb +0 -204
- data/test/bson/timestamp_test.rb +0 -24
- data/test/collection_test.rb +0 -910
- data/test/connection_test.rb +0 -309
- data/test/cursor_fail_test.rb +0 -75
- data/test/cursor_message_test.rb +0 -43
- data/test/cursor_test.rb +0 -483
- data/test/db_api_test.rb +0 -726
- data/test/db_connection_test.rb +0 -15
- data/test/db_test.rb +0 -287
- data/test/grid_file_system_test.rb +0 -243
- data/test/load/resque/load.rb +0 -21
- data/test/load/resque/processor.rb +0 -26
- data/test/load/thin/load.rb +0 -24
- data/test/load/unicorn/load.rb +0 -23
- data/test/load/unicorn/unicorn.rb +0 -29
- data/test/replica_sets/connect_test.rb +0 -94
- data/test/replica_sets/connection_string_test.rb +0 -32
- data/test/replica_sets/count_test.rb +0 -35
- data/test/replica_sets/insert_test.rb +0 -53
- data/test/replica_sets/pooled_insert_test.rb +0 -55
- data/test/replica_sets/query_secondaries.rb +0 -96
- data/test/replica_sets/query_test.rb +0 -51
- data/test/replica_sets/replication_ack_test.rb +0 -66
- data/test/replica_sets/rs_test_helper.rb +0 -27
- data/test/safe_test.rb +0 -68
- data/test/support/hash_with_indifferent_access.rb +0 -186
- data/test/support/keys.rb +0 -45
- data/test/support_test.rb +0 -18
- data/test/threading/threading_with_large_pool_test.rb +0 -90
- data/test/threading_test.rb +0 -87
- data/test/tools/auth_repl_set_manager.rb +0 -14
- data/test/tools/load.rb +0 -58
- data/test/tools/repl_set_manager.rb +0 -266
- data/test/tools/sharding_manager.rb +0 -202
- data/test/tools/test.rb +0 -4
- data/test/unit/pool_test.rb +0 -9
- data/test/unit/repl_set_connection_test.rb +0 -59
- data/test/uri_test.rb +0 -91
data/lib/mongo/cursor.rb
CHANGED
@@ -1,12 +1,10 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
# Copyright (C) 2008-2011 10gen Inc.
|
1
|
+
# Copyright (C) 2009-2013 MongoDB, Inc.
|
4
2
|
#
|
5
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
4
|
# you may not use this file except in compliance with the License.
|
7
5
|
# You may obtain a copy of the License at
|
8
6
|
#
|
9
|
-
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
8
|
#
|
11
9
|
# Unless required by applicable law or agreed to in writing, software
|
12
10
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
@@ -18,12 +16,17 @@ module Mongo
|
|
18
16
|
|
19
17
|
# A cursor over query results. Returned objects are hashes.
|
20
18
|
class Cursor
|
21
|
-
include Mongo::Conversions
|
22
19
|
include Enumerable
|
20
|
+
include Mongo::Constants
|
21
|
+
include Mongo::Conversions
|
22
|
+
include Mongo::Logging
|
23
|
+
include Mongo::ReadPreference
|
23
24
|
|
24
25
|
attr_reader :collection, :selector, :fields,
|
25
|
-
:order, :hint, :snapshot, :timeout,
|
26
|
-
:
|
26
|
+
:order, :hint, :snapshot, :timeout, :transformer,
|
27
|
+
:options, :cursor_id, :show_disk_loc,
|
28
|
+
:comment, :compile_regex, :read, :tag_sets,
|
29
|
+
:acceptable_latency
|
27
30
|
|
28
31
|
# Create a new cursor.
|
29
32
|
#
|
@@ -31,62 +34,132 @@ module Mongo
|
|
31
34
|
# similar methods. Application developers shouldn't have to create cursors manually.
|
32
35
|
#
|
33
36
|
# @return [Cursor]
|
34
|
-
#
|
35
|
-
# @core cursors constructor_details
|
36
37
|
def initialize(collection, opts={})
|
37
|
-
|
38
|
-
|
38
|
+
opts = opts.dup
|
39
|
+
@cursor_id = opts.delete(:cursor_id)
|
39
40
|
@db = collection.db
|
40
41
|
@collection = collection
|
42
|
+
@ns = opts.delete(:ns)
|
41
43
|
@connection = @db.connection
|
42
44
|
@logger = @connection.logger
|
43
45
|
|
44
|
-
|
45
|
-
@
|
46
|
-
|
47
|
-
|
48
|
-
@
|
49
|
-
|
50
|
-
|
51
|
-
@
|
52
|
-
@explain
|
53
|
-
@
|
54
|
-
@
|
55
|
-
@
|
56
|
-
@
|
57
|
-
@
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
@
|
46
|
+
# Query selector
|
47
|
+
@selector = opts.delete(:selector) || {}
|
48
|
+
|
49
|
+
# Query pre-serialized bson to append
|
50
|
+
@bson = @selector.delete(:bson)
|
51
|
+
|
52
|
+
# Special operators that form part of $query
|
53
|
+
@order = opts.delete(:order)
|
54
|
+
@explain = opts.delete(:explain)
|
55
|
+
@hint = opts.delete(:hint)
|
56
|
+
@snapshot = opts.delete(:snapshot)
|
57
|
+
@max_scan = opts.delete(:max_scan)
|
58
|
+
@return_key = opts.delete(:return_key)
|
59
|
+
@show_disk_loc = opts.delete(:show_disk_loc)
|
60
|
+
@comment = opts.delete(:comment)
|
61
|
+
@compile_regex = opts.key?(:compile_regex) ? opts.delete(:compile_regex) : true
|
62
|
+
|
63
|
+
# Wire-protocol settings
|
64
|
+
@fields = convert_fields_for_query(opts.delete(:fields))
|
65
|
+
@skip = opts.delete(:skip) || 0
|
66
|
+
@limit = opts.delete(:limit) || 0
|
67
|
+
@tailable = opts.delete(:tailable)
|
68
|
+
@timeout = opts.key?(:timeout) ? opts.delete(:timeout) : true
|
69
|
+
@options = 0
|
70
|
+
|
71
|
+
# Use this socket for the query
|
72
|
+
@socket = opts.delete(:socket)
|
73
|
+
@pool = opts.delete(:pool)
|
74
|
+
|
75
|
+
@closed = false
|
76
|
+
@query_run = false
|
77
|
+
|
78
|
+
@transformer = opts.delete(:transformer)
|
79
|
+
@read = opts.delete(:read) || @collection.read
|
80
|
+
Mongo::ReadPreference::validate(@read)
|
81
|
+
@tag_sets = opts.delete(:tag_sets) || @collection.tag_sets
|
82
|
+
@acceptable_latency = opts.delete(:acceptable_latency) || @collection.acceptable_latency
|
83
|
+
|
84
|
+
batch_size(opts.delete(:batch_size) || 0)
|
85
|
+
|
86
|
+
@cache = opts.delete(:first_batch) || []
|
87
|
+
@returned = 0
|
88
|
+
|
89
|
+
if(!@timeout)
|
90
|
+
add_option(OP_QUERY_NO_CURSOR_TIMEOUT)
|
91
|
+
end
|
92
|
+
if(@read != :primary)
|
93
|
+
add_option(OP_QUERY_SLAVE_OK)
|
94
|
+
end
|
95
|
+
if(@tailable)
|
96
|
+
add_option(OP_QUERY_TAILABLE)
|
97
|
+
end
|
98
|
+
|
99
|
+
# If a cursor_id is provided, this is a cursor for a command
|
100
|
+
if @cursor_id
|
101
|
+
@command_cursor = true
|
102
|
+
@query_run = true
|
103
|
+
end
|
63
104
|
|
64
105
|
if @collection.name =~ /^\$cmd/ || @collection.name =~ /^system/
|
65
106
|
@command = true
|
66
107
|
else
|
67
108
|
@command = false
|
68
109
|
end
|
110
|
+
|
111
|
+
@opts = opts
|
112
|
+
end
|
113
|
+
|
114
|
+
# Guess whether the cursor is alive on the server.
|
115
|
+
#
|
116
|
+
# Note that this method only checks whether we have
|
117
|
+
# a cursor id. The cursor may still have timed out
|
118
|
+
# on the server. This will be indicated in the next
|
119
|
+
# call to Cursor#next.
|
120
|
+
#
|
121
|
+
# @return [Boolean]
|
122
|
+
def alive?
|
123
|
+
@cursor_id && @cursor_id != 0
|
124
|
+
end
|
125
|
+
|
126
|
+
def full_collection_name
|
127
|
+
@ns || "#{@collection.db.name}.#{@collection.name}"
|
69
128
|
end
|
70
129
|
|
71
130
|
# Get the next document specified the cursor options.
|
72
131
|
#
|
73
132
|
# @return [Hash, Nil] the next document or Nil if no documents remain.
|
74
|
-
def
|
75
|
-
|
133
|
+
def next
|
134
|
+
if @cache.length == 0
|
135
|
+
if @query_run && exhaust?
|
136
|
+
close
|
137
|
+
return nil
|
138
|
+
else
|
139
|
+
refresh
|
140
|
+
end
|
141
|
+
end
|
76
142
|
doc = @cache.shift
|
77
143
|
|
78
|
-
if doc && doc['$err']
|
79
|
-
|
144
|
+
if doc && (err = doc['errmsg'] || doc['$err']) # assignment
|
145
|
+
code = doc['code'] || doc['assertionCode']
|
80
146
|
|
81
147
|
# If the server has stopped being the master (e.g., it's one of a
|
82
148
|
# pair but it has died or something like that) then we close that
|
83
149
|
# connection. The next request will re-open on master server.
|
84
|
-
if err
|
150
|
+
if err.include?("not master")
|
85
151
|
@connection.close
|
86
|
-
raise ConnectionFailure,
|
152
|
+
raise ConnectionFailure.new(err, code, doc)
|
153
|
+
end
|
154
|
+
|
155
|
+
# Handle server side operation execution timeout
|
156
|
+
if code == 50
|
157
|
+
raise ExecutionTimeout.new(err, code, doc)
|
87
158
|
end
|
88
159
|
|
89
|
-
raise OperationFailure,
|
160
|
+
raise OperationFailure.new(err, code, doc)
|
161
|
+
elsif doc && (write_concern_error = doc['writeConcernError']) # assignment
|
162
|
+
raise WriteConcernError.new(write_concern_error['errmsg'], write_concern_error['code'], doc)
|
90
163
|
end
|
91
164
|
|
92
165
|
if @transformer.nil?
|
@@ -95,11 +168,12 @@ module Mongo
|
|
95
168
|
@transformer.call(doc) if doc
|
96
169
|
end
|
97
170
|
end
|
98
|
-
alias :
|
171
|
+
alias :next_document :next
|
99
172
|
|
100
173
|
# Reset this cursor on the server. Cursor options, such as the
|
101
174
|
# query string and the values for skip and limit, are preserved.
|
102
175
|
def rewind!
|
176
|
+
check_command_cursor
|
103
177
|
close
|
104
178
|
@cache.clear
|
105
179
|
@cursor_id = nil
|
@@ -118,12 +192,13 @@ module Mongo
|
|
118
192
|
|
119
193
|
# Get the size of the result set for this query.
|
120
194
|
#
|
121
|
-
# @param [Boolean] whether
|
195
|
+
# @param [Boolean] skip_and_limit whether or not to take skip or limit into account.
|
122
196
|
#
|
123
197
|
# @return [Integer] the number of objects in the result set for this query.
|
124
198
|
#
|
125
199
|
# @raise [OperationFailure] on a database error.
|
126
200
|
def count(skip_and_limit = false)
|
201
|
+
check_command_cursor
|
127
202
|
command = BSON::OrderedHash["count", @collection.name, "query", @selector]
|
128
203
|
|
129
204
|
if skip_and_limit
|
@@ -131,12 +206,17 @@ module Mongo
|
|
131
206
|
command.merge!(BSON::OrderedHash["skip", @skip]) if @skip != 0
|
132
207
|
end
|
133
208
|
|
209
|
+
if @hint
|
210
|
+
hint = @hint.is_a?(String) ? @hint : generate_index_name(@hint)
|
211
|
+
end
|
212
|
+
|
134
213
|
command.merge!(BSON::OrderedHash["fields", @fields])
|
214
|
+
command.merge!(BSON::OrderedHash["hint", hint]) if hint
|
135
215
|
|
136
|
-
response = @db.command(command)
|
216
|
+
response = @db.command(command, :read => @read, :comment => @comment)
|
137
217
|
return response['n'].to_i if Mongo::Support.ok?(response)
|
138
218
|
return 0 if response['errmsg'] == "ns missing"
|
139
|
-
raise OperationFailure
|
219
|
+
raise OperationFailure.new("Count failed: #{response['errmsg']}", response['code'], response)
|
140
220
|
end
|
141
221
|
|
142
222
|
# Sort this cursor's results.
|
@@ -144,22 +224,18 @@ module Mongo
|
|
144
224
|
# This method overrides any sort order specified in the Collection#find
|
145
225
|
# method, and only the last sort applied has an effect.
|
146
226
|
#
|
147
|
-
# @param [Symbol, Array]
|
148
|
-
# an array of [key, direction] pairs to sort by
|
149
|
-
#
|
227
|
+
# @param [Symbol, Array, Hash, OrderedHash] order either 1) a key to sort by 2)
|
228
|
+
# an array of [key, direction] pairs to sort by or 3) a hash of
|
229
|
+
# field => direction pairs to sort by. Direction should be specified as
|
230
|
+
# Mongo::ASCENDING (or :ascending / :asc) or Mongo::DESCENDING
|
231
|
+
# (or :descending / :desc)
|
150
232
|
#
|
151
233
|
# @raise [InvalidOperation] if this cursor has already been used.
|
152
234
|
#
|
153
235
|
# @raise [InvalidSortValueError] if the specified order is invalid.
|
154
|
-
def sort(
|
236
|
+
def sort(order, direction=nil)
|
155
237
|
check_modifiable
|
156
|
-
|
157
|
-
if !direction.nil?
|
158
|
-
order = [[key_or_list, direction]]
|
159
|
-
else
|
160
|
-
order = key_or_list
|
161
|
-
end
|
162
|
-
|
238
|
+
order = [[order, direction]] unless direction.nil?
|
163
239
|
@order = order
|
164
240
|
self
|
165
241
|
end
|
@@ -172,12 +248,14 @@ module Mongo
|
|
172
248
|
# @return [Integer] the current number_to_return if no parameter is given.
|
173
249
|
#
|
174
250
|
# @raise [InvalidOperation] if this cursor has already been used.
|
175
|
-
#
|
176
|
-
# @core limit limit-instance_method
|
177
251
|
def limit(number_to_return=nil)
|
178
252
|
return @limit unless number_to_return
|
179
253
|
check_modifiable
|
180
254
|
|
255
|
+
if (number_to_return != 0) && exhaust?
|
256
|
+
raise MongoArgumentError, "Limit is incompatible with exhaust option."
|
257
|
+
end
|
258
|
+
|
181
259
|
@limit = number_to_return
|
182
260
|
self
|
183
261
|
end
|
@@ -199,28 +277,57 @@ module Mongo
|
|
199
277
|
self
|
200
278
|
end
|
201
279
|
|
280
|
+
# Instruct the server to abort queries after they exceed the specified
|
281
|
+
# wall-clock execution time.
|
282
|
+
#
|
283
|
+
# A query that completes in under its time limit will "roll over"
|
284
|
+
# remaining time to the first getmore op (which will then "roll over"
|
285
|
+
# its remaining time to the second getmore op and so on, until the
|
286
|
+
# time limit is hit).
|
287
|
+
#
|
288
|
+
# Cursors returned by successful time-limited queries will still obey
|
289
|
+
# the default cursor idle timeout (unless the "no cursor idle timeout"
|
290
|
+
# flag has been set).
|
291
|
+
#
|
292
|
+
# @note This will only have an effect in MongoDB 2.5+
|
293
|
+
#
|
294
|
+
# @param max_time_ms [Fixnum] max execution time (in milliseconds)
|
295
|
+
#
|
296
|
+
# @return [Fixnum, Cursor] either the current max_time_ms or cursor
|
297
|
+
def max_time_ms(max_time_ms=nil)
|
298
|
+
return @max_time_ms unless max_time_ms
|
299
|
+
check_modifiable
|
300
|
+
|
301
|
+
@max_time_ms = max_time_ms
|
302
|
+
self
|
303
|
+
end
|
304
|
+
|
202
305
|
# Set the batch size for server responses.
|
203
306
|
#
|
204
307
|
# Note that the batch size will take effect only on queries
|
205
308
|
# where the number to be returned is greater than 100.
|
206
309
|
#
|
310
|
+
# This can not override MongoDB's limit on the amount of data it will
|
311
|
+
# return to the client. Depending on server version this can be 4-16mb.
|
312
|
+
#
|
207
313
|
# @param [Integer] size either 0 or some integer greater than 1. If 0,
|
208
314
|
# the server will determine the batch size.
|
209
315
|
#
|
210
316
|
# @return [Cursor]
|
211
|
-
def batch_size(size=
|
317
|
+
def batch_size(size=nil)
|
318
|
+
return @batch_size unless size
|
212
319
|
check_modifiable
|
213
320
|
if size < 0 || size == 1
|
214
321
|
raise ArgumentError, "Invalid value for batch_size #{size}; must be 0 or > 1."
|
215
322
|
else
|
216
|
-
@batch_size = size > @limit ? @limit : size
|
323
|
+
@batch_size = @limit != 0 && size > @limit ? @limit : size
|
217
324
|
end
|
218
325
|
|
219
326
|
self
|
220
327
|
end
|
221
328
|
|
222
329
|
# Iterate over each document in this cursor, yielding it to the given
|
223
|
-
# block.
|
330
|
+
# block, if provided. An Enumerator is returned if no block is given.
|
224
331
|
#
|
225
332
|
# Iterating over an entire cursor will close it.
|
226
333
|
#
|
@@ -231,14 +338,18 @@ module Mongo
|
|
231
338
|
# puts doc['user']
|
232
339
|
# end
|
233
340
|
def each
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
341
|
+
if block_given? || !defined?(Enumerator)
|
342
|
+
while doc = self.next
|
343
|
+
yield doc
|
344
|
+
end
|
345
|
+
else
|
346
|
+
Enumerator.new do |yielder|
|
347
|
+
while doc = self.next
|
348
|
+
yielder.yield doc
|
349
|
+
end
|
350
|
+
end
|
239
351
|
end
|
240
352
|
end
|
241
|
-
|
242
353
|
# Receive all the documents from this cursor as an array of hashes.
|
243
354
|
#
|
244
355
|
# Notes:
|
@@ -258,10 +369,10 @@ module Mongo
|
|
258
369
|
# Get the explain plan for this cursor.
|
259
370
|
#
|
260
371
|
# @return [Hash] a document containing the explain plan for this cursor.
|
261
|
-
#
|
262
|
-
# @core explain explain-instance_method
|
263
372
|
def explain
|
264
|
-
|
373
|
+
check_command_cursor
|
374
|
+
c = Cursor.new(@collection,
|
375
|
+
query_options_hash.merge(:limit => -@limit.abs, :explain => true))
|
265
376
|
explanation = c.next_document
|
266
377
|
c.close
|
267
378
|
|
@@ -283,8 +394,12 @@ module Mongo
|
|
283
394
|
message = BSON::ByteBuffer.new([0, 0, 0, 0])
|
284
395
|
message.put_int(1)
|
285
396
|
message.put_long(@cursor_id)
|
286
|
-
|
287
|
-
@connection.send_message(
|
397
|
+
log(:debug, "Cursor#close #{@cursor_id}")
|
398
|
+
@connection.send_message(
|
399
|
+
Mongo::Constants::OP_KILL_CURSORS,
|
400
|
+
message,
|
401
|
+
:pool => @pool
|
402
|
+
)
|
288
403
|
end
|
289
404
|
@cursor_id = 0
|
290
405
|
@closed = true
|
@@ -293,7 +408,9 @@ module Mongo
|
|
293
408
|
# Is this cursor closed?
|
294
409
|
#
|
295
410
|
# @return [Boolean]
|
296
|
-
def closed
|
411
|
+
def closed?
|
412
|
+
@closed
|
413
|
+
end
|
297
414
|
|
298
415
|
# Returns an integer indicating which query options have been selected.
|
299
416
|
#
|
@@ -302,31 +419,76 @@ module Mongo
|
|
302
419
|
# @see http://www.mongodb.org/display/DOCS/Mongo+Wire+Protocol#MongoWireProtocol-Mongo::Constants::OPQUERY
|
303
420
|
# The MongoDB wire protocol.
|
304
421
|
def query_opts
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
422
|
+
warn "The method Cursor#query_opts has been deprecated " +
|
423
|
+
"and will removed in v2.0. Use Cursor#options instead."
|
424
|
+
@options
|
425
|
+
end
|
426
|
+
|
427
|
+
# Add an option to the query options bitfield.
|
428
|
+
#
|
429
|
+
# @param opt a valid query option
|
430
|
+
#
|
431
|
+
# @raise InvalidOperation if this method is run after the cursor has bee
|
432
|
+
# iterated for the first time.
|
433
|
+
#
|
434
|
+
# @return [Integer] the current value of the options bitfield for this cursor.
|
435
|
+
#
|
436
|
+
# @see http://www.mongodb.org/display/DOCS/Mongo+Wire+Protocol#MongoWireProtocol-Mongo::Constants::OPQUERY
|
437
|
+
def add_option(opt)
|
438
|
+
check_modifiable
|
439
|
+
|
440
|
+
if exhaust?(opt)
|
441
|
+
if @limit != 0
|
442
|
+
raise MongoArgumentError, "Exhaust is incompatible with limit."
|
443
|
+
elsif @connection.mongos?
|
444
|
+
raise MongoArgumentError, "Exhaust is incompatible with mongos."
|
445
|
+
end
|
446
|
+
end
|
447
|
+
|
448
|
+
@options |= opt
|
449
|
+
@options
|
450
|
+
end
|
451
|
+
|
452
|
+
# Remove an option from the query options bitfield.
|
453
|
+
#
|
454
|
+
# @param opt a valid query option
|
455
|
+
#
|
456
|
+
# @raise InvalidOperation if this method is run after the cursor has bee
|
457
|
+
# iterated for the first time.
|
458
|
+
#
|
459
|
+
# @return [Integer] the current value of the options bitfield for this cursor.
|
460
|
+
#
|
461
|
+
# @see http://www.mongodb.org/display/DOCS/Mongo+Wire+Protocol#MongoWireProtocol-Mongo::Constants::OPQUERY
|
462
|
+
def remove_option(opt)
|
463
|
+
check_modifiable
|
464
|
+
|
465
|
+
@options &= ~opt
|
466
|
+
@options
|
310
467
|
end
|
311
468
|
|
312
469
|
# Get the query options for this Cursor.
|
313
470
|
#
|
314
471
|
# @return [Hash]
|
315
472
|
def query_options_hash
|
316
|
-
|
473
|
+
BSON::OrderedHash[
|
474
|
+
:selector => @selector,
|
317
475
|
:fields => @fields,
|
318
476
|
:skip => @skip,
|
319
477
|
:limit => @limit,
|
320
478
|
:order => @order,
|
321
479
|
:hint => @hint,
|
322
480
|
:snapshot => @snapshot,
|
323
|
-
:timeout => @timeout
|
481
|
+
:timeout => @timeout,
|
482
|
+
:max_scan => @max_scan,
|
483
|
+
:return_key => @return_key,
|
484
|
+
:show_disk_loc => @show_disk_loc,
|
485
|
+
:comment => @comment ]
|
324
486
|
end
|
325
487
|
|
326
488
|
# Clean output for inspect.
|
327
489
|
def inspect
|
328
|
-
"<Mongo::Cursor:0x#{object_id.to_s(16)} namespace='#{
|
329
|
-
"@selector=#{@selector.inspect}>"
|
490
|
+
"<Mongo::Cursor:0x#{object_id.to_s(16)} namespace='#{full_collection_name}' " +
|
491
|
+
"@selector=#{@selector.inspect} @cursor_id=#{@cursor_id}>"
|
330
492
|
end
|
331
493
|
|
332
494
|
private
|
@@ -340,7 +502,10 @@ module Mongo
|
|
340
502
|
{fields => 1}
|
341
503
|
when Array
|
342
504
|
return nil if fields.length.zero?
|
343
|
-
fields.
|
505
|
+
fields.inject({}) do |hash, field|
|
506
|
+
field.is_a?(Hash) ? hash.merge!(field) : hash[field] = 1
|
507
|
+
hash
|
508
|
+
end
|
344
509
|
when Hash
|
345
510
|
return fields
|
346
511
|
end
|
@@ -348,16 +513,77 @@ module Mongo
|
|
348
513
|
|
349
514
|
# Return the number of documents remaining for this cursor.
|
350
515
|
def num_remaining
|
351
|
-
|
516
|
+
if @cache.length == 0
|
517
|
+
if @query_run && exhaust?
|
518
|
+
close
|
519
|
+
return 0
|
520
|
+
else
|
521
|
+
refresh
|
522
|
+
end
|
523
|
+
end
|
524
|
+
|
352
525
|
@cache.length
|
353
526
|
end
|
354
527
|
|
528
|
+
# Refresh the documents in @cache. This means either
|
529
|
+
# sending the initial query or sending a GET_MORE operation.
|
355
530
|
def refresh
|
356
|
-
|
531
|
+
if !@query_run
|
532
|
+
send_initial_query
|
533
|
+
elsif !@cursor_id.zero?
|
534
|
+
send_get_more
|
535
|
+
end
|
536
|
+
end
|
537
|
+
|
538
|
+
# Sends initial query -- which is always a read unless it is a command
|
539
|
+
#
|
540
|
+
# Upon ConnectionFailure, tries query 3 times if socket was not provided
|
541
|
+
# and the query is either not a command or is a secondary_ok command.
|
542
|
+
#
|
543
|
+
# Pins pools upon successful read and unpins pool upon ConnectionFailure
|
544
|
+
#
|
545
|
+
def send_initial_query
|
546
|
+
tries = 0
|
547
|
+
instrument(:find, instrument_payload) do
|
548
|
+
begin
|
549
|
+
message = construct_query_message
|
550
|
+
socket = @socket || checkout_socket_from_connection
|
551
|
+
results, @n_received, @cursor_id = @connection.receive_message(
|
552
|
+
Mongo::Constants::OP_QUERY, message, nil, socket, @command,
|
553
|
+
nil, exhaust?, compile_regex?)
|
554
|
+
rescue ConnectionFailure => ex
|
555
|
+
socket.close if socket
|
556
|
+
@pool = nil
|
557
|
+
@connection.unpin_pool
|
558
|
+
@connection.refresh
|
559
|
+
if tries < 3 && !@socket && (!@command || Mongo::ReadPreference::secondary_ok?(@selector))
|
560
|
+
tries += 1
|
561
|
+
retry
|
562
|
+
else
|
563
|
+
raise ex
|
564
|
+
end
|
565
|
+
rescue OperationFailure, OperationTimeout => ex
|
566
|
+
raise ex
|
567
|
+
ensure
|
568
|
+
socket.checkin unless @socket || socket.nil?
|
569
|
+
end
|
570
|
+
|
571
|
+
if pin_pool?(results.first)
|
572
|
+
@connection.pin_pool(socket.pool, read_preference)
|
573
|
+
end
|
574
|
+
|
575
|
+
@returned += @n_received
|
576
|
+
@cache += results
|
577
|
+
@query_run = true
|
578
|
+
close_cursor_if_query_complete
|
579
|
+
end
|
580
|
+
end
|
581
|
+
|
582
|
+
def send_get_more
|
357
583
|
message = BSON::ByteBuffer.new([0, 0, 0, 0])
|
358
584
|
|
359
585
|
# DB name.
|
360
|
-
BSON::BSON_RUBY.serialize_cstr(message,
|
586
|
+
BSON::BSON_RUBY.serialize_cstr(message, full_collection_name)
|
361
587
|
|
362
588
|
# Number of results to return.
|
363
589
|
if @limit > 0
|
@@ -372,52 +598,73 @@ module Mongo
|
|
372
598
|
|
373
599
|
# Cursor id.
|
374
600
|
message.put_long(@cursor_id)
|
375
|
-
|
376
|
-
|
377
|
-
|
601
|
+
log(:debug, "cursor.refresh() for cursor #{@cursor_id}") if @logger
|
602
|
+
|
603
|
+
socket = @pool.checkout
|
604
|
+
|
605
|
+
begin
|
606
|
+
results, @n_received, @cursor_id = @connection.receive_message(
|
607
|
+
Mongo::Constants::OP_GET_MORE, message, nil, socket, @command,
|
608
|
+
nil, exhaust?, compile_regex?)
|
609
|
+
ensure
|
610
|
+
socket.checkin
|
611
|
+
end
|
612
|
+
|
378
613
|
@returned += @n_received
|
379
614
|
@cache += results
|
380
615
|
close_cursor_if_query_complete
|
381
616
|
end
|
382
617
|
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
@connection.instrument(:find, instrument_payload) do
|
392
|
-
results, @n_received, @cursor_id = @connection.receive_message(
|
393
|
-
Mongo::Constants::OP_QUERY, message, nil, @socket, @command)
|
394
|
-
@returned += @n_received
|
395
|
-
@cache += results
|
396
|
-
@query_run = true
|
397
|
-
close_cursor_if_query_complete
|
618
|
+
def checkout_socket_from_connection
|
619
|
+
begin
|
620
|
+
if @pool
|
621
|
+
socket = @pool.checkout
|
622
|
+
elsif @command && !Mongo::ReadPreference::secondary_ok?(@selector)
|
623
|
+
socket = @connection.checkout_reader({:mode => :primary})
|
624
|
+
else
|
625
|
+
socket = @connection.checkout_reader(read_preference)
|
398
626
|
end
|
399
|
-
|
627
|
+
rescue SystemStackError, NoMemoryError, SystemCallError => ex
|
628
|
+
@connection.close
|
629
|
+
raise ex
|
400
630
|
end
|
631
|
+
@pool = socket.pool
|
632
|
+
socket
|
633
|
+
end
|
634
|
+
|
635
|
+
def checkin_socket(sock)
|
636
|
+
@connection.checkin(sock)
|
401
637
|
end
|
402
638
|
|
403
639
|
def construct_query_message
|
404
|
-
message = BSON::ByteBuffer.new
|
405
|
-
message.put_int(
|
406
|
-
BSON::BSON_RUBY.serialize_cstr(message,
|
640
|
+
message = BSON::ByteBuffer.new("", @connection.max_bson_size + MongoClient::COMMAND_HEADROOM)
|
641
|
+
message.put_int(@options)
|
642
|
+
BSON::BSON_RUBY.serialize_cstr(message, full_collection_name)
|
407
643
|
message.put_int(@skip)
|
408
|
-
message.put_int(@limit)
|
409
|
-
|
410
|
-
|
411
|
-
|
644
|
+
@batch_size > 1 ? message.put_int(@batch_size) : message.put_int(@limit)
|
645
|
+
if query_contains_special_fields? && @bson # costs two serialize calls
|
646
|
+
query_message = BSON::BSON_CODER.serialize(@selector, false, false, @connection.max_bson_size + MongoClient::APPEND_HEADROOM)
|
647
|
+
query_message.grow(@bson)
|
648
|
+
query_spec = construct_query_spec
|
649
|
+
query_spec.delete('$query')
|
650
|
+
query_message.grow(BSON::BSON_CODER.serialize(query_spec, false, false, @connection.max_bson_size))
|
651
|
+
else # costs only one serialize call
|
652
|
+
spec = query_contains_special_fields? ? construct_query_spec : @selector
|
653
|
+
spec.merge!(@opts)
|
654
|
+
query_message = BSON::BSON_CODER.serialize(spec, false, false, @connection.max_bson_size + MongoClient::APPEND_HEADROOM)
|
655
|
+
query_message.grow(@bson) if @bson
|
656
|
+
end
|
657
|
+
message.put_binary(query_message.to_s)
|
658
|
+
message.put_binary(BSON::BSON_CODER.serialize(@fields, false, false, @connection.max_bson_size).to_s) if @fields
|
412
659
|
message
|
413
660
|
end
|
414
661
|
|
415
662
|
def instrument_payload
|
416
663
|
log = { :database => @db.name, :collection => @collection.name, :selector => selector }
|
417
|
-
log[:fields] = @fields
|
418
|
-
log[:skip]
|
419
|
-
log[:limit]
|
420
|
-
log[:order]
|
664
|
+
log[:fields] = @fields if @fields
|
665
|
+
log[:skip] = @skip if @skip && (@skip != 0)
|
666
|
+
log[:limit] = @limit if @limit && (@limit != 0)
|
667
|
+
log[:order] = @order if @order
|
421
668
|
log
|
422
669
|
end
|
423
670
|
|
@@ -429,16 +676,25 @@ module Mongo
|
|
429
676
|
spec['$hint'] = @hint if @hint && @hint.length > 0
|
430
677
|
spec['$explain'] = true if @explain
|
431
678
|
spec['$snapshot'] = true if @snapshot
|
679
|
+
spec['$maxScan'] = @max_scan if @max_scan
|
680
|
+
spec['$returnKey'] = true if @return_key
|
681
|
+
spec['$showDiskLoc'] = true if @show_disk_loc
|
682
|
+
spec['$comment'] = @comment if @comment
|
683
|
+
spec['$maxTimeMS'] = @max_time_ms if @max_time_ms
|
684
|
+
if needs_read_pref?
|
685
|
+
read_pref = Mongo::ReadPreference::mongos(@read, @tag_sets)
|
686
|
+
spec['$readPreference'] = read_pref if read_pref
|
687
|
+
end
|
432
688
|
spec
|
433
689
|
end
|
434
690
|
|
435
|
-
|
436
|
-
|
437
|
-
@order || @explain || @hint || @snapshot
|
691
|
+
def needs_read_pref?
|
692
|
+
@connection.mongos? && @read != :primary
|
438
693
|
end
|
439
694
|
|
440
|
-
def
|
441
|
-
|
695
|
+
def query_contains_special_fields?
|
696
|
+
@order || @explain || @hint || @snapshot || @show_disk_loc ||
|
697
|
+
@max_scan || @return_key || @comment || @max_time_ms || needs_read_pref?
|
442
698
|
end
|
443
699
|
|
444
700
|
def close_cursor_if_query_complete
|
@@ -447,10 +703,40 @@ module Mongo
|
|
447
703
|
end
|
448
704
|
end
|
449
705
|
|
706
|
+
# Check whether the exhaust option is set
|
707
|
+
#
|
708
|
+
# @return [true, false] The state of the exhaust flag.
|
709
|
+
def exhaust?(opts = options)
|
710
|
+
!(opts & OP_QUERY_EXHAUST).zero?
|
711
|
+
end
|
712
|
+
|
450
713
|
def check_modifiable
|
451
714
|
if @query_run || @closed
|
452
715
|
raise InvalidOperation, "Cannot modify the query once it has been run or closed."
|
453
716
|
end
|
454
717
|
end
|
718
|
+
|
719
|
+
def check_command_cursor
|
720
|
+
if @command_cursor
|
721
|
+
raise InvalidOperation, "Cannot call #{caller.first} on command cursors"
|
722
|
+
end
|
723
|
+
end
|
724
|
+
|
725
|
+
def compile_regex?
|
726
|
+
@compile_regex
|
727
|
+
end
|
728
|
+
|
729
|
+
def generate_index_name(spec)
|
730
|
+
indexes = []
|
731
|
+
spec.each_pair do |field, type|
|
732
|
+
indexes.push("#{field}_#{type}")
|
733
|
+
end
|
734
|
+
indexes.join("_")
|
735
|
+
end
|
736
|
+
|
737
|
+
def pin_pool?(response)
|
738
|
+
( response && (response['cursor'] || response['cursors']) ) ||
|
739
|
+
( !@socket && !@command )
|
740
|
+
end
|
455
741
|
end
|
456
742
|
end
|