mongo 1.0 → 1.1.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.
Files changed (95) hide show
  1. data/LICENSE.txt +1 -13
  2. data/{README.rdoc → README.md} +129 -149
  3. data/Rakefile +94 -58
  4. data/bin/mongo_console +21 -0
  5. data/docs/1.0_UPGRADE.md +21 -0
  6. data/docs/CREDITS.md +123 -0
  7. data/docs/FAQ.md +112 -0
  8. data/docs/GridFS.md +158 -0
  9. data/docs/HISTORY.md +185 -0
  10. data/docs/REPLICA_SETS.md +75 -0
  11. data/docs/TUTORIAL.md +247 -0
  12. data/docs/WRITE_CONCERN.md +28 -0
  13. data/lib/mongo/collection.rb +225 -105
  14. data/lib/mongo/connection.rb +374 -315
  15. data/lib/mongo/cursor.rb +122 -77
  16. data/lib/mongo/db.rb +109 -85
  17. data/lib/mongo/exceptions.rb +6 -0
  18. data/lib/mongo/gridfs/grid.rb +19 -11
  19. data/lib/mongo/gridfs/grid_ext.rb +36 -9
  20. data/lib/mongo/gridfs/grid_file_system.rb +15 -9
  21. data/lib/mongo/gridfs/grid_io.rb +49 -16
  22. data/lib/mongo/gridfs/grid_io_fix.rb +38 -0
  23. data/lib/mongo/repl_set_connection.rb +290 -0
  24. data/lib/mongo/util/conversions.rb +3 -1
  25. data/lib/mongo/util/core_ext.rb +17 -4
  26. data/lib/mongo/util/pool.rb +125 -0
  27. data/lib/mongo/util/server_version.rb +2 -0
  28. data/lib/mongo/util/support.rb +12 -0
  29. data/lib/mongo/util/uri_parser.rb +71 -0
  30. data/lib/mongo.rb +23 -7
  31. data/{mongo-ruby-driver.gemspec → mongo.gemspec} +9 -7
  32. data/test/auxillary/1.4_features.rb +2 -2
  33. data/test/auxillary/authentication_test.rb +1 -1
  34. data/test/auxillary/autoreconnect_test.rb +1 -1
  35. data/test/{slave_connection_test.rb → auxillary/slave_connection_test.rb} +6 -6
  36. data/test/bson/binary_test.rb +15 -0
  37. data/test/bson/bson_test.rb +537 -0
  38. data/test/bson/byte_buffer_test.rb +190 -0
  39. data/test/bson/hash_with_indifferent_access_test.rb +38 -0
  40. data/test/bson/json_test.rb +17 -0
  41. data/test/bson/object_id_test.rb +141 -0
  42. data/test/bson/ordered_hash_test.rb +197 -0
  43. data/test/collection_test.rb +195 -15
  44. data/test/connection_test.rb +93 -56
  45. data/test/conversions_test.rb +1 -1
  46. data/test/cursor_fail_test.rb +75 -0
  47. data/test/cursor_message_test.rb +43 -0
  48. data/test/cursor_test.rb +93 -32
  49. data/test/db_api_test.rb +28 -55
  50. data/test/db_connection_test.rb +2 -3
  51. data/test/db_test.rb +45 -40
  52. data/test/grid_file_system_test.rb +14 -6
  53. data/test/grid_io_test.rb +36 -7
  54. data/test/grid_test.rb +54 -10
  55. data/test/replica_sets/connect_test.rb +84 -0
  56. data/test/replica_sets/count_test.rb +35 -0
  57. data/test/{replica → replica_sets}/insert_test.rb +17 -14
  58. data/test/replica_sets/pooled_insert_test.rb +55 -0
  59. data/test/replica_sets/query_secondaries.rb +80 -0
  60. data/test/replica_sets/query_test.rb +41 -0
  61. data/test/replica_sets/replication_ack_test.rb +64 -0
  62. data/test/replica_sets/rs_test_helper.rb +29 -0
  63. data/test/safe_test.rb +68 -0
  64. data/test/support/hash_with_indifferent_access.rb +199 -0
  65. data/test/support/keys.rb +45 -0
  66. data/test/support_test.rb +19 -0
  67. data/test/test_helper.rb +53 -15
  68. data/test/threading/{test_threading_large_pool.rb → threading_with_large_pool_test.rb} +2 -2
  69. data/test/threading_test.rb +2 -2
  70. data/test/tools/repl_set_manager.rb +241 -0
  71. data/test/tools/test.rb +13 -0
  72. data/test/unit/collection_test.rb +70 -7
  73. data/test/unit/connection_test.rb +18 -39
  74. data/test/unit/cursor_test.rb +7 -8
  75. data/test/unit/db_test.rb +14 -17
  76. data/test/unit/grid_test.rb +49 -0
  77. data/test/unit/pool_test.rb +9 -0
  78. data/test/unit/repl_set_connection_test.rb +82 -0
  79. data/test/unit/safe_test.rb +125 -0
  80. metadata +132 -51
  81. data/bin/bson_benchmark.rb +0 -59
  82. data/bin/fail_if_no_c.rb +0 -11
  83. data/examples/admin.rb +0 -43
  84. data/examples/capped.rb +0 -22
  85. data/examples/cursor.rb +0 -48
  86. data/examples/gridfs.rb +0 -44
  87. data/examples/index_test.rb +0 -126
  88. data/examples/info.rb +0 -31
  89. data/examples/queries.rb +0 -70
  90. data/examples/simple.rb +0 -24
  91. data/examples/strict.rb +0 -35
  92. data/examples/types.rb +0 -36
  93. data/test/replica/count_test.rb +0 -34
  94. data/test/replica/pooled_insert_test.rb +0 -54
  95. data/test/replica/query_test.rb +0 -39
