mongo 1.3.0 → 1.12.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.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data/{LICENSE.txt → LICENSE} +1 -1
- data/README.md +122 -271
- data/Rakefile +25 -209
- data/VERSION +1 -0
- data/bin/mongo_console +31 -9
- data/lib/mongo/bulk_write_collection_view.rb +387 -0
- data/lib/mongo/collection.rb +576 -269
- data/lib/mongo/collection_writer.rb +364 -0
- data/lib/mongo/connection/node.rb +249 -0
- data/lib/mongo/connection/pool.rb +340 -0
- data/lib/mongo/connection/pool_manager.rb +320 -0
- data/lib/mongo/connection/sharding_pool_manager.rb +67 -0
- data/lib/mongo/connection/socket/socket_util.rb +37 -0
- data/lib/mongo/connection/socket/ssl_socket.rb +95 -0
- data/lib/mongo/connection/socket/tcp_socket.rb +87 -0
- data/lib/mongo/connection/socket/unix_socket.rb +39 -0
- data/lib/mongo/connection/socket.rb +18 -0
- data/lib/mongo/connection.rb +7 -875
- data/lib/mongo/cursor.rb +403 -117
- data/lib/mongo/db.rb +444 -243
- data/lib/mongo/exception.rb +145 -0
- data/lib/mongo/functional/authentication.rb +455 -0
- data/lib/mongo/functional/logging.rb +85 -0
- data/lib/mongo/functional/read_preference.rb +183 -0
- data/lib/mongo/functional/scram.rb +556 -0
- data/lib/mongo/functional/uri_parser.rb +409 -0
- data/lib/mongo/functional/write_concern.rb +66 -0
- data/lib/mongo/functional.rb +20 -0
- data/lib/mongo/gridfs/grid.rb +30 -24
- data/lib/mongo/gridfs/grid_ext.rb +6 -10
- data/lib/mongo/gridfs/grid_file_system.rb +38 -20
- data/lib/mongo/gridfs/grid_io.rb +84 -75
- data/lib/mongo/gridfs.rb +18 -0
- data/lib/mongo/legacy.rb +140 -0
- data/lib/mongo/mongo_client.rb +697 -0
- data/lib/mongo/mongo_replica_set_client.rb +535 -0
- data/lib/mongo/mongo_sharded_client.rb +159 -0
- data/lib/mongo/networking.rb +372 -0
- data/lib/mongo/{util → utils}/conversions.rb +29 -8
- data/lib/mongo/{util → utils}/core_ext.rb +28 -18
- data/lib/mongo/{util → utils}/server_version.rb +4 -6
- data/lib/mongo/{util → utils}/support.rb +29 -31
- data/lib/mongo/utils/thread_local_variable_manager.rb +25 -0
- data/lib/mongo/utils.rb +19 -0
- data/lib/mongo.rb +51 -50
- data/mongo.gemspec +29 -32
- data/test/functional/authentication_test.rb +39 -0
- data/test/functional/bulk_api_stress_test.rb +133 -0
- data/test/functional/bulk_write_collection_view_test.rb +1198 -0
- data/test/functional/client_test.rb +627 -0
- data/test/functional/collection_test.rb +2175 -0
- data/test/functional/collection_writer_test.rb +83 -0
- data/test/{conversions_test.rb → functional/conversions_test.rb} +47 -3
- data/test/functional/cursor_fail_test.rb +57 -0
- data/test/functional/cursor_message_test.rb +56 -0
- data/test/functional/cursor_test.rb +683 -0
- data/test/functional/db_api_test.rb +835 -0
- data/test/functional/db_connection_test.rb +25 -0
- data/test/functional/db_test.rb +348 -0
- data/test/functional/grid_file_system_test.rb +285 -0
- data/test/{grid_io_test.rb → functional/grid_io_test.rb} +72 -11
- data/test/{grid_test.rb → functional/grid_test.rb} +88 -15
- data/test/functional/pool_test.rb +136 -0
- data/test/functional/safe_test.rb +98 -0
- data/test/functional/ssl_test.rb +29 -0
- data/test/functional/support_test.rb +62 -0
- data/test/functional/timeout_test.rb +60 -0
- data/test/functional/uri_test.rb +446 -0
- data/test/functional/write_concern_test.rb +118 -0
- data/test/helpers/general.rb +50 -0
- data/test/helpers/test_unit.rb +476 -0
- data/test/replica_set/authentication_test.rb +37 -0
- data/test/replica_set/basic_test.rb +189 -0
- data/test/replica_set/client_test.rb +393 -0
- data/test/replica_set/connection_test.rb +138 -0
- data/test/replica_set/count_test.rb +66 -0
- data/test/replica_set/cursor_test.rb +220 -0
- data/test/replica_set/insert_test.rb +157 -0
- data/test/replica_set/max_values_test.rb +151 -0
- data/test/replica_set/pinning_test.rb +105 -0
- data/test/replica_set/query_test.rb +73 -0
- data/test/replica_set/read_preference_test.rb +219 -0
- data/test/replica_set/refresh_test.rb +211 -0
- data/test/replica_set/replication_ack_test.rb +95 -0
- data/test/replica_set/ssl_test.rb +32 -0
- data/test/sharded_cluster/basic_test.rb +203 -0
- data/test/shared/authentication/basic_auth_shared.rb +260 -0
- data/test/shared/authentication/bulk_api_auth_shared.rb +249 -0
- data/test/shared/authentication/gssapi_shared.rb +176 -0
- data/test/shared/authentication/sasl_plain_shared.rb +96 -0
- data/test/shared/authentication/scram_shared.rb +92 -0
- data/test/shared/ssl_shared.rb +235 -0
- data/test/test_helper.rb +53 -94
- data/test/threading/basic_test.rb +120 -0
- data/test/tools/mongo_config.rb +708 -0
- data/test/tools/mongo_config_test.rb +160 -0
- data/test/unit/client_test.rb +381 -0
- data/test/unit/collection_test.rb +89 -53
- data/test/unit/connection_test.rb +282 -32
- data/test/unit/cursor_test.rb +206 -8
- data/test/unit/db_test.rb +55 -13
- data/test/unit/grid_test.rb +43 -16
- data/test/unit/mongo_sharded_client_test.rb +48 -0
- data/test/unit/node_test.rb +93 -0
- data/test/unit/pool_manager_test.rb +111 -0
- data/test/unit/read_pref_test.rb +406 -0
- data/test/unit/read_test.rb +159 -0
- data/test/unit/safe_test.rb +69 -36
- data/test/unit/sharding_pool_manager_test.rb +84 -0
- data/test/unit/write_concern_test.rb +175 -0
- data.tar.gz.sig +3 -0
- metadata +227 -216
- metadata.gz.sig +0 -0
- data/docs/CREDITS.md +0 -123
- data/docs/FAQ.md +0 -116
- data/docs/GridFS.md +0 -158
- data/docs/HISTORY.md +0 -244
- data/docs/RELEASES.md +0 -33
- data/docs/REPLICA_SETS.md +0 -72
- data/docs/TUTORIAL.md +0 -247
- data/docs/WRITE_CONCERN.md +0 -28
- data/lib/mongo/exceptions.rb +0 -71
- data/lib/mongo/gridfs/grid_io_fix.rb +0 -38
- data/lib/mongo/repl_set_connection.rb +0 -342
- data/lib/mongo/test.rb +0 -20
- data/lib/mongo/util/pool.rb +0 -177
- data/lib/mongo/util/uri_parser.rb +0 -185
- data/test/async/collection_test.rb +0 -224
- data/test/async/connection_test.rb +0 -24
- data/test/async/cursor_test.rb +0 -162
- data/test/async/worker_pool_test.rb +0 -99
- data/test/auxillary/1.4_features.rb +0 -166
- data/test/auxillary/authentication_test.rb +0 -68
- data/test/auxillary/autoreconnect_test.rb +0 -41
- data/test/auxillary/fork_test.rb +0 -30
- data/test/auxillary/repl_set_auth_test.rb +0 -58
- data/test/auxillary/slave_connection_test.rb +0 -36
- data/test/auxillary/threaded_authentication_test.rb +0 -101
- data/test/bson/binary_test.rb +0 -15
- data/test/bson/bson_test.rb +0 -649
- data/test/bson/byte_buffer_test.rb +0 -208
- data/test/bson/hash_with_indifferent_access_test.rb +0 -38
- data/test/bson/json_test.rb +0 -17
- data/test/bson/object_id_test.rb +0 -154
- data/test/bson/ordered_hash_test.rb +0 -204
- data/test/bson/timestamp_test.rb +0 -24
- data/test/collection_test.rb +0 -910
- data/test/connection_test.rb +0 -309
- data/test/cursor_fail_test.rb +0 -75
- data/test/cursor_message_test.rb +0 -43
- data/test/cursor_test.rb +0 -483
- data/test/db_api_test.rb +0 -726
- data/test/db_connection_test.rb +0 -15
- data/test/db_test.rb +0 -287
- data/test/grid_file_system_test.rb +0 -243
- data/test/load/resque/load.rb +0 -21
- data/test/load/resque/processor.rb +0 -26
- data/test/load/thin/load.rb +0 -24
- data/test/load/unicorn/load.rb +0 -23
- data/test/load/unicorn/unicorn.rb +0 -29
- data/test/replica_sets/connect_test.rb +0 -94
- data/test/replica_sets/connection_string_test.rb +0 -32
- data/test/replica_sets/count_test.rb +0 -35
- data/test/replica_sets/insert_test.rb +0 -53
- data/test/replica_sets/pooled_insert_test.rb +0 -55
- data/test/replica_sets/query_secondaries.rb +0 -96
- data/test/replica_sets/query_test.rb +0 -51
- data/test/replica_sets/replication_ack_test.rb +0 -66
- data/test/replica_sets/rs_test_helper.rb +0 -27
- data/test/safe_test.rb +0 -68
- data/test/support/hash_with_indifferent_access.rb +0 -186
- data/test/support/keys.rb +0 -45
- data/test/support_test.rb +0 -18
- data/test/threading/threading_with_large_pool_test.rb +0 -90
- data/test/threading_test.rb +0 -87
- data/test/tools/auth_repl_set_manager.rb +0 -14
- data/test/tools/load.rb +0 -58
- data/test/tools/repl_set_manager.rb +0 -266
- data/test/tools/sharding_manager.rb +0 -202
- data/test/tools/test.rb +0 -4
- data/test/unit/pool_test.rb +0 -9
- data/test/unit/repl_set_connection_test.rb +0 -59
- data/test/uri_test.rb +0 -91
data/Rakefile
CHANGED
@@ -1,215 +1,31 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
Rake::Task['test:ruby'].invoke
|
15
|
-
end
|
16
|
-
|
17
|
-
namespace :build do
|
18
|
-
desc "Build the java extensions."
|
19
|
-
task :java do
|
20
|
-
puts "Building Java extensions..."
|
21
|
-
java_dir = File.join(File.dirname(__FILE__), 'ext', 'java')
|
22
|
-
jar_dir = File.join(java_dir, 'jar')
|
23
|
-
|
24
|
-
jruby_jar = File.join(jar_dir, 'jruby.jar')
|
25
|
-
mongo_jar = File.join(jar_dir, 'mongo-2.4.jar')
|
26
|
-
bson_jar = File.join(jar_dir, 'bson-2.2.jar')
|
27
|
-
|
28
|
-
src_base = File.join(java_dir, 'src')
|
29
|
-
|
30
|
-
system("javac -Xlint:unchecked -classpath #{jruby_jar}:#{mongo_jar}:#{bson_jar} #{File.join(src_base, 'org', 'jbson', '*.java')}")
|
31
|
-
system("cd #{src_base} && jar cf #{File.join(jar_dir, 'jbson.jar')} #{File.join('.', 'org', 'jbson', '*.class')}")
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
desc "Test the MongoDB Ruby driver."
|
36
|
-
task :test do
|
37
|
-
puts "\nTo test the driver with the C-extensions:\nrake test:c\n\n"
|
38
|
-
puts "To test the pure ruby driver: \nrake test:ruby\n\n"
|
39
|
-
end
|
40
|
-
|
41
|
-
namespace :test do
|
42
|
-
|
43
|
-
desc "Test the driver with the C extension enabled."
|
44
|
-
task :c do
|
45
|
-
ENV['C_EXT'] = 'TRUE'
|
46
|
-
if ENV['TEST']
|
47
|
-
Rake::Task['test:functional'].invoke
|
48
|
-
else
|
49
|
-
Rake::Task['test:unit'].invoke
|
50
|
-
Rake::Task['test:functional'].invoke
|
51
|
-
Rake::Task['test:bson'].invoke
|
52
|
-
Rake::Task['test:pooled_threading'].invoke
|
53
|
-
Rake::Task['test:drop_databases'].invoke
|
54
|
-
end
|
55
|
-
ENV['C_EXT'] = nil
|
56
|
-
end
|
57
|
-
|
58
|
-
desc "Test the driver using pure ruby (no C extension)"
|
59
|
-
task :ruby do
|
60
|
-
ENV['C_EXT'] = nil
|
61
|
-
if ENV['TEST']
|
62
|
-
Rake::Task['test:functional'].invoke
|
63
|
-
else
|
64
|
-
Rake::Task['test:unit'].invoke
|
65
|
-
Rake::Task['test:functional'].invoke
|
66
|
-
Rake::Task['test:bson'].invoke
|
67
|
-
Rake::Task['test:pooled_threading'].invoke
|
68
|
-
Rake::Task['test:drop_databases'].invoke
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
desc "Run the replica set test suite"
|
73
|
-
Rake::TestTask.new(:rs) do |t|
|
74
|
-
t.test_files = FileList['test/replica_sets/*_test.rb']
|
75
|
-
t.verbose = true
|
76
|
-
t.ruby_opts << '-w'
|
77
|
-
end
|
78
|
-
|
79
|
-
Rake::TestTask.new(:unit) do |t|
|
80
|
-
t.test_files = FileList['test/unit/*_test.rb']
|
81
|
-
t.verbose = true
|
82
|
-
t.ruby_opts << '-w'
|
83
|
-
end
|
84
|
-
|
85
|
-
Rake::TestTask.new(:functional) do |t|
|
86
|
-
t.test_files = FileList['test/*_test.rb']
|
87
|
-
t.verbose = true
|
88
|
-
t.ruby_opts << '-w'
|
89
|
-
end
|
90
|
-
|
91
|
-
Rake::TestTask.new(:pooled_threading) do |t|
|
92
|
-
t.test_files = FileList['test/threading/*_test.rb']
|
93
|
-
t.verbose = true
|
94
|
-
t.ruby_opts << '-w'
|
95
|
-
end
|
96
|
-
|
97
|
-
Rake::TestTask.new(:auto_reconnect) do |t|
|
98
|
-
t.test_files = FileList['test/auxillary/autoreconnect_test.rb']
|
99
|
-
t.verbose = true
|
100
|
-
t.ruby_opts << '-w'
|
101
|
-
end
|
102
|
-
|
103
|
-
Rake::TestTask.new(:authentication) do |t|
|
104
|
-
t.test_files = FileList['test/auxillary/authentication_test.rb']
|
105
|
-
t.verbose = true
|
106
|
-
t.ruby_opts << '-w'
|
107
|
-
end
|
108
|
-
|
109
|
-
Rake::TestTask.new(:new_features) do |t|
|
110
|
-
t.test_files = FileList['test/auxillary/1.4_features.rb']
|
111
|
-
t.verbose = true
|
112
|
-
t.ruby_opts << '-w'
|
113
|
-
end
|
1
|
+
# Copyright (C) 2009-2013 MongoDB, Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
114
14
|
|
115
|
-
|
116
|
-
t.test_files = FileList['test/bson/*_test.rb']
|
117
|
-
t.verbose = true
|
118
|
-
t.ruby_opts << '-w'
|
119
|
-
end
|
120
|
-
|
121
|
-
task :drop_databases do |t|
|
122
|
-
puts "Dropping test databases..."
|
123
|
-
require './lib/mongo'
|
124
|
-
con = Mongo::Connection.new(ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost',
|
125
|
-
ENV['MONGO_RUBY_DRIVER_PORT'] || Mongo::Connection::DEFAULT_PORT)
|
126
|
-
con.database_names.each do |name|
|
127
|
-
con.drop_database(name) if name =~ /^ruby-test/
|
128
|
-
end
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
desc "Generate RDOC documentation"
|
133
|
-
task :rdoc do
|
134
|
-
version = eval(File.read("mongo.gemspec")).version
|
135
|
-
out = File.join('html', version.to_s)
|
136
|
-
FileUtils.rm_rf('html')
|
137
|
-
system "rdoc --main README.md --op #{out} --inline-source --quiet README.md `find lib -name '*.rb'`"
|
138
|
-
end
|
139
|
-
|
140
|
-
desc "Generate YARD documentation"
|
141
|
-
task :ydoc do
|
142
|
-
require File.join(File.dirname(__FILE__), 'lib', 'mongo')
|
143
|
-
out = File.join('ydoc', Mongo::VERSION)
|
144
|
-
FileUtils.rm_rf('ydoc')
|
145
|
-
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/RELEASES.md"
|
146
|
-
end
|
147
|
-
|
148
|
-
namespace :bamboo do
|
149
|
-
task :ci_reporter do
|
150
|
-
begin
|
151
|
-
require 'ci/reporter/rake/test_unit'
|
152
|
-
rescue LoadError
|
153
|
-
warn "Warning: Unable to load ci_reporter gem."
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
namespace :test do
|
158
|
-
task :ruby => [:ci_reporter, "ci:setup:testunit"] do
|
159
|
-
Rake::Task['test:ruby'].invoke
|
160
|
-
end
|
161
|
-
|
162
|
-
task :c => [:ci_reporter, "ci:setup:testunit"] do
|
163
|
-
Rake::Task['gem:install_extensions'].invoke
|
164
|
-
Rake::Task['test:c'].invoke
|
165
|
-
end
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
|
-
namespace :gem do
|
170
|
-
|
171
|
-
desc "Install the gem locally"
|
172
|
-
task :install do
|
173
|
-
`gem build bson.gemspec`
|
174
|
-
`gem install --no-rdoc --no-ri bson-*.gem`
|
175
|
-
|
176
|
-
`gem build mongo.gemspec`
|
177
|
-
`gem install --no-rdoc --no-ri mongo-*.gem`
|
178
|
-
|
179
|
-
`rm mongo-*.gem`
|
180
|
-
`rm bson-*.gem`
|
181
|
-
end
|
182
|
-
|
183
|
-
desc "Install the optional c extensions"
|
184
|
-
task :install_extensions do
|
185
|
-
`gem uninstall bson_ext`
|
186
|
-
`gem build bson_ext.gemspec`
|
187
|
-
`gem install --no-rdoc --no-ri bson_ext-*.gem`
|
188
|
-
`rm bson_ext-*.gem`
|
189
|
-
end
|
190
|
-
|
191
|
-
desc "Build all gems"
|
192
|
-
task :build_all do
|
193
|
-
`gem build mongo.gemspec`
|
194
|
-
`gem build bson.gemspec`
|
195
|
-
`gem build bson.java.gemspec`
|
196
|
-
`gem build bson_ext.gemspec`
|
197
|
-
end
|
15
|
+
require 'rubygems'
|
198
16
|
|
17
|
+
begin
|
18
|
+
require 'bundler'
|
19
|
+
rescue LoadError
|
20
|
+
raise '[FAIL] Bundler not found! Install it with `gem install bundler && bundle`.'
|
199
21
|
end
|
200
22
|
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
end
|
208
|
-
end
|
23
|
+
rake_tasks = Dir.glob(File.join('tasks', '**', '*.rake')).sort
|
24
|
+
if ENV.keys.any? { |k| k.end_with?('_CI') }
|
25
|
+
Bundler.require(:default, :testing)
|
26
|
+
rake_tasks.reject! { |r| r =~ /deploy/ }
|
27
|
+
else
|
28
|
+
Bundler.require(:default, :testing, :deploy, :development)
|
209
29
|
end
|
210
30
|
|
211
|
-
|
212
|
-
|
213
|
-
task :list do
|
214
|
-
system 'rake -T'
|
215
|
-
end
|
31
|
+
rake_tasks.each { |rake| load File.expand_path(rake) }
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.12.5
|
data/bin/mongo_console
CHANGED
@@ -1,21 +1,43 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Copyright (C) 2009-2013 MongoDB, Inc.
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
|
2
17
|
org_argv = ARGV.dup
|
3
18
|
ARGV.clear
|
4
19
|
|
5
|
-
require 'irb'
|
6
|
-
|
7
20
|
$LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
|
8
|
-
require 'mongo'
|
9
21
|
|
22
|
+
require 'mongo'
|
10
23
|
include Mongo
|
11
24
|
|
12
25
|
host = org_argv[0] || ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
13
|
-
port = org_argv[1] || ENV['MONGO_RUBY_DRIVER_PORT'] ||
|
26
|
+
port = org_argv[1] || ENV['MONGO_RUBY_DRIVER_PORT'] || MongoClient::DEFAULT_PORT
|
14
27
|
dbnm = org_argv[2] || ENV['MONGO_RUBY_DRIVER_DB'] || 'ruby-mongo-console'
|
15
28
|
|
16
|
-
puts "Connecting to #{host}:#{port} (
|
17
|
-
|
18
|
-
DB =
|
29
|
+
puts "Connecting to #{host}:#{port} (CLIENT) on with database #{dbnm} (DB)"
|
30
|
+
CLIENT = MongoClient.new(host, port)
|
31
|
+
DB = CLIENT.db(dbnm)
|
32
|
+
|
33
|
+
# try pry if available, fall back to irb
|
34
|
+
begin
|
35
|
+
require 'pry'
|
36
|
+
CONSOLE_CLASS = Pry
|
37
|
+
rescue LoadError
|
38
|
+
require 'irb'
|
39
|
+
CONSOLE_CLASS = IRB
|
40
|
+
end
|
19
41
|
|
20
|
-
puts "Starting
|
21
|
-
|
42
|
+
puts "Starting #{CONSOLE_CLASS.name} session..."
|
43
|
+
CONSOLE_CLASS.start(__FILE__)
|
@@ -0,0 +1,387 @@
|
|
1
|
+
# Copyright (C) 2009-2013 MongoDB, Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
module Mongo
|
16
|
+
|
17
|
+
# A bulk write view to a collection of documents in a database.
|
18
|
+
class BulkWriteCollectionView
|
19
|
+
include Mongo::WriteConcern
|
20
|
+
|
21
|
+
DEFAULT_OP_ARGS = {:q => nil}
|
22
|
+
MULTIPLE_ERRORS_MSG = "batch item errors occurred"
|
23
|
+
EMPTY_BATCH_MSG = "batch is empty"
|
24
|
+
|
25
|
+
attr_reader :collection, :options, :ops, :op_args
|
26
|
+
|
27
|
+
# Initialize a bulk-write-view object to a collection with default query selector {}.
|
28
|
+
#
|
29
|
+
# A bulk write operation is initialized from a collection object.
|
30
|
+
# For example, for an ordered bulk write view:
|
31
|
+
#
|
32
|
+
# bulk = collection.initialize_ordered_bulk_op
|
33
|
+
#
|
34
|
+
# or for an unordered bulk write view:
|
35
|
+
#
|
36
|
+
# bulk = collection.initialize_unordered_bulk_op
|
37
|
+
#
|
38
|
+
# The bulk write view collects individual write operations together so that they can be
|
39
|
+
# executed as a batch for significant performance gains.
|
40
|
+
# The ordered bulk operation will execute each operation serially in order.
|
41
|
+
# Execution will stop at the first occurrence of an error for an ordered bulk operation.
|
42
|
+
# The unordered bulk operation will be executed and may take advantage of parallelism.
|
43
|
+
# There are no guarantees for the order of execution of the operations on the server.
|
44
|
+
# Execution will continue even if there are errors for an unordered bulk operation.
|
45
|
+
#
|
46
|
+
# A bulk operation is programmed as a sequence of individual operations.
|
47
|
+
# An individual operation is composed of a method chain of modifiers or setters terminated by a write method.
|
48
|
+
# A modify method sets a value on the current object.
|
49
|
+
# A set methods returns a duplicate of the current object with a value set.
|
50
|
+
# A terminator write method appends a write operation to the bulk batch collected in the view.
|
51
|
+
#
|
52
|
+
# The API supports mixing of write operation types in a bulk operation.
|
53
|
+
# However, server support affects the implementation and performance of bulk operations.
|
54
|
+
#
|
55
|
+
# MongoDB version 2.6 servers currently support only bulk commands of the same type.
|
56
|
+
# With an ordered bulk operation,
|
57
|
+
# contiguous individual ops of the same type can be batched into the same db request,
|
58
|
+
# and the next op of a different type must be sent separately in the next request.
|
59
|
+
# Performance will improve if you can arrange your ops to reduce the number of db requests.
|
60
|
+
# With an unordered bulk operation,
|
61
|
+
# individual ops can be grouped by type and sent in at most three requests,
|
62
|
+
# one each per insert, update, or delete.
|
63
|
+
#
|
64
|
+
# MongoDB pre-version 2.6 servers do not support bulk write commands.
|
65
|
+
# The bulk operation must be sent one request per individual op.
|
66
|
+
# This also applies to inserts in order to have accurate counts and error reporting.
|
67
|
+
#
|
68
|
+
# Important note on pre-2.6 performance:
|
69
|
+
# Performance is very poor compared to version 2.6.
|
70
|
+
# We recommend bulk operation with pre-2.6 only for compatibility or
|
71
|
+
# for development in preparation for version 2.6.
|
72
|
+
# For better performance with pre-version 2.6, use bulk insertion with Collection#insert.
|
73
|
+
#
|
74
|
+
# @param [Collection] collection the parent collection object
|
75
|
+
#
|
76
|
+
# @option opts [Boolean] :ordered (true) Set bulk execution for ordered or unordered
|
77
|
+
#
|
78
|
+
# @return [BulkWriteCollectionView]
|
79
|
+
def initialize(collection, options = {})
|
80
|
+
@collection = collection
|
81
|
+
@options = options
|
82
|
+
@ops = []
|
83
|
+
@op_args = DEFAULT_OP_ARGS.dup
|
84
|
+
end
|
85
|
+
|
86
|
+
def inspect
|
87
|
+
vars = [:@options, :@ops, :@op_args]
|
88
|
+
vars_inspect = vars.collect{|var| "#{var}=#{instance_variable_get(var).inspect}"}
|
89
|
+
"#<Mongo::BulkWriteCollectionView:0x#{self.object_id} " <<
|
90
|
+
"@collection=#<Mongo::Collection:0x#{@collection.object_id}>, #{vars_inspect.join(', ')}>"
|
91
|
+
end
|
92
|
+
|
93
|
+
# Modify the query selector for subsequent bulk write operations.
|
94
|
+
# The default query selector on creation of the bulk write view is {}.
|
95
|
+
# For operations that require a query selector, find() must be set
|
96
|
+
# per operation, or set once for all operations on the bulk object.
|
97
|
+
# For example, these operations:
|
98
|
+
#
|
99
|
+
# bulk.find({"a" => 2}).update({"$inc" => {"x" => 2}})
|
100
|
+
# bulk.find({"a" => 2}).update({"$set" => {"b" => 3}})
|
101
|
+
#
|
102
|
+
# may be rewritten as:
|
103
|
+
#
|
104
|
+
# bulk = find({"a" => 2})
|
105
|
+
# bulk.update({"$inc" => {"x" => 2}})
|
106
|
+
# bulk.update({"$set" => {"b" => 3}})
|
107
|
+
#
|
108
|
+
# Note that modifying the query selector in this way will not affect
|
109
|
+
# operations that do not use a query selector, like insert().
|
110
|
+
#
|
111
|
+
# @param [Hash] q the query selector
|
112
|
+
#
|
113
|
+
# @return [BulkWriteCollectionView]
|
114
|
+
def find(q)
|
115
|
+
op_args_set(:q, q)
|
116
|
+
end
|
117
|
+
|
118
|
+
# Modify the upsert option argument for subsequent bulk write operations.
|
119
|
+
#
|
120
|
+
# @param [Boolean] value (true) the upsert option value
|
121
|
+
#
|
122
|
+
# @return [BulkWriteCollectionView]
|
123
|
+
def upsert!(value = true)
|
124
|
+
op_args_set(:upsert, value)
|
125
|
+
end
|
126
|
+
|
127
|
+
# Set the upsert option argument for subsequent bulk write operations.
|
128
|
+
#
|
129
|
+
# @param [Boolean] value (true) the upsert option value
|
130
|
+
#
|
131
|
+
# @return [BulkWriteCollectionView] a duplicated object
|
132
|
+
def upsert(value = true)
|
133
|
+
dup.upsert!(value)
|
134
|
+
end
|
135
|
+
|
136
|
+
# Update one document matching the selector.
|
137
|
+
#
|
138
|
+
# bulk.find({"a" => 1}).update_one({"$inc" => {"x" => 1}})
|
139
|
+
#
|
140
|
+
# Use the upsert! or upsert method to specify an upsert. For example:
|
141
|
+
#
|
142
|
+
# bulk.find({"a" => 1}).upsert.updateOne({"$inc" => {"x" => 1}})
|
143
|
+
#
|
144
|
+
# @param [Hash] u the update document
|
145
|
+
#
|
146
|
+
# @return [BulkWriteCollectionView]
|
147
|
+
def update_one(u)
|
148
|
+
raise MongoArgumentError, "document must start with an operator" unless update_doc?(u)
|
149
|
+
op_push([:update, @op_args.merge(:u => u, :multi => false)])
|
150
|
+
end
|
151
|
+
|
152
|
+
# Update all documents matching the selector. For example:
|
153
|
+
#
|
154
|
+
# bulk.find({"a" => 2}).update({"$inc" => {"x" => 2}})
|
155
|
+
#
|
156
|
+
# Use the upsert! or upsert method to specify an upsert. For example:
|
157
|
+
#
|
158
|
+
# bulk.find({"a" => 2}).upsert.update({"$inc" => {"x" => 2}})
|
159
|
+
#
|
160
|
+
# @param [Hash] u the update document
|
161
|
+
#
|
162
|
+
# @return [BulkWriteCollectionView]
|
163
|
+
def update(u)
|
164
|
+
raise MongoArgumentError, "document must start with an operator" unless update_doc?(u)
|
165
|
+
op_push([:update, @op_args.merge(:u => u, :multi => true)])
|
166
|
+
end
|
167
|
+
|
168
|
+
# Replace entire document (update with whole doc replace). For example:
|
169
|
+
#
|
170
|
+
# bulk.find({"a" => 3}).replace_one({"x" => 3})
|
171
|
+
#
|
172
|
+
# @param [Hash] u the replacement document
|
173
|
+
#
|
174
|
+
# @return [BulkWriteCollectionView]
|
175
|
+
def replace_one(u)
|
176
|
+
raise MongoArgumentError, "document must not contain any operators" unless replace_doc?(u)
|
177
|
+
op_push([:update, @op_args.merge(:u => u, :multi => false)])
|
178
|
+
end
|
179
|
+
|
180
|
+
# Remove a single document matching the selector. For example:
|
181
|
+
#
|
182
|
+
# bulk.find({"a" => 4}).remove_one;
|
183
|
+
#
|
184
|
+
# @return [BulkWriteCollectionView]
|
185
|
+
def remove_one
|
186
|
+
op_push([:delete, @op_args.merge(:limit => 1)])
|
187
|
+
end
|
188
|
+
|
189
|
+
# Remove all documents matching the selector. For example:
|
190
|
+
#
|
191
|
+
# bulk.find({"a" => 5}).remove;
|
192
|
+
#
|
193
|
+
# @return [BulkWriteCollectionView]
|
194
|
+
def remove
|
195
|
+
op_push([:delete, @op_args.merge(:limit => 0)])
|
196
|
+
end
|
197
|
+
|
198
|
+
# Insert a document. For example:
|
199
|
+
#
|
200
|
+
# bulk.insert({"x" => 4})
|
201
|
+
#
|
202
|
+
# @return [BulkWriteCollectionView]
|
203
|
+
def insert(document)
|
204
|
+
# TODO - check keys
|
205
|
+
op_push([:insert, {:d => document}])
|
206
|
+
end
|
207
|
+
|
208
|
+
# Execute the bulk operation, with an optional write concern overwriting the default w:1.
|
209
|
+
# For example:
|
210
|
+
#
|
211
|
+
# write_concern = {:w => 1, :j => 1}
|
212
|
+
# bulk.execute({write_concern})
|
213
|
+
#
|
214
|
+
# On return from execute, the bulk operation is cleared,
|
215
|
+
# but the selector and upsert settings are preserved.
|
216
|
+
#
|
217
|
+
# @return [BulkWriteCollectionView]
|
218
|
+
def execute(opts = {})
|
219
|
+
raise MongoArgumentError, EMPTY_BATCH_MSG if @ops.empty?
|
220
|
+
write_concern = get_write_concern(opts, @collection)
|
221
|
+
@ops.each_with_index{|op, index| op.last.merge!(:ord => index)} # infuse ordinal here to avoid issues with upsert
|
222
|
+
if @collection.db.connection.use_write_command?(write_concern)
|
223
|
+
errors, write_concern_errors, exchanges = @collection.command_writer.bulk_execute(@ops, @options, opts)
|
224
|
+
else
|
225
|
+
errors, write_concern_errors, exchanges = @collection.operation_writer.bulk_execute(@ops, @options, opts)
|
226
|
+
end
|
227
|
+
@ops = []
|
228
|
+
return true if errors.empty? && (exchanges.empty? || exchanges.first[:response] == true) # w 0 without GLE
|
229
|
+
result = merge_result(errors + write_concern_errors, exchanges)
|
230
|
+
raise BulkWriteError.new(MULTIPLE_ERRORS_MSG, Mongo::ErrorCode::MULTIPLE_ERRORS_OCCURRED, result) if !errors.empty? || !write_concern_errors.empty?
|
231
|
+
result
|
232
|
+
end
|
233
|
+
|
234
|
+
private
|
235
|
+
|
236
|
+
def hash_except(h, *keys)
|
237
|
+
keys.each { |key| h.delete(key) }
|
238
|
+
h
|
239
|
+
end
|
240
|
+
|
241
|
+
def hash_select(h, *keys)
|
242
|
+
Hash[*keys.zip(h.values_at(*keys)).flatten]
|
243
|
+
end
|
244
|
+
|
245
|
+
def tally(h, key, n)
|
246
|
+
h[key] = h.fetch(key, 0) + n
|
247
|
+
end
|
248
|
+
|
249
|
+
def nil_tally(h, key, n)
|
250
|
+
if !h.has_key?(key)
|
251
|
+
h[key] = n
|
252
|
+
elsif h[key]
|
253
|
+
h[key] = n ? h[key] + n : n
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
def append(h, key, obj)
|
258
|
+
h[key] = h.fetch(key, []) << obj
|
259
|
+
end
|
260
|
+
|
261
|
+
def concat(h, key, a)
|
262
|
+
h[key] = h.fetch(key, []) + a
|
263
|
+
end
|
264
|
+
|
265
|
+
def merge_index(h, exchange)
|
266
|
+
h.merge("index" => exchange[:batch][h.fetch("index", 0)][:ord])
|
267
|
+
end
|
268
|
+
|
269
|
+
def merge_indexes(a, exchange)
|
270
|
+
a.collect{|h| merge_index(h, exchange)}
|
271
|
+
end
|
272
|
+
|
273
|
+
def merge_result(errors, exchanges)
|
274
|
+
ok = 0
|
275
|
+
result = {"ok" => 0, "n" => 0}
|
276
|
+
unless errors.empty?
|
277
|
+
unless (writeErrors = errors.select { |error| error.class != Mongo::OperationFailure && error.class != WriteConcernError }).empty? # assignment
|
278
|
+
concat(result, "writeErrors",
|
279
|
+
writeErrors.collect { |error|
|
280
|
+
{"index" => error.result[:ord], "code" => error.error_code, "errmsg" => error.result[:error].message}
|
281
|
+
})
|
282
|
+
end
|
283
|
+
result.merge!("code" => Mongo::ErrorCode::MULTIPLE_ERRORS_OCCURRED, "errmsg" => MULTIPLE_ERRORS_MSG)
|
284
|
+
end
|
285
|
+
exchanges.each do |exchange|
|
286
|
+
response = exchange[:response]
|
287
|
+
next unless response
|
288
|
+
ok += response["ok"].to_i
|
289
|
+
n = response["n"] || 0
|
290
|
+
op_type = exchange[:op_type]
|
291
|
+
if op_type == :insert
|
292
|
+
n = 1 if response.key?("err") && (response["err"].nil? || response["err"] == "norepl" || response["err"] == "timeout") # OP_INSERT override n = 0 bug, n = exchange[:batch].size always 1
|
293
|
+
tally(result, "nInserted", n)
|
294
|
+
elsif op_type == :update
|
295
|
+
n_upserted = 0
|
296
|
+
if (upserted = response.fetch("upserted", nil)) # assignment
|
297
|
+
upserted = [{"_id" => upserted}] if upserted.class != Array # OP_UPDATE non-array
|
298
|
+
n_upserted = upserted.size
|
299
|
+
concat(result, "upserted", merge_indexes(upserted, exchange))
|
300
|
+
elsif (response["updatedExisting"] == false && n == 1)
|
301
|
+
# workaround for DRIVERS-151 (non-ObjectID _id fields in pre-2.6 servers)
|
302
|
+
op = exchange[:batch][0]
|
303
|
+
missing_id = op[:u].fetch(:_id, op[:q][:_id]) # _id in update document takes precedence
|
304
|
+
upserted = [ { "_id" => missing_id, "index" => 0 } ]
|
305
|
+
n_upserted = n
|
306
|
+
concat(result, "upserted", merge_indexes(upserted, exchange))
|
307
|
+
end
|
308
|
+
tally(result, "nUpserted", n_upserted) if n_upserted > 0
|
309
|
+
tally(result, "nMatched", n - n_upserted)
|
310
|
+
nil_tally(result, "nModified", response["nModified"])
|
311
|
+
elsif op_type == :delete
|
312
|
+
tally(result, "nRemoved", n)
|
313
|
+
end
|
314
|
+
result["n"] += n
|
315
|
+
write_concern_error = nil
|
316
|
+
errmsg = response["errmsg"] || response["err"] # top level
|
317
|
+
if (writeErrors = response["writeErrors"] || response["errDetails"]) # assignment
|
318
|
+
concat(result, "writeErrors", merge_indexes(writeErrors, exchange))
|
319
|
+
elsif response["err"] == "timeout" # errmsg == "timed out waiting for slaves" # OP_*
|
320
|
+
write_concern_error = {"errmsg" => errmsg, "code" => Mongo::ErrorCode::WRITE_CONCERN_FAILED,
|
321
|
+
"errInfo" => {"wtimeout" => response["wtimeout"]}} # OP_* does not have "code"
|
322
|
+
elsif errmsg == "norepl" # OP_*
|
323
|
+
write_concern_error = {"errmsg" => errmsg, "code" => Mongo::ErrorCode::WRITE_CONCERN_FAILED} # OP_* does not have "code"
|
324
|
+
elsif errmsg # OP_INSERT, OP_UPDATE have "err"
|
325
|
+
append(result, "writeErrors", merge_index({"errmsg" => errmsg, "code" => response["code"]}, exchange))
|
326
|
+
end
|
327
|
+
if response["writeConcernError"]
|
328
|
+
write_concern_error = response["writeConcernError"]
|
329
|
+
elsif (wnote = response["wnote"]) # assignment - OP_*
|
330
|
+
write_concern_error = {"errmsg" => wnote, "code" => Mongo::ErrorCode::WRITE_CONCERN_FAILED} # OP_* does not have "code"
|
331
|
+
elsif (jnote = response["jnote"]) # assignment - OP_*
|
332
|
+
write_concern_error = {"errmsg" => jnote, "code" => Mongo::ErrorCode::BAD_VALUE} # OP_* does not have "code"
|
333
|
+
end
|
334
|
+
append(result, "writeConcernError", merge_index(write_concern_error, exchange)) if write_concern_error
|
335
|
+
end
|
336
|
+
result.delete("nModified") if result.has_key?("nModified") && !result["nModified"]
|
337
|
+
result.merge!("ok" => [ok + result["n"], 1].min)
|
338
|
+
end
|
339
|
+
|
340
|
+
def initialize_copy(other)
|
341
|
+
other.instance_variable_set(:@options, other.options.dup)
|
342
|
+
end
|
343
|
+
|
344
|
+
def op_args_set(op, value)
|
345
|
+
@op_args[op] = value
|
346
|
+
self
|
347
|
+
end
|
348
|
+
|
349
|
+
def op_push(op)
|
350
|
+
raise MongoArgumentError, "non-nil query must be set via find" if op.first != :insert && !op.last[:q]
|
351
|
+
@ops << op
|
352
|
+
self
|
353
|
+
end
|
354
|
+
|
355
|
+
def update_doc?(doc)
|
356
|
+
!doc.empty? && doc.keys.first.to_s =~ /^\$/
|
357
|
+
end
|
358
|
+
|
359
|
+
def replace_doc?(doc)
|
360
|
+
doc.keys.all?{|key| key !~ /^\$/}
|
361
|
+
end
|
362
|
+
|
363
|
+
end
|
364
|
+
|
365
|
+
class Collection
|
366
|
+
|
367
|
+
# Initialize an ordered bulk write view for this collection
|
368
|
+
# Execution will stop at the first occurrence of an error for an ordered bulk operation.
|
369
|
+
#
|
370
|
+
# @return [BulkWriteCollectionView]
|
371
|
+
def initialize_ordered_bulk_op
|
372
|
+
BulkWriteCollectionView.new(self, :ordered => true)
|
373
|
+
end
|
374
|
+
|
375
|
+
# Initialize an unordered bulk write view for this collection
|
376
|
+
# The unordered bulk operation will be executed and may take advantage of parallelism.
|
377
|
+
# There are no guarantees for the order of execution of the operations on the server.
|
378
|
+
# Execution will continue even if there are errors for an unordered bulk operation.
|
379
|
+
#
|
380
|
+
# @return [BulkWriteCollectionView]
|
381
|
+
def initialize_unordered_bulk_op
|
382
|
+
BulkWriteCollectionView.new(self, :ordered => false)
|
383
|
+
end
|
384
|
+
|
385
|
+
end
|
386
|
+
|
387
|
+
end
|