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.
- data/LICENSE.txt +1 -13
- data/{README.rdoc → README.md} +129 -149
- data/Rakefile +94 -58
- data/bin/mongo_console +21 -0
- data/docs/1.0_UPGRADE.md +21 -0
- data/docs/CREDITS.md +123 -0
- data/docs/FAQ.md +112 -0
- data/docs/GridFS.md +158 -0
- data/docs/HISTORY.md +185 -0
- data/docs/REPLICA_SETS.md +75 -0
- data/docs/TUTORIAL.md +247 -0
- data/docs/WRITE_CONCERN.md +28 -0
- data/lib/mongo/collection.rb +225 -105
- data/lib/mongo/connection.rb +374 -315
- data/lib/mongo/cursor.rb +122 -77
- data/lib/mongo/db.rb +109 -85
- data/lib/mongo/exceptions.rb +6 -0
- data/lib/mongo/gridfs/grid.rb +19 -11
- data/lib/mongo/gridfs/grid_ext.rb +36 -9
- data/lib/mongo/gridfs/grid_file_system.rb +15 -9
- data/lib/mongo/gridfs/grid_io.rb +49 -16
- data/lib/mongo/gridfs/grid_io_fix.rb +38 -0
- data/lib/mongo/repl_set_connection.rb +290 -0
- data/lib/mongo/util/conversions.rb +3 -1
- data/lib/mongo/util/core_ext.rb +17 -4
- data/lib/mongo/util/pool.rb +125 -0
- data/lib/mongo/util/server_version.rb +2 -0
- data/lib/mongo/util/support.rb +12 -0
- data/lib/mongo/util/uri_parser.rb +71 -0
- data/lib/mongo.rb +23 -7
- data/{mongo-ruby-driver.gemspec → mongo.gemspec} +9 -7
- data/test/auxillary/1.4_features.rb +2 -2
- data/test/auxillary/authentication_test.rb +1 -1
- data/test/auxillary/autoreconnect_test.rb +1 -1
- data/test/{slave_connection_test.rb → auxillary/slave_connection_test.rb} +6 -6
- data/test/bson/binary_test.rb +15 -0
- data/test/bson/bson_test.rb +537 -0
- data/test/bson/byte_buffer_test.rb +190 -0
- data/test/bson/hash_with_indifferent_access_test.rb +38 -0
- data/test/bson/json_test.rb +17 -0
- data/test/bson/object_id_test.rb +141 -0
- data/test/bson/ordered_hash_test.rb +197 -0
- data/test/collection_test.rb +195 -15
- data/test/connection_test.rb +93 -56
- data/test/conversions_test.rb +1 -1
- data/test/cursor_fail_test.rb +75 -0
- data/test/cursor_message_test.rb +43 -0
- data/test/cursor_test.rb +93 -32
- data/test/db_api_test.rb +28 -55
- data/test/db_connection_test.rb +2 -3
- data/test/db_test.rb +45 -40
- data/test/grid_file_system_test.rb +14 -6
- data/test/grid_io_test.rb +36 -7
- data/test/grid_test.rb +54 -10
- data/test/replica_sets/connect_test.rb +84 -0
- data/test/replica_sets/count_test.rb +35 -0
- data/test/{replica → replica_sets}/insert_test.rb +17 -14
- data/test/replica_sets/pooled_insert_test.rb +55 -0
- data/test/replica_sets/query_secondaries.rb +80 -0
- data/test/replica_sets/query_test.rb +41 -0
- data/test/replica_sets/replication_ack_test.rb +64 -0
- data/test/replica_sets/rs_test_helper.rb +29 -0
- data/test/safe_test.rb +68 -0
- data/test/support/hash_with_indifferent_access.rb +199 -0
- data/test/support/keys.rb +45 -0
- data/test/support_test.rb +19 -0
- data/test/test_helper.rb +53 -15
- data/test/threading/{test_threading_large_pool.rb → threading_with_large_pool_test.rb} +2 -2
- data/test/threading_test.rb +2 -2
- data/test/tools/repl_set_manager.rb +241 -0
- data/test/tools/test.rb +13 -0
- data/test/unit/collection_test.rb +70 -7
- data/test/unit/connection_test.rb +18 -39
- data/test/unit/cursor_test.rb +7 -8
- data/test/unit/db_test.rb +14 -17
- data/test/unit/grid_test.rb +49 -0
- data/test/unit/pool_test.rb +9 -0
- data/test/unit/repl_set_connection_test.rb +82 -0
- data/test/unit/safe_test.rb +125 -0
- metadata +132 -51
- data/bin/bson_benchmark.rb +0 -59
- data/bin/fail_if_no_c.rb +0 -11
- data/examples/admin.rb +0 -43
- data/examples/capped.rb +0 -22
- data/examples/cursor.rb +0 -48
- data/examples/gridfs.rb +0 -44
- data/examples/index_test.rb +0 -126
- data/examples/info.rb +0 -31
- data/examples/queries.rb +0 -70
- data/examples/simple.rb +0 -24
- data/examples/strict.rb +0 -35
- data/examples/types.rb +0 -36
- data/test/replica/count_test.rb +0 -34
- data/test/replica/pooled_insert_test.rb +0 -54
- data/test/replica/query_test.rb +0 -39
data/Rakefile
CHANGED
|
@@ -13,36 +13,72 @@ require 'rbconfig'
|
|
|
13
13
|
include Config
|
|
14
14
|
ENV['TEST_MODE'] = 'TRUE'
|
|
15
15
|
|
|
16
|
+
task :java do
|
|
17
|
+
Rake::Task['build:java'].invoke
|
|
18
|
+
Rake::Task['test:ruby'].invoke
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
namespace :build do
|
|
22
|
+
desc "Build the java extensions."
|
|
23
|
+
task :java do
|
|
24
|
+
puts "Building Java extensions..."
|
|
25
|
+
java_dir = File.join(File.dirname(__FILE__), 'ext', 'java')
|
|
26
|
+
jar_dir = File.join(java_dir, 'jar')
|
|
27
|
+
|
|
28
|
+
jruby_jar = File.join(jar_dir, 'jruby.jar')
|
|
29
|
+
mongo_jar = File.join(jar_dir, 'mongo-2.2.jar')
|
|
30
|
+
bson_jar = File.join(jar_dir, 'bson-2.2.jar')
|
|
31
|
+
|
|
32
|
+
src_base = File.join(java_dir, 'src')
|
|
33
|
+
|
|
34
|
+
system("javac -Xlint:unchecked -classpath #{jruby_jar}:#{mongo_jar}:#{bson_jar} #{File.join(src_base, 'org', 'jbson', '*.java')}")
|
|
35
|
+
system("cd #{src_base} && jar cf #{File.join(jar_dir, 'jbson.jar')} #{File.join('.', 'org', 'jbson', '*.class')}")
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
16
39
|
desc "Test the MongoDB Ruby driver."
|
|
17
40
|
task :test do
|
|
18
|
-
puts "\
|
|
19
|
-
puts "
|
|
20
|
-
puts "To test the pure ruby driver: \nrake test:ruby"
|
|
41
|
+
puts "\nTo test the driver with the C-extensions:\nrake test:c\n\n"
|
|
42
|
+
puts "To test the pure ruby driver: \nrake test:ruby\n\n"
|
|
21
43
|
end
|
|
22
44
|
|
|
23
45
|
namespace :test do
|
|
24
46
|
|
|
25
|
-
desc "Test the driver with the
|
|
47
|
+
desc "Test the driver with the C extension enabled."
|
|
26
48
|
task :c do
|
|
27
49
|
ENV['C_EXT'] = 'TRUE'
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
50
|
+
if ENV['TEST']
|
|
51
|
+
Rake::Task['test:functional'].invoke
|
|
52
|
+
else
|
|
53
|
+
Rake::Task['test:unit'].invoke
|
|
54
|
+
Rake::Task['test:functional'].invoke
|
|
55
|
+
Rake::Task['test:bson'].invoke
|
|
56
|
+
Rake::Task['test:pooled_threading'].invoke
|
|
57
|
+
Rake::Task['test:drop_databases'].invoke
|
|
58
|
+
end
|
|
33
59
|
ENV['C_EXT'] = nil
|
|
34
60
|
end
|
|
35
61
|
|
|
36
|
-
desc "Test the driver using pure ruby (no
|
|
62
|
+
desc "Test the driver using pure ruby (no C extension)"
|
|
37
63
|
task :ruby do
|
|
38
64
|
ENV['C_EXT'] = nil
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
65
|
+
if ENV['TEST']
|
|
66
|
+
Rake::Task['test:functional'].invoke
|
|
67
|
+
else
|
|
68
|
+
Rake::Task['test:unit'].invoke
|
|
69
|
+
Rake::Task['test:functional'].invoke
|
|
70
|
+
Rake::Task['test:bson'].invoke
|
|
71
|
+
Rake::Task['test:pooled_threading'].invoke
|
|
72
|
+
Rake::Task['test:drop_databases'].invoke
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
desc "Run the replica set test suite"
|
|
77
|
+
Rake::TestTask.new(:rs) do |t|
|
|
78
|
+
t.test_files = FileList['test/replica_sets/*_test.rb']
|
|
79
|
+
t.verbose = true
|
|
44
80
|
end
|
|
45
|
-
|
|
81
|
+
|
|
46
82
|
Rake::TestTask.new(:unit) do |t|
|
|
47
83
|
t.test_files = FileList['test/unit/*_test.rb']
|
|
48
84
|
t.verbose = true
|
|
@@ -54,27 +90,7 @@ namespace :test do
|
|
|
54
90
|
end
|
|
55
91
|
|
|
56
92
|
Rake::TestTask.new(:pooled_threading) do |t|
|
|
57
|
-
t.test_files = FileList['test/threading
|
|
58
|
-
t.verbose = true
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
Rake::TestTask.new(:pair_count) do |t|
|
|
62
|
-
t.test_files = FileList['test/replica/count_test.rb']
|
|
63
|
-
t.verbose = true
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
Rake::TestTask.new(:pair_insert) do |t|
|
|
67
|
-
t.test_files = FileList['test/replica/insert_test.rb']
|
|
68
|
-
t.verbose = true
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
Rake::TestTask.new(:pooled_pair_insert) do |t|
|
|
72
|
-
t.test_files = FileList['test/replica/pooled_insert_test.rb']
|
|
73
|
-
t.verbose = true
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
Rake::TestTask.new(:pair_query) do |t|
|
|
77
|
-
t.test_files = FileList['test/replica/query_test.rb']
|
|
93
|
+
t.test_files = FileList['test/threading/*_test.rb']
|
|
78
94
|
t.verbose = true
|
|
79
95
|
end
|
|
80
96
|
|
|
@@ -94,26 +110,27 @@ namespace :test do
|
|
|
94
110
|
end
|
|
95
111
|
|
|
96
112
|
Rake::TestTask.new(:bson) do |t|
|
|
97
|
-
t.test_files = FileList['test/
|
|
113
|
+
t.test_files = FileList['test/bson/*_test.rb']
|
|
98
114
|
t.verbose = true
|
|
99
115
|
end
|
|
100
116
|
|
|
101
117
|
task :drop_databases do |t|
|
|
102
|
-
puts "Dropping test
|
|
103
|
-
require
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
118
|
+
puts "Dropping test databases..."
|
|
119
|
+
require './lib/mongo'
|
|
120
|
+
con = Mongo::Connection.new(ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost',
|
|
121
|
+
ENV['MONGO_RUBY_DRIVER_PORT'] || Mongo::Connection::DEFAULT_PORT)
|
|
122
|
+
con.database_names.each do |name|
|
|
123
|
+
con.drop_database(name) if name =~ /^ruby-test/
|
|
124
|
+
end
|
|
108
125
|
end
|
|
109
126
|
end
|
|
110
127
|
|
|
111
|
-
desc "Generate documentation"
|
|
128
|
+
desc "Generate RDOC documentation"
|
|
112
129
|
task :rdoc do
|
|
113
|
-
version = eval(File.read("mongo
|
|
130
|
+
version = eval(File.read("mongo.gemspec")).version
|
|
114
131
|
out = File.join('html', version.to_s)
|
|
115
132
|
FileUtils.rm_rf('html')
|
|
116
|
-
system "rdoc --main README.
|
|
133
|
+
system "rdoc --main README.md --op #{out} --inline-source --quiet README.md `find lib -name '*.rb'`"
|
|
117
134
|
end
|
|
118
135
|
|
|
119
136
|
desc "Generate YARD documentation"
|
|
@@ -121,36 +138,55 @@ task :ydoc do
|
|
|
121
138
|
require File.join(File.dirname(__FILE__), 'lib', 'mongo')
|
|
122
139
|
out = File.join('ydoc', Mongo::VERSION)
|
|
123
140
|
FileUtils.rm_rf('ydoc')
|
|
124
|
-
system "yardoc lib/**/*.rb lib/mongo/**/*.rb -e
|
|
141
|
+
system "yardoc lib/**/*.rb lib/mongo/**/*.rb lib/bson/**/*.rb -e yard/yard_ext.rb -p yard/templates -o #{out} --title MongoRuby-#{Mongo::VERSION} --files docs/TUTORIAL.md,docs/GridFS.md,docs/FAQ.md,docs/REPLICA_SETS.md,docs/WRITE_CONCERN.md,docs/HISTORY.md,docs/CREDITS.md,docs/1.0_UPGRADE.md"
|
|
125
142
|
end
|
|
126
143
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
144
|
+
namespace :bamboo do
|
|
145
|
+
namespace :test do
|
|
146
|
+
task :ruby do
|
|
147
|
+
Rake::Task['test:ruby'].invoke
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
task :c do
|
|
151
|
+
Rake::Task['gem:install_extensions'].invoke
|
|
152
|
+
Rake::Task['test:c'].invoke
|
|
153
|
+
end
|
|
154
|
+
end
|
|
131
155
|
end
|
|
132
156
|
|
|
133
157
|
namespace :gem do
|
|
134
158
|
|
|
135
159
|
desc "Install the gem locally"
|
|
136
160
|
task :install do
|
|
137
|
-
sh "gem build
|
|
138
|
-
sh "gem install
|
|
161
|
+
sh "gem build bson.gemspec"
|
|
162
|
+
sh "gem install --no-rdoc --no-ri bson-*.gem"
|
|
163
|
+
|
|
164
|
+
sh "gem build mongo.gemspec"
|
|
165
|
+
sh "gem install --no-rdoc --no-ri mongo-*.gem"
|
|
166
|
+
|
|
139
167
|
sh "rm mongo-*.gem"
|
|
168
|
+
sh "rm bson-*.gem"
|
|
140
169
|
end
|
|
141
170
|
|
|
142
171
|
desc "Install the optional c extensions"
|
|
143
172
|
task :install_extensions do
|
|
144
|
-
sh "gem build bson.gemspec"
|
|
145
173
|
sh "gem build bson_ext.gemspec"
|
|
146
|
-
sh "gem install
|
|
147
|
-
sh "gem install bson_ext-*.gem"
|
|
148
|
-
sh "rm bson-*.gem"
|
|
174
|
+
sh "gem install --no-rdoc --no-ri bson_ext-*.gem"
|
|
149
175
|
sh "rm bson_ext-*.gem"
|
|
150
176
|
end
|
|
151
177
|
|
|
152
178
|
end
|
|
153
179
|
|
|
180
|
+
namespace :ci do
|
|
181
|
+
namespace :test do
|
|
182
|
+
task :c do
|
|
183
|
+
Rake::Task['gem:install'].invoke
|
|
184
|
+
Rake::Task['gem:install_extensions'].invoke
|
|
185
|
+
Rake::Task['test:c'].invoke
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
154
190
|
task :default => :list
|
|
155
191
|
|
|
156
192
|
task :list do
|
data/bin/mongo_console
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
org_argv = ARGV.dup
|
|
3
|
+
ARGV.clear
|
|
4
|
+
|
|
5
|
+
require 'irb'
|
|
6
|
+
|
|
7
|
+
$LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
|
|
8
|
+
require 'mongo'
|
|
9
|
+
|
|
10
|
+
include Mongo
|
|
11
|
+
|
|
12
|
+
host = org_argv[0] || ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
|
13
|
+
port = org_argv[1] || ENV['MONGO_RUBY_DRIVER_PORT'] || Connection::DEFAULT_PORT
|
|
14
|
+
dbnm = org_argv[2] || ENV['MONGO_RUBY_DRIVER_DB'] || 'ruby-mongo-console'
|
|
15
|
+
|
|
16
|
+
puts "Connecting to #{host}:#{port} (CONN) on with database #{dbnm} (DB)"
|
|
17
|
+
CONN = Connection.new(host, port)
|
|
18
|
+
DB = CONN.db(dbnm)
|
|
19
|
+
|
|
20
|
+
puts "Starting IRB session..."
|
|
21
|
+
IRB.start(__FILE__)
|
data/docs/1.0_UPGRADE.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
You can upgrade freely from v0.20 to v1.0.
|
|
2
|
+
|
|
3
|
+
However, if you're running a version < 0.20, upgrade to 0.20
|
|
4
|
+
before upgrading to 1.0.
|
|
5
|
+
|
|
6
|
+
The upgrade to 0.20 requires some minor code upgrades.
|
|
7
|
+
|
|
8
|
+
1. Note the exception changes in HISTORY. Certain exceptions are now scoped under the BSON
|
|
9
|
+
module; if you're catching these, you will need to modify your code.
|
|
10
|
+
|
|
11
|
+
2. The BSON types are now scoped under the BSON module.
|
|
12
|
+
|
|
13
|
+
3. Note that mongo_ext no longer exists. The new gems are bson and bson_ext.
|
|
14
|
+
|
|
15
|
+
4. Indexes on GridFS chunks collections should be unique. If you have existing GridFS
|
|
16
|
+
collections, you should drop the current index and replace with a unique one. Before you do this,
|
|
17
|
+
make sure that index doesn't exist; no need to go through process unnecessarily.
|
|
18
|
+
If you do need to create the index, once you have the chunks collection, here are the commands you can run:
|
|
19
|
+
|
|
20
|
+
@chunks.drop_index('files_id_1_n_1')
|
|
21
|
+
@chunks.create_index([['files_id', Mongo::ASCENDING], ['n', Mongo::ASCENDING]], :unique => true)
|
data/docs/CREDITS.md
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# Credits
|
|
2
|
+
|
|
3
|
+
Adrian Madrid, aemadrid@gmail.com
|
|
4
|
+
|
|
5
|
+
* bin/mongo_console
|
|
6
|
+
* examples/benchmarks.rb
|
|
7
|
+
* examples/irb.rb
|
|
8
|
+
* Modifications to examples/simple.rb
|
|
9
|
+
* Found plenty of bugs and missing features.
|
|
10
|
+
* Ruby 1.9 support.
|
|
11
|
+
* Gem support.
|
|
12
|
+
* Many other code suggestions and improvements.
|
|
13
|
+
|
|
14
|
+
Aman Gupta, aman@tmm1.net
|
|
15
|
+
|
|
16
|
+
* Collection#save
|
|
17
|
+
* Noted bug in returning query batch size.
|
|
18
|
+
|
|
19
|
+
Jon Crosby, jon@joncrosby.me
|
|
20
|
+
|
|
21
|
+
* Some code clean-up
|
|
22
|
+
|
|
23
|
+
John Nunemaker, http://railstips.org
|
|
24
|
+
|
|
25
|
+
* Collection#create_index takes symbols as well as strings
|
|
26
|
+
* Fix for Collection#save
|
|
27
|
+
* Add logger convenience methods to connection and database
|
|
28
|
+
|
|
29
|
+
David James, djames@sunlightfoundation.com
|
|
30
|
+
|
|
31
|
+
* Fix dates to return as UTC
|
|
32
|
+
|
|
33
|
+
Paul Dlug, paul.dlug@gmail.com
|
|
34
|
+
|
|
35
|
+
* Generate _id on the client side if not provided
|
|
36
|
+
* Collection#insert and Collection#save return _id
|
|
37
|
+
|
|
38
|
+
Durran Jordan, durran@gmail.com
|
|
39
|
+
|
|
40
|
+
* DB#collections
|
|
41
|
+
* Support for specifying sort order as array of [key, direction] pairs
|
|
42
|
+
* OrderedHash#update aliases to merge!
|
|
43
|
+
|
|
44
|
+
Cyril Mougel, cyril.mougel@gmail.com
|
|
45
|
+
|
|
46
|
+
* Initial logging support
|
|
47
|
+
* Test case
|
|
48
|
+
|
|
49
|
+
Jack Chen, chendo on github
|
|
50
|
+
|
|
51
|
+
* Test case + fix for deserializing pre-epoch Time instances
|
|
52
|
+
|
|
53
|
+
Michael Bernstein, mrb on github
|
|
54
|
+
|
|
55
|
+
* Cursor#sort
|
|
56
|
+
|
|
57
|
+
Paulo Ahahgon, pahagon on github
|
|
58
|
+
|
|
59
|
+
* removed hard limit
|
|
60
|
+
|
|
61
|
+
Les Hill, leshill on github
|
|
62
|
+
|
|
63
|
+
* OrderedHash#each returns self
|
|
64
|
+
|
|
65
|
+
Sean Cribbs, seancribbs on github
|
|
66
|
+
|
|
67
|
+
* Modified standard_benchmark to allow profiling
|
|
68
|
+
* C ext for faster ObjectID creation
|
|
69
|
+
|
|
70
|
+
Sunny Hirai
|
|
71
|
+
|
|
72
|
+
* Suggested hashcode fix for Mongo::ObjectID
|
|
73
|
+
* Noted index ordering bug.
|
|
74
|
+
* GridFS performance boost
|
|
75
|
+
|
|
76
|
+
Christos Trochalakis
|
|
77
|
+
|
|
78
|
+
* Added map/reduce helper
|
|
79
|
+
|
|
80
|
+
Blythe Dunham
|
|
81
|
+
|
|
82
|
+
* Added finalize option to map/reduce
|
|
83
|
+
|
|
84
|
+
Matt Powell (fauxparse)
|
|
85
|
+
|
|
86
|
+
* Added GridStore#mv
|
|
87
|
+
|
|
88
|
+
Patrick Collison
|
|
89
|
+
|
|
90
|
+
* Added safe mode for Collection#remove
|
|
91
|
+
|
|
92
|
+
Chuck Remes
|
|
93
|
+
|
|
94
|
+
* Extraction of BSON into separate gems
|
|
95
|
+
* Extensions compile on Rubinius
|
|
96
|
+
* Performance improvements for INT in C extensions
|
|
97
|
+
* Performance improvements for JRuby BSON encoder and callback classes
|
|
98
|
+
|
|
99
|
+
Dmitrii Golub (Houdini) and Jacques Crocker (railsjedi)
|
|
100
|
+
|
|
101
|
+
* Support open to exclude fields on query
|
|
102
|
+
|
|
103
|
+
dfitzgibbon
|
|
104
|
+
|
|
105
|
+
* patch for ensuring bson_ext compatibility with early release of Ruby 1.8.5
|
|
106
|
+
|
|
107
|
+
Matt Taylor
|
|
108
|
+
|
|
109
|
+
* Noticed excessive calls to ObjectId#to_s. As a result, stopped creating
|
|
110
|
+
log messages when no logger was passed to Mongo::Connection. Resulted in a significant
|
|
111
|
+
performance improvement.
|
|
112
|
+
|
|
113
|
+
Hongli Lai (Phusion)
|
|
114
|
+
|
|
115
|
+
* Significant performance improvements. See commits.
|
|
116
|
+
|
|
117
|
+
Mislav Marohnić
|
|
118
|
+
|
|
119
|
+
* Replaced returning with each_with_object
|
|
120
|
+
|
|
121
|
+
Alex Stupka
|
|
122
|
+
|
|
123
|
+
* Replica set port bug
|
data/docs/FAQ.md
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Ruby MongoDB FAQ
|
|
2
|
+
|
|
3
|
+
This is a list of frequently asked questions about using Ruby with MongoDB. If you have a question you'd like to have answered here, please post your question to the [mongodb-user list](http://groups.google.com/group/mongodb-user).
|
|
4
|
+
|
|
5
|
+
#### Can I run (insert command name here) from the Ruby driver?
|
|
6
|
+
|
|
7
|
+
Yes. You can run any of the [available database commands|List of Database Commands] from the driver using the DB#command method. The only trick is to use an OrderedHash when specifying the command. For example, here's how you'd run an asynchronous fsync from the driver:
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# This command is run on the admin database.
|
|
11
|
+
@db = Mongo::Connection.new.db('admin')
|
|
12
|
+
|
|
13
|
+
# Build the command.
|
|
14
|
+
cmd = OrderedHash.new
|
|
15
|
+
cmd['fsync'] = 1
|
|
16
|
+
cmd['async'] = true
|
|
17
|
+
|
|
18
|
+
# Run it.
|
|
19
|
+
@db.command(cmd)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
It's important to keep in mind that some commands, like `fsync`, must be run on the `admin` database, while other commands can be run on any database. If you're having trouble, check the [command reference|List of Database Commands] to make sure you're using the command correctly.
|
|
23
|
+
|
|
24
|
+
#### Does the Ruby driver support an EXPLAIN command?
|
|
25
|
+
|
|
26
|
+
Yes. `explain` is, technically speaking, an option sent to a query that tells MongoDB to return an explain plan rather than the query's results. You can use `explain` by constructing a query and calling explain at the end:
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@collection = @db['users']
|
|
30
|
+
result = @collection.find({:name => "jones"}).explain
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
The resulting explain plan might look something like this:
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
{"cursor"=>"BtreeCursor name_1",
|
|
37
|
+
"startKey"=>{"name"=>"Jones"},
|
|
38
|
+
"endKey"=>{"name"=>"Jones"},
|
|
39
|
+
"nscanned"=>1.0,
|
|
40
|
+
"n"=>1,
|
|
41
|
+
"millis"=>0,
|
|
42
|
+
"oldPlan"=>{"cursor"=>"BtreeCursor name_1",
|
|
43
|
+
"startKey"=>{"name"=>"Jones"},
|
|
44
|
+
"endKey"=>{"name"=>"Jones"}
|
|
45
|
+
},
|
|
46
|
+
"allPlans"=>[{"cursor"=>"BtreeCursor name_1",
|
|
47
|
+
"startKey"=>{"name"=>"Jones"},
|
|
48
|
+
"endKey"=>{"name"=>"Jones"`]
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
Because this collection has an index on the "name" field, the query uses that index, only having to scan a single record. "n" is the number of records the query will return. "millis" is the time the query takes, in milliseconds. "oldPlan" indicates that the query optimizer has already seen this kind of query and has, therefore, saved an efficient query plan. "allPlans" shows all the plans considered for this query.
|
|
53
|
+
|
|
54
|
+
#### I see that BSON supports a symbol type. Does this mean that I can store Ruby symbols in MongoDB?
|
|
55
|
+
|
|
56
|
+
You can store Ruby symbols in MongoDB, but only as values. BSON specifies that document keys must be strings. So, for instance, you can do this:
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@collection = @db['test']
|
|
60
|
+
|
|
61
|
+
boat_id = @collection.save({:vehicle => :boat})
|
|
62
|
+
car_id = @collection.save({"vehicle" => "car"})
|
|
63
|
+
|
|
64
|
+
@collection.find_one('_id' => boat_id)
|
|
65
|
+
{"_id" => ObjectID('4bb372a8238d3b5c8c000001'), "vehicle" => :boat}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@collection.find_one('_id' => car_id)
|
|
69
|
+
{"_id" => ObjectID('4bb372a8238d3b5c8c000002'), "vehicle" => "car"}
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
Notice that the symbol values are returned as expected, but that symbol keys are treated as strings.
|
|
73
|
+
|
|
74
|
+
#### Why can't I access random elements within a cursor?
|
|
75
|
+
|
|
76
|
+
MongoDB cursors are designed for sequentially iterating over a result set, and all the drivers, including the Ruby driver, stick closely to this directive. Internally, a Ruby cursor fetches results in batches by running a MongoDB `getmore` operation. The results are buffered for efficient iteration on the client-side.
|
|
77
|
+
|
|
78
|
+
What this means is that a cursor is nothing more than a device for returning a result set on a query that's been initiated on the server. Cursors are not containers for result sets. If we allow a cursor to be randomly accessed, then we run into issues regarding the freshness of the data. For instance, if I iterate over a cursor and then want to retrieve the cursor's first element, should a stored copy be returned, or should the cursor re-run the query? If we returned a stored copy, it may not be fresh. And if the the query is re-run, then we're technically dealing with a new cursor.
|
|
79
|
+
|
|
80
|
+
To avoid those issues, we're saying that anyone who needs flexible access to the results of a query should store those results in an array and then access the data as needed.
|
|
81
|
+
|
|
82
|
+
#### Why can't I save an instance of TimeWithZone?
|
|
83
|
+
|
|
84
|
+
MongoDB stores times in UTC as the number of milliseconds since the epoch. This means that the Ruby driver serializes Ruby Time objects only. While it would certainly be possible to serialize a TimeWithZone, this isn't preferable since the driver would still deserialize to a Time object.
|
|
85
|
+
|
|
86
|
+
All that said, if necessary, it'd be easy to write a thin wrapper over the driver that would store an extra time zone attribute and handle the serialization/deserialization of TimeWithZone transparently.
|
|
87
|
+
|
|
88
|
+
#### I keep getting CURSOR_NOT_FOUND exceptions. What's happening?
|
|
89
|
+
|
|
90
|
+
The most likely culprit here is that the cursor is timing out on the server. Whenever you issue a query, a cursor is created on the server. Cursor naturally time out after ten minutes, which means that if you happen to be iterating over a cursor for more than ten minutes, you risk a CURSOR_NOT_FOUND exception.
|
|
91
|
+
|
|
92
|
+
There are two solutions to this problem. You can either:
|
|
93
|
+
|
|
94
|
+
1. Limit your query. Use some combination of `limit` and `skip` to reduce the total number of query results. This will, obviously, bring down the time it takes to iterate.
|
|
95
|
+
|
|
96
|
+
2. Turn off the cursor timeout. To do that, invoke `find` with a block, and pass `:timeout => true`:
|
|
97
|
+
|
|
98
|
+
@collection.find({}, :timeout => false) do |cursor|
|
|
99
|
+
cursor.each do |document
|
|
100
|
+
# Process documents here
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
#### I periodically see connection failures between the driver and MongoDB. Why can't the driver retry the operation automatically?
|
|
105
|
+
|
|
106
|
+
A connection failure can indicate any number of failure scenarios. Has the server crashed? Are we experiencing a temporary network partition? Is there a bug in our ssh tunnel?
|
|
107
|
+
|
|
108
|
+
Without further investigation, it's impossible to know exactly what has caused the connection failure. Furthermore, when we do see a connection failure, it's impossible to know how many operations prior to the failure succeeded. Imagine, for instance, that we're using safe mode and we send an `$inc` operation to the server. It's entirely possible that the server has received the `$inc` but failed on the call to `getLastError`. In that case, retrying the operation would result in a double-increment.
|
|
109
|
+
|
|
110
|
+
Because of the indeterminacy involved, the MongoDB drivers will not retry operations on connection failure. How connection failures should be handled is entirely dependent on the application. Therefore, we leave it to the application developers to make the best decision in this case.
|
|
111
|
+
|
|
112
|
+
The drivers will reconnect on the subsequent operation.
|
data/docs/GridFS.md
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# GridFS in Ruby
|
|
2
|
+
|
|
3
|
+
GridFS, which stands for "Grid File Store," is a specification for storing large files in MongoDB. It works by dividing a file into manageable chunks and storing each of those chunks as a separate document. GridFS requires two collections to achieve this: one collection stores each file's metadata (e.g., name, size, etc.) and another stores the chunks themselves. If you're interested in more details, check out the [GridFS Specification](http://www.mongodb.org/display/DOCS/GridFS+Specification).
|
|
4
|
+
|
|
5
|
+
### The Grid class
|
|
6
|
+
|
|
7
|
+
The [Grid class](Mongo/Grid.html) represents the core GridFS implementation. Grid gives you a simple file store, keyed on a unique ID. This means that duplicate filenames aren't a problem. To use the Grid class, first make sure you have a database, and then instantiate a Grid:
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@db = Mongo::Connection.new.db('social_site')
|
|
11
|
+
@grid = Grid.new(@db)
|
|
12
|
+
|
|
13
|
+
#### Saving files
|
|
14
|
+
Once you have a Grid object, you can start saving data to it. The data can be either a string or an IO-like object that responds to a #read method:
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# Saving string data
|
|
18
|
+
id = @grid.put("here's some string / binary data")
|
|
19
|
+
|
|
20
|
+
# Saving IO data and including the optional filename
|
|
21
|
+
image = File.open("me.jpg")
|
|
22
|
+
id2 = @grid.put(image, :filename => "me.jpg")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
Grid#put returns an object id, which you can use to retrieve the file:
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# Get the string we saved
|
|
29
|
+
file = @grid.get(id)
|
|
30
|
+
|
|
31
|
+
# Get the file we saved
|
|
32
|
+
image = @grid.get(id2)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
#### File metadata
|
|
36
|
+
|
|
37
|
+
There are accessors for the various file attributes:
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
image.filename
|
|
41
|
+
# => "me.jpg"
|
|
42
|
+
|
|
43
|
+
image.content_type
|
|
44
|
+
# => "image/jpg"
|
|
45
|
+
|
|
46
|
+
image.file_length
|
|
47
|
+
# => 502357
|
|
48
|
+
|
|
49
|
+
image.upload_date
|
|
50
|
+
# => Mon Mar 01 16:18:30 UTC 2010
|
|
51
|
+
|
|
52
|
+
# Read all the image's data at once
|
|
53
|
+
image.read
|
|
54
|
+
|
|
55
|
+
# Read the first 100k bytes of the image
|
|
56
|
+
image.read(100 * 1024)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
When putting a file, you can set many of these attributes and write arbitrary metadata:
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
# Saving IO data
|
|
63
|
+
file = File.open("me.jpg")
|
|
64
|
+
id2 = @grid.put(file,
|
|
65
|
+
:filename => "my-avatar.jpg"
|
|
66
|
+
:content_type => "application/jpg",
|
|
67
|
+
:_id => 'a-unique-id-to-use-in-lieu-of-a-random-one',
|
|
68
|
+
:chunk_size => 100 * 1024,
|
|
69
|
+
:metadata => {'description' => "taken after a game of ultimate"})
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
#### Safe mode
|
|
73
|
+
|
|
74
|
+
A kind of safe mode is built into the GridFS specification. When you save a file, and MD5 hash is created on the server. If you save the file in safe mode, an MD5 will be created on the client for comparison with the server version. If the two hashes don't match, an exception will be raised.
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
image = File.open("me.jpg")
|
|
78
|
+
id2 = @grid.put(image, "my-avatar.jpg", :safe => true)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
#### Deleting files
|
|
82
|
+
|
|
83
|
+
Deleting a file is as simple as providing the id:
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@grid.delete(id2)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
### The GridFileSystem class
|
|
90
|
+
|
|
91
|
+
[GridFileSystem](http://api.mongodb.org/ruby/current/Mongo/GridFileSystem.html) is a light emulation of a file system and therefore has a couple of unique properties. The first is that filenames are assumed to be unique. The second, a consequence of the first, is that files are versioned. To see what this means, let's create a GridFileSystem instance:
|
|
92
|
+
|
|
93
|
+
#### Saving files
|
|
94
|
+
|
|
95
|
+
@db = Mongo::Connection.new.db("social_site")
|
|
96
|
+
@fs = GridFileSystem.new(@db)
|
|
97
|
+
|
|
98
|
+
Now suppose we want to save the file 'me.jpg.' This is easily done using a filesystem-like API:
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
image = File.open("me.jpg")
|
|
102
|
+
@fs.open("me.jpg", "w") do |f|
|
|
103
|
+
f.write image
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
We can then retrieve the file by filename:
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
image = @fs.open("me.jpg", "r") {|f| f.read }
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
No problems there. But what if we need to replace the file? That too is straightforward:
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
image = File.open("me-dancing.jpg")
|
|
117
|
+
@fs.open("me.jpg", "w") do |f|
|
|
118
|
+
f.write image
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
But a couple things need to be kept in mind. First is that the original 'me.jpg' will be available until the new 'me.jpg' saves. From then on, calls to the #open method will always return the most recently saved version of a file. But, and this the second point, old versions of the file won't be deleted. So if you're going to be rewriting files often, you could end up with a lot of old versions piling up. One solution to this is to use the :delete_old options when writing a file:
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
image = File.open("me-dancing.jpg")
|
|
126
|
+
@fs.open("me.jpg", "w", :delete_old => true) do |f|
|
|
127
|
+
f.write image
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
This will delete all but the latest version of the file.
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
#### Deleting files
|
|
135
|
+
|
|
136
|
+
When you delete a file by name, you delete all versions of that file:
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
@fs.delete("me.jpg")
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
#### Metadata and safe mode
|
|
143
|
+
|
|
144
|
+
All of the options for storing metadata and saving in safe mode are available for the GridFileSystem class:
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
image = File.open("me.jpg")
|
|
148
|
+
@fs.open('my-avatar.jpg', w,
|
|
149
|
+
:content_type => "application/jpg",
|
|
150
|
+
:metadata => {'description' => "taken on 3/1/2010 after a game of ultimate"},
|
|
151
|
+
:_id => 'a-unique-id-to-use-instead-of-the-automatically-generated-one',
|
|
152
|
+
:safe => true) { |f| f.write image }
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
### Advanced Users
|
|
156
|
+
|
|
157
|
+
Astute code readers will notice that the Grid and GridFileSystem classes are merely thin wrappers around an underlying [GridIO class](http://api.mongodb.org/ruby/current/Mongo/GridIO.html). This means that it's easy to customize the GridFS implementation presented here; just use GridIO for all the low-level work, and build the API you need in an external manager class similar to Grid or GridFileSystem.
|
|
158
|
+
|