data/lib/mongo/cursor.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # encoding: UTF-8
2
+
1
3
  # Copyright (C) 2008-2010 10gen Inc.
2
4
  #
3
5
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,7 +21,7 @@ module Mongo
19
21
  include Mongo::Conversions
20
22
  include Enumerable
21
23
 
22
- attr_reader :collection, :selector, :admin, :fields,
24
+ attr_reader :collection, :selector, :fields,
23
25
  :order, :hint, :snapshot, :timeout,
24
26
  :full_collection_name
25
27
 
@@ -35,32 +37,39 @@ module Mongo
35
37
  @db = collection.db
36
38
  @collection = collection
37
39
  @connection = @db.connection
40
+ @logger = @connection.logger
38
41
 
39
- @selector = convert_selector_for_query(options[:selector])
42
+ @selector = options[:selector] || {}
40
43
  @fields = convert_fields_for_query(options[:fields])
41
- @admin = options[:admin] || false
42
44
  @skip = options[:skip] || 0
43
45
  @limit = options[:limit] || 0
44
46
  @order = options[:order]
45
47
  @hint = options[:hint]
46
48
  @snapshot = options[:snapshot]
47
- @timeout = options[:timeout] || false
49
+ @timeout = options.has_key?(:timeout) ? options[:timeout] : true
48
50
  @explain = options[:explain]
49
51
  @socket = options[:socket]
50
52
  @tailable = options[:tailable] || false
51
- @batch_size = options[:batch_size] || Mongo::Constants::DEFAULT_BATCH_SIZE
53
+ @closed = false
54
+ @query_run = false
55
+ batch_size(options[:batch_size] || 0)
52
56
 
53
57
  @full_collection_name = "#{@collection.db.name}.#{@collection.name}"
54
- @cache = []
55
- @closed = false
56
- @query_run = false
58
+ @cache = []
59
+ @returned = 0
60
+
61
+ if @collection.name =~ /^\$cmd/ || @collection.name =~ /^system/
62
+ @command = true
63
+ else
64
+ @command = false
65
+ end
57
66
  end
58
67
 
59
68
  # Get the next document specified the cursor options.
60
69
  #
61
70
  # @return [Hash, Nil] the next document or Nil if no documents remain.
62
71
  def next_document
63
- refill_via_get_more if num_remaining == 0
72
+ refresh if @cache.length == 0
64
73
  doc = @cache.shift
65
74
 
66
75
  if doc && doc['$err']
@@ -70,8 +79,8 @@ module Mongo
70
79
  # pair but it has died or something like that) then we close that
71
80
  # connection. The next request will re-open on master server.
72
81
  if err == "not master"
73
- raise ConnectionFailure, err
74
82
  @connection.close
83
+ raise ConnectionFailure, err
75
84
  end
76
85
 
77
86
  raise OperationFailure, err
@@ -80,6 +89,18 @@ module Mongo
80
89
  doc
81
90
  end
82
91
 
92
+ # Reset this cursor on the server. Cursor options, such as the
93
+ # query string and the values for skip and limit, are preserved.
94
+ def rewind!
95
+ close
96
+ @cache.clear
97
+ @cursor_id = nil
98
+ @closed = false
99
+ @query_run = false
100
+ @n_received = nil
101
+ true
102
+ end
103
+
83
104
  # Determine whether this cursor has any remaining results.
