mongo 0.18.1 → 0.18.2
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/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
|