mongo 0.15.1 → 0.16
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.
- 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
|