84
105
  #
85
106
  # @return [Boolean]
@@ -89,16 +110,23 @@ module Mongo
89
110
 
90
111
  # Get the size of the result set for this query.
91
112
  #
92
- # @return [Integer] the number of objects in the result set for this query. Does
93
- # not take limit and skip into account.
113
+ # @param [Boolean] whether of not to take notice of skip and limit
114
+ #
115
+ # @return [Integer] the number of objects in the result set for this query.
94
116
  #
95
117
  # @raise [OperationFailure] on a database error.
96
- def count
97
- command = OrderedHash["count", @collection.name,
98
- "query", @selector,
99
- "fields", @fields]
118
+ def count(skip_and_limit = false)
119
+ command = BSON::OrderedHash["count", @collection.name, "query", @selector]
120
+
121
+ if skip_and_limit
122
+ command.merge!(BSON::OrderedHash["limit", @limit]) if @limit != 0
123
+ command.merge!(BSON::OrderedHash["skip", @skip]) if @skip != 0
124
+ end
125
+
126
+ command.merge!(BSON::OrderedHash["fields", @fields])
127
+
100
128
  response = @db.command(command)
101
- return response['n'].to_i if response['ok'] == 1
129
+ return response['n'].to_i if Mongo::Support.ok?(response)
102
130
  return 0 if response['errmsg'] == "ns missing"
103
131
  raise OperationFailure, "Count failed: #{response['errmsg']}"
104
132
  end
@@ -141,7 +169,6 @@ module Mongo
141
169
  def limit(number_to_return=nil)
142
170
  return @limit unless number_to_return
143
171
  check_modifiable
144
- raise ArgumentError, "limit requires an integer" unless number_to_return.is_a? Integer
145
172
 
146
173
  @limit = number_to_return
147
174
  self
@@ -159,12 +186,31 @@ module Mongo
159
186
  def skip(number_to_skip=nil)
160
187
  return @skip unless number_to_skip
161
188
  check_modifiable
162
- raise ArgumentError, "skip requires an integer" unless number_to_skip.is_a? Integer
163
189
 
164
190
  @skip = number_to_skip
165
191
  self
166
192
  end
167
193
 
194
+ # Set the batch size for server responses.
195
+ #
196
+ # Note that the batch size will take effect only on queries
197
+ # where the number to be returned is greater than 100.
198
+ #
199
+ # @param [Integer] size either 0 or some integer greater than 1. If 0,
200
+ # the server will determine the batch size.
201
+ #
202
+ # @return [Cursor]
203
+ def batch_size(size=0)
204
+ check_modifiable
205
+ if size < 0 || size == 1
206
+ raise ArgumentError, "Invalid value for batch_size #{size}; must be 0 or > 1."
207
+ else
208
+ @batch_size = size > @limit ? @limit : size
209
+ end
210
+
211
+ self
212
+ end
213
+
168
214
  # Iterate over each document in this cursor, yielding it to the given
169
215
  # block.
170
216
  #
@@ -177,31 +223,28 @@ module Mongo
177
223
  # puts doc['user']
178
224
  # end
179
225
  def each
180
- num_returned = 0
181
- while has_next? && (@limit <= 0 || num_returned < @limit)
182
- yield next_document
183
- num_returned += 1
226
+ #num_returned = 0
227
+ #while has_next? && (@limit <= 0 || num_returned < @limit)
228
+ while doc = next_document
229
+ yield doc #next_document
230
+ #num_returned += 1
184
231
  end
185
232
  end
186
233
 
187
234
  # Receive all the documents from this cursor as an array of hashes.
188
235
  #
189
- # Note: use of this method is discouraged - in most cases, it's much more
236
+ # Notes:
237
+ #
238
+ # If you've already started iterating over the cursor, the array returned
239
+ # by this method contains only the remaining documents. See Cursor#rewind! if you
240
+ # need to reset the cursor.
241
+ #
242
+ # Use of this method is discouraged - in most cases, it's much more
190
243
  # efficient to retrieve documents as you need them by iterating over the cursor.
191
244
  #
192
245
  # @return [Array] an array of documents.
193
- #
194
- # @raise [InvalidOperation] if this cursor has already been used or if
195
- # this method has already been called on the cursor.
196
246
  def to_a
