mongo 0.15.1 → 0.16
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE.txt +202 -0
- data/README.rdoc +15 -2
- data/Rakefile +17 -3
- data/bin/autoreconnect.rb +26 -0
- data/bin/fail_if_no_c.rb +11 -0
- data/lib/mongo.rb +9 -2
- data/lib/mongo/admin.rb +1 -1
- data/lib/mongo/collection.rb +86 -25
- data/lib/mongo/connection.rb +11 -0
- data/lib/mongo/constants.rb +15 -0
- data/lib/mongo/cursor.rb +176 -36
- data/lib/mongo/db.rb +84 -97
- data/lib/mongo/errors.rb +7 -1
- data/lib/mongo/types/objectid.rb +5 -0
- data/lib/mongo/util/byte_buffer.rb +12 -0
- data/lib/mongo/util/server_version.rb +69 -0
- data/lib/mongo/{message.rb → util/support.rb} +7 -4
- data/mongo-ruby-driver.gemspec +9 -86
- data/test/test_admin.rb +2 -2
- data/test/test_bson.rb +14 -0
- data/test/test_byte_buffer.rb +14 -0
- data/test/test_chunk.rb +4 -4
- data/test/test_collection.rb +107 -17
- data/test/test_connection.rb +13 -4
- data/test/test_cursor.rb +17 -14
- data/test/test_db.rb +7 -7
- data/test/test_db_api.rb +31 -19
- data/test/test_db_connection.rb +1 -1
- data/test/test_grid_store.rb +8 -8
- data/test/test_helper.rb +25 -0
- data/test/test_objectid.rb +11 -1
- data/test/test_slave_connection.rb +37 -0
- data/test/test_threading.rb +1 -1
- data/test/unit/cursor_test.rb +122 -0
- metadata +26 -46
- data/bin/mongo_console +0 -21
- data/bin/run_test_script +0 -19
- data/bin/standard_benchmark +0 -109
- data/lib/mongo/message/get_more_message.rb +0 -32
- data/lib/mongo/message/insert_message.rb +0 -37
- data/lib/mongo/message/kill_cursors_message.rb +0 -31
- data/lib/mongo/message/message.rb +0 -80
- data/lib/mongo/message/message_header.rb +0 -45
- data/lib/mongo/message/msg_message.rb +0 -29
- data/lib/mongo/message/opcodes.rb +0 -27
- data/lib/mongo/message/query_message.rb +0 -69
- data/lib/mongo/message/remove_message.rb +0 -37
- data/lib/mongo/message/update_message.rb +0 -38
- data/lib/mongo/query.rb +0 -118
- data/test/mongo-qa/admin +0 -26
- data/test/mongo-qa/capped +0 -22
- data/test/mongo-qa/count1 +0 -18
- data/test/mongo-qa/dbs +0 -22
- data/test/mongo-qa/find +0 -10
- data/test/mongo-qa/find1 +0 -15
- data/test/mongo-qa/gridfs_in +0 -16
- data/test/mongo-qa/gridfs_out +0 -17
- data/test/mongo-qa/indices +0 -49
- data/test/mongo-qa/remove +0 -25
- data/test/mongo-qa/stress1 +0 -35
- data/test/mongo-qa/test1 +0 -11
- data/test/mongo-qa/update +0 -18
- data/test/test_message.rb +0 -35
data/lib/mongo/connection.rb
CHANGED
@@ -120,6 +120,17 @@ module Mongo
|
|
120
120
|
single_db_command(name, :dropDatabase => 1)
|
121
121
|
end
|
122
122
|
|
123
|
+
# Return the build information for the current connection.
|
124
|
+
def server_info
|
125
|
+
db("admin").db_command(:buildinfo => 1)
|
126
|
+
end
|
127
|
+
|
128
|
+
# Returns the build version of the current server, using
|
129
|
+
# a ServerVersion object for comparability.
|
130
|
+
def server_version
|
131
|
+
ServerVersion.new(server_info["version"])
|
132
|
+
end
|
133
|
+
|
123
134
|
protected
|
124
135
|
|
125
136
|
# Turns an array containing a host name string and a
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Mongo
|
2
|
+
module Constants
|
3
|
+
OP_REPLY = 1
|
4
|
+
OP_MSG = 1000
|
5
|
+
OP_UPDATE = 2001
|
6
|
+
OP_INSERT = 2002
|
7
|
+
OP_QUERY = 2004
|
8
|
+
OP_GET_MORE = 2005
|
9
|
+
OP_DELETE = 2006
|
10
|
+
OP_KILL_CURSORS = 2007
|
11
|
+
|
12
|
+
OP_QUERY_SLAVE_OK = 4
|
13
|
+
OP_QUERY_NO_CURSOR_TIMEOUT = 16
|
14
|
+
end
|
15
|
+
end
|
data/lib/mongo/cursor.rb
CHANGED
@@ -12,7 +12,6 @@
|
|
12
12
|
# See the License for the specific language governing permissions and
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
|
-
require 'mongo/message'
|
16
15
|
require 'mongo/util/byte_buffer'
|
17
16
|
require 'mongo/util/bson'
|
18
17
|
|
@@ -20,18 +19,35 @@ module Mongo
|
|
20
19
|
|
21
20
|
# A cursor over query results. Returned objects are hashes.
|
22
21
|
class Cursor
|
22
|
+
include Mongo::Conversions
|
23
23
|
|
24
24
|
include Enumerable
|
25
25
|
|
26
26
|
RESPONSE_HEADER_SIZE = 20
|
27
27
|
|
28
|
-
attr_reader :
|
28
|
+
attr_reader :collection, :selector, :admin, :fields,
|
29
|
+
:order, :hint, :snapshot, :timeout,
|
30
|
+
:full_collection_name
|
29
31
|
|
30
32
|
# Create a new cursor.
|
31
33
|
#
|
32
34
|
# Should not be called directly by application developers.
|
33
|
-
def initialize(
|
34
|
-
@db
|
35
|
+
def initialize(collection, options={})
|
36
|
+
@db = collection.db
|
37
|
+
@collection = collection
|
38
|
+
|
39
|
+
@selector = convert_selector_for_query(options[:selector])
|
40
|
+
@fields = convert_fields_for_query(options[:fields])
|
41
|
+
@admin = options[:admin] || false
|
42
|
+
@skip = options[:skip] || 0
|
43
|
+
@limit = options[:limit] || 0
|
44
|
+
@order = options[:order]
|
45
|
+
@hint = options[:hint]
|
46
|
+
@snapshot = options[:snapshot]
|
47
|
+
@timeout = options[:timeout] || false
|
48
|
+
@explain = options[:explain]
|
49
|
+
|
50
|
+
@full_collection_name = "#{@collection.db.name}.#{@collection.name}"
|
35
51
|
@cache = []
|
36
52
|
@closed = false
|
37
53
|
@query_run = false
|
@@ -64,9 +80,9 @@ module Mongo
|
|
64
80
|
# not take limit and skip into account. Raises OperationFailure on a
|
65
81
|
# database error.
|
66
82
|
def count
|
67
|
-
command = OrderedHash["count",
|
68
|
-
"query",
|
69
|
-
"fields", @
|
83
|
+
command = OrderedHash["count", @collection.name,
|
84
|
+
"query", @selector,
|
85
|
+
"fields", @fields]
|
70
86
|
response = @db.db_command(command)
|
71
87
|
return response['n'].to_i if response['ok'] == 1
|
72
88
|
return 0 if response['errmsg'] == "ns missing"
|
@@ -93,35 +109,39 @@ module Mongo
|
|
93
109
|
order = key_or_list
|
94
110
|
end
|
95
111
|
|
96
|
-
@
|
112
|
+
@order = order
|
97
113
|
self
|
98
114
|
end
|
99
115
|
|
100
116
|
# Limits the number of results to be returned by this cursor.
|
117
|
+
# Returns the current number_to_return if no parameter is given.
|
101
118
|
#
|
102
119
|
# Raises InvalidOperation if this cursor has already been used.
|
103
120
|
#
|
104
121
|
# This method overrides any limit specified in the Collection#find method,
|
105
122
|
# and only the last limit applied has an effect.
|
106
|
-
def limit(number_to_return)
|
123
|
+
def limit(number_to_return=nil)
|
124
|
+
return @limit unless number_to_return
|
107
125
|
check_modifiable
|
108
126
|
raise ArgumentError, "limit requires an integer" unless number_to_return.is_a? Integer
|
109
127
|
|
110
|
-
@
|
128
|
+
@limit = number_to_return
|
111
129
|
self
|
112
130
|
end
|
113
131
|
|
114
132
|
# Skips the first +number_to_skip+ results of this cursor.
|
115
|
-
#
|
133
|
+
# Returns the current number_to_skip if no parameter is given.
|
134
|
+
#
|
116
135
|
# Raises InvalidOperation if this cursor has already been used.
|
117
136
|
#
|
118
137
|
# This method overrides any skip specified in the Collection#find method,
|
119
138
|
# and only the last skip applied has an effect.
|
120
|
-
def skip(number_to_skip)
|
139
|
+
def skip(number_to_skip=nil)
|
140
|
+
return @skip unless number_to_skip
|
121
141
|
check_modifiable
|
122
142
|
raise ArgumentError, "skip requires an integer" unless number_to_skip.is_a? Integer
|
123
143
|
|
124
|
-
@
|
144
|
+
@skip = number_to_skip
|
125
145
|
self
|
126
146
|
end
|
127
147
|
|
@@ -131,7 +151,7 @@ module Mongo
|
|
131
151
|
# Iterating over an entire cursor will close it.
|
132
152
|
def each
|
133
153
|
num_returned = 0
|
134
|
-
while more? && (@
|
154
|
+
while more? && (@limit <= 0 || num_returned < @limit)
|
135
155
|
yield next_object()
|
136
156
|
num_returned += 1
|
137
157
|
end
|
@@ -148,7 +168,7 @@ module Mongo
|
|
148
168
|
raise InvalidOperation, "can't call Cursor#to_a on a used cursor" if @query_run
|
149
169
|
rows = []
|
150
170
|
num_returned = 0
|
151
|
-
while more? && (@
|
171
|
+
while more? && (@limit <= 0 || num_returned < @limit)
|
152
172
|
rows << next_object()
|
153
173
|
num_returned += 1
|
154
174
|
end
|
@@ -157,39 +177,96 @@ module Mongo
|
|
157
177
|
|
158
178
|
# Returns an explain plan record for this cursor.
|
159
179
|
def explain
|
160
|
-
|
161
|
-
@query.explain = true
|
162
|
-
@query.number_to_return = -limit.abs
|
163
|
-
|
164
|
-
c = Cursor.new(@db, @collection, @query)
|
180
|
+
c = Cursor.new(@collection, query_options_hash.merge(:limit => -@limit.abs, :explain => true))
|
165
181
|
explanation = c.next_object
|
166
182
|
c.close
|
167
183
|
|
168
|
-
@query.explain = false
|
169
|
-
@query.number_to_return = limit
|
170
184
|
explanation
|
171
185
|
end
|
172
186
|
|
173
187
|
# Close the cursor.
|
174
188
|
#
|
175
|
-
# Note: if a cursor is read until exhausted (read until OP_QUERY or
|
176
|
-
# OP_GETMORE returns zero for the cursor id), there is no need to
|
189
|
+
# Note: if a cursor is read until exhausted (read until Mongo::Constants::OP_QUERY or
|
190
|
+
# Mongo::Constants::OP_GETMORE returns zero for the cursor id), there is no need to
|
177
191
|
# close it by calling this method.
|
178
192
|
#
|
179
193
|
# Collection#find takes an optional block argument which can be used to
|
180
194
|
# ensure that your cursors get closed. See the documentation for
|
181
195
|
# Collection#find for details.
|
182
196
|
def close
|
183
|
-
|
197
|
+
if @cursor_id
|
198
|
+
message = ByteBuffer.new
|
199
|
+
message.put_int(0)
|
200
|
+
message.put_int(1)
|
201
|
+
message.put_long(@cursor_id)
|
202
|
+
@db.send_message_with_operation(Mongo::Constants::OP_KILL_CURSORS, message)
|
203
|
+
end
|
184
204
|
@cursor_id = 0
|
185
|
-
@closed
|
205
|
+
@closed = true
|
186
206
|
end
|
187
207
|
|
188
208
|
# Returns true if this cursor is closed, false otherwise.
|
189
209
|
def closed?; @closed; end
|
190
210
|
|
211
|
+
# Returns an integer indicating which query options have been selected.
|
212
|
+
# See http://www.mongodb.org/display/DOCS/Mongo+Wire+Protocol#MongoWireProtocol-Mongo::Constants::OPQUERY
|
213
|
+
def query_opts
|
214
|
+
timeout = @timeout ? 0 : Mongo::Constants::OP_QUERY_NO_CURSOR_TIMEOUT
|
215
|
+
slave_ok = @db.slave_ok? ? Mongo::Constants::OP_QUERY_SLAVE_OK : 0
|
216
|
+
slave_ok + timeout
|
217
|
+
end
|
218
|
+
|
219
|
+
# Returns the query options set on this Cursor.
|
220
|
+
def query_options_hash
|
221
|
+
{ :selector => @selector,
|
222
|
+
:fields => @fields,
|
223
|
+
:admin => @admin,
|
224
|
+
:skip => @skip_num,
|
225
|
+
:limit => @limit_num,
|
226
|
+
:order => @order,
|
227
|
+
:hint => @hint,
|
228
|
+
:snapshot => @snapshot,
|
229
|
+
:timeout => @timeout }
|
230
|
+
end
|
231
|
+
|
191
232
|
private
|
192
233
|
|
234
|
+
# Converts the +:fields+ parameter from a single field name or an array
|
235
|
+
# of fields names to a hash, with the field names for keys and '1' for each
|
236
|
+
# value.
|
237
|
+
def convert_fields_for_query(fields)
|
238
|
+
case fields
|
239
|
+
when String, Symbol
|
240
|
+
{fields => 1}
|
241
|
+
when Array
|
242
|
+
return nil if fields.length.zero?
|
243
|
+
returning({}) do |hash|
|
244
|
+
fields.each { |field| hash[field] = 1 }
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
# Set query selector hash. If the selector is a Code or String object,
|
250
|
+
# the selector will be used in a $where clause.
|
251
|
+
# See http://www.mongodb.org/display/DOCS/Server-side+Code+Execution
|
252
|
+
def convert_selector_for_query(selector)
|
253
|
+
case selector
|
254
|
+
when Hash
|
255
|
+
selector
|
256
|
+
when nil
|
257
|
+
{}
|
258
|
+
when String
|
259
|
+
{"$where" => Code.new(selector)}
|
260
|
+
when Code
|
261
|
+
{"$where" => selector}
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
# Returns true if the query contains order, explain, hint, or snapshot.
|
266
|
+
def query_contains_special_fields?
|
267
|
+
@order || @explain || @hint || @snapshot
|
268
|
+
end
|
269
|
+
|
193
270
|
def read_all
|
194
271
|
read_message_header
|
195
272
|
read_response_header
|
@@ -203,7 +280,16 @@ module Mongo
|
|
203
280
|
end
|
204
281
|
|
205
282
|
def read_message_header
|
206
|
-
|
283
|
+
message = ByteBuffer.new
|
284
|
+
message.put_array(@db.receive_full(16).unpack("C*"))
|
285
|
+
unless message.size == 16 #HEADER_SIZE
|
286
|
+
raise "Short read for DB response header: expected #{16} bytes, saw #{message.size}"
|
287
|
+
end
|
288
|
+
message.rewind
|
289
|
+
size = message.get_int
|
290
|
+
request_id = message.get_int
|
291
|
+
response_to = message.get_int
|
292
|
+
op = message.get_int
|
207
293
|
end
|
208
294
|
|
209
295
|
def read_response_header
|
@@ -220,9 +306,6 @@ module Mongo
|
|
220
306
|
else
|
221
307
|
@n_received = @n_remaining
|
222
308
|
end
|
223
|
-
if @query.number_to_return > 0 and @n_received >= @query.number_to_return
|
224
|
-
close()
|
225
|
-
end
|
226
309
|
end
|
227
310
|
|
228
311
|
def num_remaining
|
@@ -231,7 +314,7 @@ module Mongo
|
|
231
314
|
end
|
232
315
|
|
233
316
|
# Internal method, not for general use. Return +true+ if there are
|
234
|
-
# more records to retrieve.
|
317
|
+
# more records to retrieve. This methods does not check @limit;
|
235
318
|
# #each is responsible for doing that.
|
236
319
|
def more?
|
237
320
|
num_remaining > 0
|
@@ -246,13 +329,25 @@ module Mongo
|
|
246
329
|
end
|
247
330
|
|
248
331
|
def refill_via_get_more
|
249
|
-
if send_query_if_needed
|
250
|
-
return
|
251
|
-
end
|
332
|
+
return if send_query_if_needed || @cursor_id.zero?
|
252
333
|
@db._synchronize {
|
253
|
-
|
334
|
+
message = ByteBuffer.new
|
335
|
+
# Reserved.
|
336
|
+
message.put_int(0)
|
337
|
+
|
338
|
+
# DB name.
|
339
|
+
db_name = @admin ? 'admin' : @db.name
|
340
|
+
BSON.serialize_cstr(message, "#{db_name}.#{@collection.name}")
|
341
|
+
|
342
|
+
# Number of results to return; db decides for now.
|
343
|
+
message.put_int(0)
|
344
|
+
|
345
|
+
# Cursor id.
|
346
|
+
message.put_long(@cursor_id)
|
347
|
+
@db.send_message_with_operation_without_synchronize(Mongo::Constants::OP_GET_MORE, message)
|
254
348
|
read_all
|
255
349
|
}
|
350
|
+
close_cursor_if_query_complete
|
256
351
|
end
|
257
352
|
|
258
353
|
def object_from_stream
|
@@ -271,19 +366,64 @@ module Mongo
|
|
271
366
|
if @query_run
|
272
367
|
false
|
273
368
|
else
|
369
|
+
message = construct_query_message(@query)
|
274
370
|
@db._synchronize {
|
275
|
-
@db.
|
371
|
+
@db.send_message_with_operation_without_synchronize(Mongo::Constants::OP_QUERY, message)
|
276
372
|
@query_run = true
|
277
373
|
read_all
|
278
374
|
}
|
375
|
+
close_cursor_if_query_complete
|
279
376
|
true
|
280
377
|
end
|
281
378
|
end
|
282
379
|
|
380
|
+
def construct_query_message(query)
|
381
|
+
message = ByteBuffer.new
|
382
|
+
message.put_int(query_opts)
|
383
|
+
db_name = @admin ? 'admin' : @db.name
|
384
|
+
BSON.serialize_cstr(message, "#{db_name}.#{@collection.name}")
|
385
|
+
message.put_int(@skip)
|
386
|
+
message.put_int(@limit)
|
387
|
+
selector = @selector
|
388
|
+
if query_contains_special_fields?
|
389
|
+
selector = selector_with_special_query_fields
|
390
|
+
end
|
391
|
+
message.put_array(BSON.new.serialize(selector).to_a)
|
392
|
+
message.put_array(BSON.new.serialize(@fields).to_a) if @fields
|
393
|
+
message
|
394
|
+
end
|
395
|
+
|
396
|
+
def selector_with_special_query_fields
|
397
|
+
sel = OrderedHash.new
|
398
|
+
sel['query'] = @selector
|
399
|
+
sel['orderby'] = formatted_order_clause if @order
|
400
|
+
sel['$hint'] = @hint if @hint && @hint.length > 0
|
401
|
+
sel['$explain'] = true if @explain
|
402
|
+
sel['$snapshot'] = true if @snapshot
|
403
|
+
sel
|
404
|
+
end
|
405
|
+
|
406
|
+
def formatted_order_clause
|
407
|
+
case @order
|
408
|
+
when String then string_as_sort_parameters(@order)
|
409
|
+
when Symbol then symbol_as_sort_parameters(@order)
|
410
|
+
when Array then array_as_sort_parameters(@order)
|
411
|
+
when Hash # Should be an ordered hash, but this message doesn't care
|
412
|
+
warn_if_deprecated(@order)
|
413
|
+
@order
|
414
|
+
else
|
415
|
+
raise InvalidSortValueError, "Illegal order_by, '#{@order.class.name}'; must be String, Array, Hash, or OrderedHash"
|
416
|
+
end
|
417
|
+
end
|
418
|
+
|
283
419
|
def to_s
|
284
420
|
"DBResponse(flags=#@result_flags, cursor_id=#@cursor_id, start=#@starting_from)"
|
285
421
|
end
|
286
422
|
|
423
|
+
def close_cursor_if_query_complete
|
424
|
+
close if @limit > 0 && @n_received >= @limit
|
425
|
+
end
|
426
|
+
|
287
427
|
def check_modifiable
|
288
428
|
if @query_run || @closed
|
289
429
|
raise InvalidOperation, "Cannot modify the query once it has been run or closed."
|
data/lib/mongo/db.rb
CHANGED
@@ -16,10 +16,8 @@
|
|
16
16
|
|
17
17
|
require 'socket'
|
18
18
|
require 'digest/md5'
|
19
|
-
require '
|
19
|
+
require 'thread'
|
20
20
|
require 'mongo/collection'
|
21
|
-
require 'mongo/message'
|
22
|
-
require 'mongo/query'
|
23
21
|
require 'mongo/util/ordered_hash.rb'
|
24
22
|
require 'mongo/admin'
|
25
23
|
|
@@ -34,6 +32,9 @@ module Mongo
|
|
34
32
|
SYSTEM_USER_COLLECTION = "system.users"
|
35
33
|
SYSTEM_COMMAND_COLLECTION = "$cmd"
|
36
34
|
|
35
|
+
# Counter for generating unique request ids.
|
36
|
+
@@current_request_id = 0
|
37
|
+
|
37
38
|
# Strict mode enforces collection existence checks. When +true+,
|
38
39
|
# asking for a collection that does not exist or trying to create a
|
39
40
|
# collection that already exists raises an error.
|
@@ -137,8 +138,7 @@ module Mongo
|
|
137
138
|
@pk_factory = options[:pk]
|
138
139
|
@slave_ok = options[:slave_ok] && @nodes.length == 1 # only OK if one node
|
139
140
|
@auto_reconnect = options[:auto_reconnect]
|
140
|
-
@semaphore =
|
141
|
-
@semaphore.extend Mutex_m
|
141
|
+
@semaphore = Mutex.new
|
142
142
|
@socket = nil
|
143
143
|
@logger = options[:logger]
|
144
144
|
connect_to_master
|
@@ -160,6 +160,9 @@ module Mongo
|
|
160
160
|
is_master = master?
|
161
161
|
@semaphore.lock if semaphore_is_locked
|
162
162
|
|
163
|
+
if !@slave_ok && !is_master
|
164
|
+
raise ConfigurationError, "Trying to connect directly to slave; if this is what you want, specify :slave_ok => true."
|
165
|
+
end
|
163
166
|
@slave_ok || is_master
|
164
167
|
rescue SocketError, SystemCallError, IOError => ex
|
165
168
|
close if @socket
|
@@ -211,8 +214,8 @@ module Mongo
|
|
211
214
|
# specified, an array of length 1 is returned.
|
212
215
|
def collections_info(coll_name=nil)
|
213
216
|
selector = {}
|
214
|
-
selector[:name] =
|
215
|
-
|
217
|
+
selector[:name] = full_collection_name(coll_name) if coll_name
|
218
|
+
Cursor.new(Collection.new(self, SYSTEM_NAMESPACE_COLLECTION), :selector => selector)
|
216
219
|
end
|
217
220
|
|
218
221
|
# Create a collection. If +strict+ is false, will return existing or
|
@@ -243,7 +246,7 @@ module Mongo
|
|
243
246
|
oh[:create] = name
|
244
247
|
doc = db_command(oh.merge(options || {}))
|
245
248
|
ok = doc['ok']
|
246
|
-
return Collection.new(self, name) if ok.kind_of?(Numeric) && (ok.to_i == 1 || ok.to_i == 0)
|
249
|
+
return Collection.new(self, name, @pk_factory) if ok.kind_of?(Numeric) && (ok.to_i == 1 || ok.to_i == 0)
|
247
250
|
raise "Error creating collection: #{doc.inspect}"
|
248
251
|
end
|
249
252
|
|
@@ -255,7 +258,7 @@ module Mongo
|
|
255
258
|
# new collection. If +strict+ is true, will raise an error if
|
256
259
|
# collection +name+ does not already exists.
|
257
260
|
def collection(name)
|
258
|
-
return Collection.new(self, name) if !strict? || collection_names.include?(name)
|
261
|
+
return Collection.new(self, name, @pk_factory) if !strict? || collection_names.include?(name)
|
259
262
|
raise "Collection #{name} doesn't exist. Currently in strict mode."
|
260
263
|
end
|
261
264
|
alias_method :[], :collection
|
@@ -353,11 +356,6 @@ module Mongo
|
|
353
356
|
message
|
354
357
|
end
|
355
358
|
|
356
|
-
# Send a MsgMessage to the database.
|
357
|
-
def send_message(msg)
|
358
|
-
send_to_db(MsgMessage.new(msg))
|
359
|
-
end
|
360
|
-
|
361
359
|
# Returns a Cursor over the query results.
|
362
360
|
#
|
363
361
|
# Note that the query gets sent lazily; the cursor calls
|
@@ -367,38 +365,6 @@ module Mongo
|
|
367
365
|
Cursor.new(self, collection, query, admin)
|
368
366
|
end
|
369
367
|
|
370
|
-
# Used by a Cursor to lazily send the query to the database.
|
371
|
-
def send_query_message(query_message)
|
372
|
-
send_to_db(query_message)
|
373
|
-
end
|
374
|
-
|
375
|
-
# Remove the records that match +selector+ from +collection_name+.
|
376
|
-
# Normally called by Collection#remove or Collection#clear.
|
377
|
-
def remove_from_db(collection_name, selector)
|
378
|
-
_synchronize {
|
379
|
-
send_to_db(RemoveMessage.new(@name, collection_name, selector))
|
380
|
-
}
|
381
|
-
end
|
382
|
-
|
383
|
-
# Update records in +collection_name+ that match +selector+ by
|
384
|
-
# applying +obj+ as an update. Normally called by Collection#replace.
|
385
|
-
def replace_in_db(collection_name, selector, obj)
|
386
|
-
_synchronize {
|
387
|
-
send_to_db(UpdateMessage.new(@name, collection_name, selector, obj, false))
|
388
|
-
}
|
389
|
-
end
|
390
|
-
|
391
|
-
# Update records in +collection_name+ that match +selector+ by
|
392
|
-
# applying +obj+ as an update. If no match, inserts (???). Normally
|
393
|
-
# called by Collection#repsert.
|
394
|
-
def repsert_in_db(collection_name, selector, obj)
|
395
|
-
_synchronize {
|
396
|
-
obj = @pk_factory.create_pk(obj) if @pk_factory
|
397
|
-
send_to_db(UpdateMessage.new(@name, collection_name, selector, obj, true))
|
398
|
-
obj
|
399
|
-
}
|
400
|
-
end
|
401
|
-
|
402
368
|
# Dereference a DBRef, getting the document it points to.
|
403
369
|
def dereference(dbref)
|
404
370
|
collection(dbref.namespace).find_one("_id" => dbref.object_id)
|
@@ -447,9 +413,9 @@ module Mongo
|
|
447
413
|
# the values are lists of [key, direction] pairs specifying the index
|
448
414
|
# (as passed to Collection#create_index).
|
449
415
|
def index_information(collection_name)
|
450
|
-
sel = {:ns =>
|
416
|
+
sel = {:ns => full_collection_name(collection_name)}
|
451
417
|
info = {}
|
452
|
-
|
418
|
+
Cursor.new(Collection.new(self, SYSTEM_INDEX_COLLECTION), :selector => sel).each { |index|
|
453
419
|
info[index['name']] = index['key'].to_a
|
454
420
|
}
|
455
421
|
info
|
@@ -462,42 +428,7 @@ module Mongo
|
|
462
428
|
# by Collection#create_index. If +unique+ is true the index will
|
463
429
|
# enforce a uniqueness constraint.
|
464
430
|
def create_index(collection_name, field_or_spec, unique=false)
|
465
|
-
|
466
|
-
if field_or_spec.is_a?(String) || field_or_spec.is_a?(Symbol)
|
467
|
-
field_h[field_or_spec.to_s] = 1
|
468
|
-
else
|
469
|
-
field_or_spec.each { |f| field_h[f[0].to_s] = f[1] }
|
470
|
-
end
|
471
|
-
name = gen_index_name(field_h)
|
472
|
-
sel = {
|
473
|
-
:name => name,
|
474
|
-
:ns => full_coll_name(collection_name),
|
475
|
-
:key => field_h,
|
476
|
-
:unique => unique
|
477
|
-
}
|
478
|
-
_synchronize {
|
479
|
-
send_to_db(InsertMessage.new(@name, SYSTEM_INDEX_COLLECTION, false, sel))
|
480
|
-
}
|
481
|
-
name
|
482
|
-
end
|
483
|
-
|
484
|
-
# Insert +objects+ into +collection_name+. Normally called by
|
485
|
-
# Collection#insert. Returns a new array containing the _ids
|
486
|
-
# of the inserted documents.
|
487
|
-
def insert_into_db(collection_name, objects)
|
488
|
-
_synchronize {
|
489
|
-
if @pk_factory
|
490
|
-
objects.collect! { |o|
|
491
|
-
@pk_factory.create_pk(o)
|
492
|
-
}
|
493
|
-
else
|
494
|
-
objects = objects.collect do |o|
|
495
|
-
o[:_id] || o['_id'] ? o : o.merge!(:_id => ObjectID.new)
|
496
|
-
end
|
497
|
-
end
|
498
|
-
send_to_db(InsertMessage.new(@name, collection_name, true, *objects))
|
499
|
-
objects.collect { |o| o[:_id] || o['_id'] }
|
500
|
-
}
|
431
|
+
self.collection(collection_name).create_index(field_or_spec, unique)
|
501
432
|
end
|
502
433
|
|
503
434
|
def send_to_db(message)
|
@@ -512,8 +443,43 @@ module Mongo
|
|
512
443
|
end
|
513
444
|
end
|
514
445
|
|
515
|
-
|
516
|
-
|
446
|
+
# Sends a message to MongoDB.
|
447
|
+
#
|
448
|
+
# Takes a MongoDB opcode, +operation+, and a message of class ByteBuffer,
|
449
|
+
# +message+, and sends the message to the databse, adding the necessary headers.
|
450
|
+
def send_message_with_operation(operation, message)
|
451
|
+
@semaphore.synchronize do
|
452
|
+
connect_to_master if !connected? && @auto_reconnect
|
453
|
+
begin
|
454
|
+
message_with_headers = add_message_headers(operation, message)
|
455
|
+
@logger.debug(" MONGODB #{message}") if @logger
|
456
|
+
@socket.print(message_with_headers.to_s)
|
457
|
+
@socket.flush
|
458
|
+
rescue => ex
|
459
|
+
close
|
460
|
+
raise ex
|
461
|
+
end
|
462
|
+
end
|
463
|
+
end
|
464
|
+
|
465
|
+
def send_message_with_operation_without_synchronize(operation, message)
|
466
|
+
connect_to_master if !connected? && @auto_reconnect
|
467
|
+
begin
|
468
|
+
message_with_headers = add_message_headers(operation, message)
|
469
|
+
@logger.debug(" MONGODB #{operation} #{message}") if @logger
|
470
|
+
@socket.print(message_with_headers.to_s)
|
471
|
+
@socket.flush
|
472
|
+
rescue => ex
|
473
|
+
close
|
474
|
+
raise ex
|
475
|
+
end
|
476
|
+
end
|
477
|
+
|
478
|
+
def receive_message_with_operation(operation, message)
|
479
|
+
@semaphore.synchronize do
|
480
|
+
|
481
|
+
|
482
|
+
end
|
517
483
|
end
|
518
484
|
|
519
485
|
# Return +true+ if +doc+ contains an 'ok' field with the value 1.
|
@@ -532,27 +498,48 @@ module Mongo
|
|
532
498
|
end
|
533
499
|
end
|
534
500
|
|
535
|
-
|
536
|
-
|
537
|
-
query(Collection.new(self, SYSTEM_COMMAND_COLLECTION), q, use_admin_db).next_object
|
501
|
+
cursor = Cursor.new(Collection.new(self, SYSTEM_COMMAND_COLLECTION), :admin => use_admin_db, :limit => -1, :selector => selector)
|
502
|
+
cursor.next_object
|
538
503
|
end
|
539
504
|
|
540
505
|
def _synchronize &block
|
541
506
|
@semaphore.synchronize &block
|
542
507
|
end
|
543
508
|
|
509
|
+
def full_collection_name(collection_name)
|
510
|
+
"#{@name}.#{collection_name}"
|
511
|
+
end
|
512
|
+
|
544
513
|
private
|
545
514
|
|
546
|
-
|
547
|
-
|
515
|
+
# Prepares a message for transmission to MongoDB by
|
516
|
+
# constructing a valid message header.
|
517
|
+
def add_message_headers(operation, message)
|
518
|
+
headers = ByteBuffer.new
|
519
|
+
|
520
|
+
# Message size.
|
521
|
+
headers.put_int(16 + message.size)
|
522
|
+
|
523
|
+
# Unique request id.
|
524
|
+
headers.put_int(get_request_id)
|
525
|
+
|
526
|
+
# Response id.
|
527
|
+
headers.put_int(0)
|
528
|
+
|
529
|
+
# Opcode.
|
530
|
+
headers.put_int(operation)
|
531
|
+
message.prepend!(headers)
|
548
532
|
end
|
549
533
|
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
534
|
+
# Increments and then returns the next available request id.
|
535
|
+
# Note: this method should be called from within a lock.
|
536
|
+
def get_request_id
|
537
|
+
@@current_request_id += 1
|
538
|
+
@@current_request_id
|
539
|
+
end
|
540
|
+
|
541
|
+
def hash_password(username, plaintext)
|
542
|
+
Digest::MD5.hexdigest("#{username}:mongo:#{plaintext}")
|
556
543
|
end
|
557
544
|
end
|
558
545
|
end
|