mongo 0.18.1 → 0.18.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +3 -59
- data/Rakefile +7 -13
- data/bin/perf.rb +30 -0
- data/examples/cursor.rb +4 -4
- data/examples/types.rb +1 -1
- data/lib/mongo.rb +2 -2
- data/lib/mongo/admin.rb +1 -2
- data/lib/mongo/collection.rb +85 -45
- data/lib/mongo/connection.rb +55 -102
- data/lib/mongo/constants.rb +3 -3
- data/lib/mongo/cursor.rb +48 -44
- data/lib/mongo/db.rb +8 -8
- data/lib/mongo/errors.rb +8 -2
- data/lib/mongo/gridfs/chunk.rb +0 -1
- data/lib/mongo/gridfs/grid_store.rb +57 -14
- data/lib/mongo/types/code.rb +1 -0
- data/lib/mongo/types/objectid.rb +5 -6
- data/lib/mongo/util/bson_ruby.rb +25 -15
- data/lib/mongo/util/conversions.rb +12 -4
- data/lib/mongo/util/ordered_hash.rb +18 -0
- data/lib/mongo/util/server_version.rb +3 -3
- data/test/replica/count_test.rb +3 -3
- data/test/replica/insert_test.rb +6 -6
- data/test/replica/pooled_insert_test.rb +8 -8
- data/test/replica/query_test.rb +3 -3
- data/test/test_bson.rb +32 -0
- data/test/test_collection.rb +140 -65
- data/test/test_connection.rb +2 -2
- data/test/test_conversions.rb +3 -3
- data/test/test_cursor.rb +44 -20
- data/test/test_db_api.rb +7 -1
- data/test/test_grid_store.rb +16 -2
- data/test/test_objectid.rb +12 -0
- data/test/test_ordered_hash.rb +16 -0
- data/test/test_threading.rb +3 -3
- data/test/threading/test_threading_large_pool.rb +7 -7
- data/test/unit/collection_test.rb +7 -7
- data/test/unit/connection_test.rb +0 -79
- data/test/unit/cursor_test.rb +12 -12
- data/test/unit/db_test.rb +11 -11
- metadata +7 -5
- data/bin/autoreconnect.rb +0 -26
data/README.rdoc
CHANGED
@@ -13,7 +13,7 @@ Here is a quick code sample. See the MongoDB Ruby Tutorial
|
|
13
13
|
@coll = db.collection('test')
|
14
14
|
|
15
15
|
@coll.remove
|
16
|
-
3.times do |i|
|
16
|
+
3.times do |i|
|
17
17
|
@coll.insert({'a' => i+1})
|
18
18
|
end
|
19
19
|
puts "There are #{@coll.count()} records. Here they are:"
|
@@ -254,7 +254,7 @@ Random cursor fun facts:
|
|
254
254
|
|
255
255
|
= Testing
|
256
256
|
|
257
|
-
If you have the source code, you can run the tests. There's a separate rake task for testing with
|
257
|
+
If you have the source code, you can run the tests. There's a separate rake task for testing with
|
258
258
|
the mongo_ext c extension enabled.
|
259
259
|
|
260
260
|
$ rake test:c
|
@@ -329,63 +329,7 @@ See HISTORY.
|
|
329
329
|
|
330
330
|
= Credits
|
331
331
|
|
332
|
-
|
333
|
-
* bin/mongo_console
|
334
|
-
* examples/benchmarks.rb
|
335
|
-
* examples/irb.rb
|
336
|
-
* Modifications to examples/simple.rb
|
337
|
-
* Found plenty of bugs and missing features.
|
338
|
-
* Ruby 1.9 support.
|
339
|
-
* Gem support.
|
340
|
-
* Many other code suggestions and improvements.
|
341
|
-
|
342
|
-
Aman Gupta, aman@tmm1.net
|
343
|
-
* Collection#save
|
344
|
-
|
345
|
-
Jon Crosby, jon@joncrosby.me
|
346
|
-
* Some code clean-up
|
347
|
-
|
348
|
-
John Nunemaker, http://railstips.org
|
349
|
-
* Collection#create_index takes symbols as well as strings
|
350
|
-
* Fix for Collection#save
|
351
|
-
* Add logger convenience methods to connection and database
|
352
|
-
|
353
|
-
David James, djames@sunlightfoundation.com
|
354
|
-
* Fix dates to return as UTC
|
355
|
-
|
356
|
-
Paul Dlug, paul.dlug@gmail.com
|
357
|
-
* Generate _id on the client side if not provided
|
358
|
-
* Collection#insert and Collection#save return _id
|
359
|
-
|
360
|
-
Durran Jordan, durran@gmail.com
|
361
|
-
* DB#collections
|
362
|
-
* Support for specifying sort order as array of [key, direction] pairs
|
363
|
-
|
364
|
-
Cyril Mougel, cyril.mougel@gmail.com
|
365
|
-
* Initial logging support
|
366
|
-
* Test case
|
367
|
-
|
368
|
-
Jack Chen, chendo on github
|
369
|
-
* Test case + fix for deserializing pre-epoch Time instances
|
370
|
-
|
371
|
-
Michael Bernstein, mrb on github
|
372
|
-
* #sort method for Cursor instances
|
373
|
-
|
374
|
-
Paulo Ahahgon, pahagon on github
|
375
|
-
* removed hard limit
|
376
|
-
|
377
|
-
Les Hill, leshill on github
|
378
|
-
* OrderedHash#each returns self
|
379
|
-
|
380
|
-
Sean Cribbs, seancribbs on github
|
381
|
-
* Modify standard_benchmark to allow profiling
|
382
|
-
|
383
|
-
Sunny Hirai
|
384
|
-
* Suggested hashcode fix for Mongo::ObjectID
|
385
|
-
* Noted index ordering bug.
|
386
|
-
|
387
|
-
Christos Trochalakis
|
388
|
-
* Added map/reduce helper
|
332
|
+
See CREDITS.
|
389
333
|
|
390
334
|
= License
|
391
335
|
|
data/Rakefile
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# -*- mode: ruby; -*-
|
1
2
|
require 'rubygems'
|
2
3
|
require 'rubygems/specification'
|
3
4
|
require 'fileutils'
|
@@ -12,9 +13,6 @@ require 'rbconfig'
|
|
12
13
|
include Config
|
13
14
|
ENV['TEST_MODE'] = 'TRUE'
|
14
15
|
|
15
|
-
gem_command = "gem"
|
16
|
-
gem_command = "gem1.9" if $0.match(/1\.9$/) # use gem1.9 if we used rake1.9
|
17
|
-
|
18
16
|
desc "Test the MongoDB Ruby driver."
|
19
17
|
task :test do
|
20
18
|
puts "\nThis option has changed."
|
@@ -95,20 +93,16 @@ namespace :gem do
|
|
95
93
|
|
96
94
|
desc "Install the gem locally"
|
97
95
|
task :install do
|
98
|
-
sh
|
99
|
-
|
100
|
-
|
101
|
-
rm mongo-*.gem
|
102
|
-
EOS
|
96
|
+
sh "gem build mongo-ruby-driver.gemspec"
|
97
|
+
sh "gem install mongo-*.gem"
|
98
|
+
sh "rm mongo-*.gem"
|
103
99
|
end
|
104
100
|
|
105
101
|
desc "Install the optional c extensions"
|
106
102
|
task :install_extensions do
|
107
|
-
sh
|
108
|
-
|
109
|
-
|
110
|
-
rm mongo_ext-*.gem
|
111
|
-
EOS
|
103
|
+
sh "gem build mongo-extensions.gemspec"
|
104
|
+
sh "gem install mongo_ext-*.gem"
|
105
|
+
sh "rm mongo_ext-*.gem"
|
112
106
|
end
|
113
107
|
|
114
108
|
end
|
data/bin/perf.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'rubygems'
|
3
|
+
require 'mongo'
|
4
|
+
require 'benchmark'
|
5
|
+
require 'socket'
|
6
|
+
require 'digest/md5'
|
7
|
+
require 'thread'
|
8
|
+
|
9
|
+
include Mongo
|
10
|
+
|
11
|
+
c = Mongo::Connection.new('localhost', 27017, :pool_size => 10)
|
12
|
+
coll = c['perf']['docs']
|
13
|
+
coll.remove
|
14
|
+
|
15
|
+
doc = {'name' => 'kyle',
|
16
|
+
'languages' => ['ruby', 'javascript', 'c'],
|
17
|
+
'date' => Time.now}
|
18
|
+
|
19
|
+
TRIES = 5000
|
20
|
+
@threads = []
|
21
|
+
100.times do |n|
|
22
|
+
@threads << Thread.new do
|
23
|
+
50.times do |n|
|
24
|
+
doc['n'] = n*n
|
25
|
+
coll.insert(doc)
|
26
|
+
doc.delete(:_id)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
@threads.join
|
data/examples/cursor.rb
CHANGED
@@ -33,14 +33,14 @@ array = cursor.to_a
|
|
33
33
|
cursor.each { |row| pp row }
|
34
34
|
|
35
35
|
# You can get the next object
|
36
|
-
first_object = coll.find().
|
36
|
+
first_object = coll.find().next_document
|
37
37
|
|
38
|
-
#
|
38
|
+
# next_document returns nil if there are no more objects that match
|
39
39
|
cursor = coll.find()
|
40
|
-
obj = cursor.
|
40
|
+
obj = cursor.next_document
|
41
41
|
while obj
|
42
42
|
pp obj
|
43
|
-
obj = cursor.
|
43
|
+
obj = cursor.next_document
|
44
44
|
end
|
45
45
|
|
46
46
|
# Destroy the collection
|
data/examples/types.rb
CHANGED
data/lib/mongo.rb
CHANGED
@@ -4,7 +4,7 @@ module Mongo
|
|
4
4
|
ASCENDING = 1
|
5
5
|
DESCENDING = -1
|
6
6
|
|
7
|
-
VERSION = "0.18.
|
7
|
+
VERSION = "0.18.2"
|
8
8
|
end
|
9
9
|
|
10
10
|
begin
|
@@ -21,7 +21,7 @@ begin
|
|
21
21
|
warn " You can install the extension as follows:\n gem install mongo_ext\n"
|
22
22
|
warn " If you continue to receive this message after installing, make sure that the"
|
23
23
|
warn " mongo_ext gem is in your load path and that the mongo_ext and mongo gems are of the same version.\n"
|
24
|
-
end
|
24
|
+
end
|
25
25
|
|
26
26
|
require 'mongo/types/binary'
|
27
27
|
require 'mongo/types/code'
|
data/lib/mongo/admin.rb
CHANGED
@@ -59,8 +59,7 @@ module Mongo
|
|
59
59
|
raise "Error with profile command: #{doc.inspect}" unless @db.ok?(doc)
|
60
60
|
end
|
61
61
|
|
62
|
-
#
|
63
|
-
# database.
|
62
|
+
# Returns an array containing current profiling information.
|
64
63
|
def profiling_info
|
65
64
|
Cursor.new(Collection.new(@db, DB::SYSTEM_PROFILE_COLLECTION), :selector => {}).to_a
|
66
65
|
end
|
data/lib/mongo/collection.rb
CHANGED
@@ -100,12 +100,12 @@ module Mongo
|
|
100
100
|
# objects missed, which were preset at both the start and
|
101
101
|
# end of the query's execution. For details see
|
102
102
|
# http://www.mongodb.org/display/DOCS/How+to+do+Snapshotting+in+the+Mongo+Database
|
103
|
-
# :timeout :: When +true+ (default), the returned cursor will be subject to
|
104
|
-
# the normal cursor timeout behavior of the mongod process.
|
103
|
+
# :timeout :: When +true+ (default), the returned cursor will be subject to
|
104
|
+
# the normal cursor timeout behavior of the mongod process.
|
105
105
|
# When +false+, the returned cursor will never timeout. Note
|
106
106
|
# that disabling timeout will only work when #find is invoked
|
107
107
|
# with a block. This is to prevent any inadvertant failure to
|
108
|
-
# close the cursor, as the cursor is explicitly closed when
|
108
|
+
# close the cursor, as the cursor is explicitly closed when
|
109
109
|
# block code finishes.
|
110
110
|
def find(selector={}, options={})
|
111
111
|
fields = options.delete(:fields)
|
@@ -116,7 +116,7 @@ module Mongo
|
|
116
116
|
hint = options.delete(:hint)
|
117
117
|
snapshot = options.delete(:snapshot)
|
118
118
|
if options[:timeout] == false && !block_given?
|
119
|
-
raise ArgumentError, "Timeout can be set to false only when #find is invoked with a block."
|
119
|
+
raise ArgumentError, "Timeout can be set to false only when #find is invoked with a block."
|
120
120
|
end
|
121
121
|
timeout = block_given? ? (options.delete(:timeout) || true) : true
|
122
122
|
if hint
|
@@ -126,7 +126,7 @@ module Mongo
|
|
126
126
|
end
|
127
127
|
raise RuntimeError, "Unknown options [#{options.inspect}]" unless options.empty?
|
128
128
|
|
129
|
-
cursor = Cursor.new(self, :selector => selector, :fields => fields, :skip => skip, :limit => limit,
|
129
|
+
cursor = Cursor.new(self, :selector => selector, :fields => fields, :skip => skip, :limit => limit,
|
130
130
|
:order => sort, :hint => hint, :snapshot => snapshot, :timeout => timeout)
|
131
131
|
if block_given?
|
132
132
|
yield cursor
|
@@ -158,7 +158,7 @@ module Mongo
|
|
158
158
|
else
|
159
159
|
raise TypeError, "spec_or_object_id must be an instance of ObjectID or Hash, or nil"
|
160
160
|
end
|
161
|
-
find(spec, options.merge(:limit => -1)).
|
161
|
+
find(spec, options.merge(:limit => -1)).next_document
|
162
162
|
end
|
163
163
|
|
164
164
|
# Save a document in this collection.
|
@@ -175,7 +175,8 @@ module Mongo
|
|
175
175
|
# will be raised on an error. Checking for safety requires an extra
|
176
176
|
# round-trip to the database
|
177
177
|
def save(to_save, options={})
|
178
|
-
if
|
178
|
+
if to_save.has_key?(:_id) || to_save.has_key?('_id')
|
179
|
+
id = to_save[:_id] || to_save['_id']
|
179
180
|
update({:_id => id}, to_save, :upsert => true, :safe => options.delete(:safe))
|
180
181
|
id
|
181
182
|
else
|
@@ -204,14 +205,14 @@ module Mongo
|
|
204
205
|
end
|
205
206
|
alias_method :<<, :insert
|
206
207
|
|
207
|
-
# Remove all records from this collection.
|
208
|
+
# Remove all records from this collection.
|
208
209
|
# If +selector+ is specified, only matching documents will be removed.
|
209
|
-
#
|
210
|
+
#
|
210
211
|
# Remove all records from the collection:
|
211
212
|
# @collection.remove
|
212
213
|
# @collection.remove({})
|
213
214
|
#
|
214
|
-
# Remove only records that have expired:
|
215
|
+
# Remove only records that have expired:
|
215
216
|
# @collection.remove({:expire => {'$lte' => Time.now}})
|
216
217
|
def remove(selector={})
|
217
218
|
message = ByteBuffer.new
|
@@ -225,7 +226,7 @@ module Mongo
|
|
225
226
|
|
226
227
|
# Update a single document in this collection.
|
227
228
|
#
|
228
|
-
# :selector :: a hash specifying elements which must be present for a document to be updated. Note:
|
229
|
+
# :selector :: a hash specifying elements which must be present for a document to be updated. Note:
|
229
230
|
# the update command currently updates only the first document matching the
|
230
231
|
# given selector. If you want all matching documents to be updated, be sure
|
231
232
|
# to specify :multi => true.
|
@@ -254,7 +255,7 @@ module Mongo
|
|
254
255
|
@connection.send_message_with_safe_check(Mongo::Constants::OP_UPDATE, message, @db.name,
|
255
256
|
"db.#{@name}.update(#{selector.inspect}, #{document.inspect})")
|
256
257
|
else
|
257
|
-
@connection.send_message(Mongo::Constants::OP_UPDATE, message,
|
258
|
+
@connection.send_message(Mongo::Constants::OP_UPDATE, message,
|
258
259
|
"db.#{@name}.update(#{selector.inspect}, #{document.inspect})")
|
259
260
|
end
|
260
261
|
end
|
@@ -329,7 +330,7 @@ module Mongo
|
|
329
330
|
|
330
331
|
result = @db.command(hash)
|
331
332
|
unless result["ok"] == 1
|
332
|
-
raise Mongo::OperationFailure, "map-reduce failed: #{result['errmsg']}"
|
333
|
+
raise Mongo::OperationFailure, "map-reduce failed: #{result['errmsg']}"
|
333
334
|
end
|
334
335
|
@db[result["result"]]
|
335
336
|
end
|
@@ -338,48 +339,80 @@ module Mongo
|
|
338
339
|
# Performs a group query, similar to the 'SQL GROUP BY' operation.
|
339
340
|
# Returns an array of grouped items.
|
340
341
|
#
|
341
|
-
# :
|
342
|
-
#
|
343
|
-
#
|
342
|
+
# :key :: either 1) an array of fields to group by, 2) a javascript function to generate
|
343
|
+
# the key object, or 3) nil.
|
344
|
+
# :condition :: an optional document specifying a query to limit the documents over which group is run.
|
344
345
|
# :initial :: initial value of the aggregation counter object
|
345
346
|
# :reduce :: aggregation function as a JavaScript string
|
347
|
+
# :finalize :: optional. a JavaScript function that receives and modifies
|
348
|
+
# each of the resultant grouped objects. Available only when group is run
|
349
|
+
# with command set to true.
|
346
350
|
# :command :: if true, run the group as a command instead of in an
|
347
351
|
# eval - it is likely that this option will eventually be
|
348
352
|
# deprecated and all groups will be run as commands
|
349
|
-
def group(
|
353
|
+
def group(key, condition, initial, reduce, command=false, finalize=nil)
|
354
|
+
|
350
355
|
if command
|
351
|
-
hash = {}
|
352
|
-
keys.each do |k|
|
353
|
-
hash[k] = 1
|
354
|
-
end
|
355
356
|
|
356
357
|
reduce = Code.new(reduce) unless reduce.is_a?(Code)
|
357
358
|
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
359
|
+
group_command = {
|
360
|
+
"group" => {
|
361
|
+
"ns" => @name,
|
362
|
+
"$reduce" => reduce,
|
363
|
+
"cond" => condition,
|
364
|
+
"initial" => initial
|
365
|
+
}
|
366
|
+
}
|
367
|
+
|
368
|
+
unless key.nil?
|
369
|
+
if key.is_a? Array
|
370
|
+
key_type = "key"
|
371
|
+
key_value = {}
|
372
|
+
key.each { |k| key_value[k] = 1 }
|
373
|
+
else
|
374
|
+
key_type = "$keyf"
|
375
|
+
key_value = key.is_a?(Code) ? key : Code.new(key)
|
376
|
+
end
|
377
|
+
|
378
|
+
group_command["group"][key_type] = key_value
|
379
|
+
end
|
380
|
+
|
381
|
+
# only add finalize if specified
|
382
|
+
if finalize
|
383
|
+
finalize = Code.new(finalize) unless finalize.is_a?(Code)
|
384
|
+
group_command['group']['finalize'] = finalize
|
385
|
+
end
|
386
|
+
|
387
|
+
result = @db.command group_command
|
388
|
+
|
365
389
|
if result["ok"] == 1
|
366
390
|
return result["retval"]
|
367
391
|
else
|
368
392
|
raise OperationFailure, "group command failed: #{result['errmsg']}"
|
369
393
|
end
|
370
|
-
end
|
371
394
|
|
372
|
-
case reduce
|
373
|
-
when Code
|
374
|
-
scope = reduce.scope
|
375
395
|
else
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
396
|
+
|
397
|
+
warn "Collection#group must now be run as a command; you can do this by passing 'true' as the command argument."
|
398
|
+
|
399
|
+
raise OperationFailure, ":finalize can be specified only when " +
|
400
|
+
"group is run as a command (set command param to true)" if finalize
|
401
|
+
|
402
|
+
raise OperationFailure, "key must be an array of fields to group by. If you want to pass a key function,
|
403
|
+
run group as a command by passing 'true' as the command argument." unless key.is_a? Array || key.nil?
|
404
|
+
|
405
|
+
case reduce
|
406
|
+
when Code
|
407
|
+
scope = reduce.scope
|
408
|
+
else
|
409
|
+
scope = {}
|
410
|
+
end
|
411
|
+
scope.merge!({
|
412
|
+
"ns" => @name,
|
413
|
+
"keys" => key,
|
414
|
+
"condition" => condition,
|
415
|
+
"initial" => initial })
|
383
416
|
|
384
417
|
group_function = <<EOS
|
385
418
|
function () {
|
@@ -406,7 +439,8 @@ function () {
|
|
406
439
|
return {"result": map.values()};
|
407
440
|
}
|
408
441
|
EOS
|
409
|
-
|
442
|
+
@db.eval(Code.new(group_function, scope))["result"]
|
443
|
+
end
|
410
444
|
end
|
411
445
|
|
412
446
|
# Returns a list of distinct values for +key+ across all
|
@@ -422,11 +456,17 @@ EOS
|
|
422
456
|
# [10010, 94108, 99701]
|
423
457
|
# @collection.distinct("name.age")
|
424
458
|
# [27, 24]
|
425
|
-
|
459
|
+
#
|
460
|
+
# You may also pass a document selector as the second parameter
|
461
|
+
# to limit the documents over which distinct is run:
|
462
|
+
# @collection.distinct("name.age", {"name.age" => {"$gt" => 24}})
|
463
|
+
# [27]
|
464
|
+
def distinct(key, query=nil)
|
426
465
|
raise MongoArgumentError unless [String, Symbol].include?(key.class)
|
427
466
|
command = OrderedHash.new
|
428
467
|
command[:distinct] = @name
|
429
|
-
command[:key]
|
468
|
+
command[:key] = key.to_s
|
469
|
+
command[:query] = query
|
430
470
|
|
431
471
|
@db.command(command)["values"]
|
432
472
|
end
|
@@ -472,7 +512,7 @@ EOS
|
|
472
512
|
# 'create' will be the collection name. For the other possible keys
|
473
513
|
# and values, see DB#create_collection.
|
474
514
|
def options
|
475
|
-
@db.collections_info(@name).
|
515
|
+
@db.collections_info(@name).next_document['options']
|
476
516
|
end
|
477
517
|
|
478
518
|
# Get the number of documents in this collection.
|
@@ -501,7 +541,7 @@ EOS
|
|
501
541
|
|
502
542
|
private
|
503
543
|
|
504
|
-
# Sends
|
544
|
+
# Sends a Mongo::Constants::OP_INSERT message to the database.
|
505
545
|
# Takes an array of +documents+, an optional +collection_name+, and a
|
506
546
|
# +check_keys+ setting.
|
507
547
|
def insert_documents(documents, collection_name=@name, check_keys=true, safe=false)
|
@@ -523,7 +563,7 @@ EOS
|
|
523
563
|
indexes = []
|
524
564
|
spec.each_pair do |field, direction|
|
525
565
|
indexes.push("#{field}_#{direction}")
|
526
|
-
end
|
566
|
+
end
|
527
567
|
indexes.join("_")
|
528
568
|
end
|
529
569
|
end
|