197
- raise InvalidOperation, "can't call Cursor#to_a on a used cursor" if @query_run
198
- rows = []
199
- num_returned = 0
200
- while has_next? && (@limit <= 0 || num_returned < @limit)
201
- rows << next_document
202
- num_returned += 1
203
- end
204
- rows
247
+ super
205
248
  end
206
249
 
207
250
  # Get the explain plan for this cursor.
@@ -228,11 +271,12 @@ module Mongo
228
271
  #
229
272
  # @return [True]
230
273
  def close
231
- if @cursor_id
274
+ if @cursor_id && @cursor_id != 0
232
275
  message = BSON::ByteBuffer.new([0, 0, 0, 0])
233
276
  message.put_int(1)
234
277
  message.put_long(@cursor_id)
235
- @connection.send_message(Mongo::Constants::OP_KILL_CURSORS, message, "cursor.close")
278
+ @logger.debug("MONGODB cursor.close #{@cursor_id}") if @logger
279
+ @connection.send_message(Mongo::Constants::OP_KILL_CURSORS, message, nil)
236
280
  end
237
281
  @cursor_id = 0
238
282
  @closed = true
@@ -250,10 +294,11 @@ module Mongo
250
294
  # @see http://www.mongodb.org/display/DOCS/Mongo+Wire+Protocol#MongoWireProtocol-Mongo::Constants::OPQUERY
251
295
  # The MongoDB wire protocol.
252
296
  def query_opts
253
- timeout = @timeout ? 0 : Mongo::Constants::OP_QUERY_NO_CURSOR_TIMEOUT
254
- slave_ok = @connection.slave_ok? ? Mongo::Constants::OP_QUERY_SLAVE_OK : 0
255
- tailable = @tailable ? Mongo::Constants::OP_QUERY_TAILABLE : 0
256
- slave_ok + timeout + tailable
297
+ opts = 0
298
+ opts |= Mongo::Constants::OP_QUERY_NO_CURSOR_TIMEOUT unless @timeout
299
+ opts |= Mongo::Constants::OP_QUERY_SLAVE_OK if @connection.slave_ok?
300
+ opts |= Mongo::Constants::OP_QUERY_TAILABLE if @tailable
301
+ opts
257
302
  end
258
303
 
259
304
  # Get the query options for this Cursor.
@@ -262,7 +307,6 @@ module Mongo
262
307
  def query_options_hash
263
308
  { :selector => @selector,
264
309
  :fields => @fields,
265
- :admin => @admin,
266
310
  :skip => @skip_num,
267
311
  :limit => @limit_num,
268
312
  :order => @order,
@@ -271,6 +315,12 @@ module Mongo
271
315
  :timeout => @timeout }
272
316
  end
273
317
 
318
+ # Clean output for inspect.
319
+ def inspect
320
+ "<Mongo::Cursor:0x#{object_id.to_s(16)} namespace='#{@db.name}.#{@collection.name}' " +
321
+ "@selector=#{@selector.inspect}>"
322
+ end
323
+
274
324
  private
275
325
 
276
326
  # Convert the +:fields+ parameter from a single field name or an array
@@ -282,50 +332,42 @@ module Mongo
282
332
  {fields => 1}
283
333
  when Array
284
334
  return nil if fields.length.zero?
285
- returning({}) do |hash|
286
- fields.each { |field| hash[field] = 1 }
287
- end
335
+ fields.each_with_object({}) { |field, hash| hash[field] = 1 }
288
336
  when Hash
289
337
  return fields
290
338
  end
291
339
  end
292
340
 
293
- # Set the query selector hash. If the selector is a Code or String object,
294
- # the selector will be used in a $where clause.
295
- # See http://www.mongodb.org/display/DOCS/Server-side+Code+Execution
296
- def convert_selector_for_query(selector)
297
- case selector
298
- when Hash
299
- selector
300
- when nil
301
- {}
302
- when String
303
- {"$where" => Code.new(selector)}
304
- when Code
305
- {"$where" => selector}
306
- end
307
- end
308
-
309
- # Return a number of documents remaining for this cursor.
341
+ # Return the number of documents remaining for this cursor.
310
342
  def num_remaining
311
- refill_via_get_more if @cache.length == 0
343
+ refresh if @cache.length == 0
312
344
  @cache.length
313
345
  end
314
346
 
315
- def refill_via_get_more
347
+ def refresh
316
348
  return if send_initial_query || @cursor_id.zero?
317
349
  message = BSON::ByteBuffer.new([0, 0, 0, 0])
318
350
 
319
351
  # DB name.
320
- db_name = @admin ? 'admin' : @db.name
321
- BSON::BSON_RUBY.serialize_cstr(message, "#{db_name}.#{@collection.name}")
352
+ BSON::BSON_RUBY.serialize_cstr(message, "#{@db.name}.#{@collection.name}")
322
353
 
323
354
  # Number of results to return.
324
- message.put_int(@batch_size)
355
+ if @limit > 0
356
+ limit = @limit - @returned
357
+ if @batch_size > 0
358
+ limit = limit < @batch_size ? limit : @batch_size
359
+ end
360
+ message.put_int(limit)
361
+ else
362
+ message.put_int(@batch_size)
363
+ end
325
364
 
326
365
  # Cursor id.
327
366
  message.put_long(@cursor_id)
328
- results, @n_received, @cursor_id = @connection.receive_message(Mongo::Constants::OP_GET_MORE, message, "cursor.get_more()", @socket)
367
+ @logger.debug("MONGODB cursor.refresh() for cursor #{@cursor_id}") if @logger
368
+ results, @n_received, @cursor_id = @connection.receive_message(
369
+ Mongo::Constants::OP_GET_MORE, message, nil, @socket, @command)
370
+ @returned += @n_received
329
371
  @cache += results
330
372
  close_cursor_if_query_complete
331
373
  end
@@ -336,8 +378,10 @@ module Mongo
336
378
  false
337
379
  else
338
380
  message = construct_query_message
339
- results, @n_received, @cursor_id = @connection.receive_message(Mongo::Constants::OP_QUERY, message,
340
- (query_log_message if @connection.logger), @socket)
381
+ @logger.debug query_log_message if @logger
382
+ results, @n_received, @cursor_id = @connection.receive_message(
383
+ Mongo::Constants::OP_QUERY, message, nil, @socket, @command)
384
+ @returned += @n_received
341
385
  @cache += results
342
386
  @query_run = true
343
387
  close_cursor_if_query_complete
@@ -348,25 +392,24 @@ module Mongo
348
392
  def construct_query_message
349
393
  message = BSON::ByteBuffer.new
350
394
  message.put_int(query_opts)
351
- db_name = @admin ? 'admin' : @db.name
352
- BSON::BSON_RUBY.serialize_cstr(message, "#{db_name}.#{@collection.name}")
395
+ BSON::BSON_RUBY.serialize_cstr(message, "#{@db.name}.#{@collection.name}")
353
396
  message.put_int(@skip)
354
397
  message.put_int(@limit)
355
398
  spec = query_contains_special_fields? ? construct_query_spec : @selector
356
- message.put_array(BSON::BSON_CODER.serialize(spec, false).to_a)
357
- message.put_array(BSON::BSON_CODER.serialize(@fields, false).to_a) if @fields
399
+ message.put_binary(BSON::BSON_CODER.serialize(spec, false).to_s)
400
+ message.put_binary(BSON::BSON_CODER.serialize(@fields, false).to_s) if @fields
358
401
  message
359
402
  end
360
403
 
361
404
  def query_log_message
362
- "#{@admin ? 'admin' : @db.name}['#{@collection.name}'].find(#{@selector.inspect}, #{@fields ? @fields.inspect : '{}'})" +
405
+ "#{@db.name}['#{@collection.name}'].find(#{@selector.inspect}, #{@fields ? @fields.inspect : '{}'})" +
363
406
  "#{@skip != 0 ? ('.skip(' + @skip.to_s + ')') : ''}#{@limit != 0 ? ('.limit(' + @limit.to_s + ')') : ''}" +
364
407
  "#{@order ? ('.sort(' + @order.inspect + ')') : ''}"
365
408
  end
366
409
 
367
410
  def construct_query_spec
368
411
  return @selector if @selector.has_key?('$query')
369
- spec = OrderedHash.new
412
+ spec = BSON::OrderedHash.new
370
413
  spec['$query'] = @selector
371
414
  spec['$orderby'] = Mongo::Support.format_order_clause(@order) if @order
372
415
  spec['$hint'] = @hint if @hint && @hint.length > 0
@@ -385,7 +428,9 @@ module Mongo
385
428
  end
386
429
 
387
430
  def close_cursor_if_query_complete
388
- close if @limit > 0 && @n_received >= @limit
431
+ if @limit > 0 && @returned >= @limit
432
+ close
433
+ end
389
434
  end
390
435
 
391
436
  def check_modifiable