mongo 1.9.2 → 1.10.0.rc0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/LICENSE +1 -1
- data/README.md +94 -334
- data/Rakefile +6 -4
- data/VERSION +1 -1
- data/bin/mongo_console +13 -6
- data/lib/mongo.rb +22 -27
- data/lib/mongo/bulk_write_collection_view.rb +352 -0
- data/lib/mongo/collection.rb +128 -188
- data/lib/mongo/collection_writer.rb +348 -0
- data/lib/mongo/connection.rb +19 -0
- data/lib/mongo/{util → connection}/node.rb +15 -1
- data/lib/mongo/{util → connection}/pool.rb +34 -19
- data/lib/mongo/{util → connection}/pool_manager.rb +8 -2
- data/lib/mongo/{util → connection}/sharding_pool_manager.rb +1 -1
- data/lib/mongo/connection/socket.rb +18 -0
- data/lib/mongo/{util → connection/socket}/socket_util.rb +5 -2
- data/lib/mongo/{util → connection/socket}/ssl_socket.rb +3 -4
- data/lib/mongo/{util → connection/socket}/tcp_socket.rb +25 -15
- data/lib/mongo/{util → connection/socket}/unix_socket.rb +6 -4
- data/lib/mongo/cursor.rb +113 -47
- data/lib/mongo/db.rb +203 -131
- data/lib/mongo/{exceptions.rb → exception.rb} +7 -1
- data/lib/mongo/functional.rb +19 -0
- data/lib/mongo/functional/authentication.rb +303 -0
- data/lib/mongo/{util → functional}/logging.rb +1 -1
- data/lib/mongo/{util → functional}/read_preference.rb +49 -1
- data/lib/mongo/{util → functional}/uri_parser.rb +81 -69
- data/lib/mongo/{util → functional}/write_concern.rb +2 -1
- data/{test/unit/pool_test.rb → lib/mongo/gridfs.rb} +5 -10
- data/lib/mongo/gridfs/grid.rb +1 -3
- data/lib/mongo/gridfs/grid_ext.rb +1 -1
- data/lib/mongo/gridfs/grid_file_system.rb +1 -1
- data/lib/mongo/gridfs/grid_io.rb +1 -1
- data/lib/mongo/legacy.rb +63 -8
- data/lib/mongo/mongo_client.rb +128 -154
- data/lib/mongo/mongo_replica_set_client.rb +17 -11
- data/lib/mongo/mongo_sharded_client.rb +2 -1
- data/lib/mongo/networking.rb +19 -10
- data/lib/mongo/utils.rb +19 -0
- data/lib/mongo/{util → utils}/conversions.rb +1 -1
- data/lib/mongo/{util → utils}/core_ext.rb +1 -1
- data/lib/mongo/{util → utils}/server_version.rb +1 -1
- data/lib/mongo/{util → utils}/support.rb +10 -57
- data/lib/mongo/{util → utils}/thread_local_variable_manager.rb +1 -1
- data/test/functional/authentication_test.rb +8 -21
- data/test/functional/bulk_write_collection_view_test.rb +782 -0
- data/test/functional/{connection_test.rb → client_test.rb} +153 -78
- data/test/functional/collection_test.rb +343 -97
- data/test/functional/collection_writer_test.rb +83 -0
- data/test/functional/conversions_test.rb +1 -3
- data/test/functional/cursor_fail_test.rb +3 -3
- data/test/functional/cursor_message_test.rb +3 -3
- data/test/functional/cursor_test.rb +38 -3
- data/test/functional/db_api_test.rb +5 -5
- data/test/functional/db_connection_test.rb +2 -2
- data/test/functional/db_test.rb +35 -11
- data/test/functional/grid_file_system_test.rb +2 -2
- data/test/functional/grid_io_test.rb +2 -2
- data/test/functional/grid_test.rb +2 -2
- data/test/functional/pool_test.rb +2 -3
- data/test/functional/safe_test.rb +5 -5
- data/test/functional/ssl_test.rb +22 -102
- data/test/functional/support_test.rb +1 -1
- data/test/functional/timeout_test.rb +6 -22
- data/test/functional/uri_test.rb +113 -12
- data/test/functional/write_concern_test.rb +6 -6
- data/test/helpers/general.rb +50 -0
- data/test/helpers/test_unit.rb +309 -0
- data/test/replica_set/authentication_test.rb +8 -23
- data/test/replica_set/basic_test.rb +41 -14
- data/test/replica_set/client_test.rb +179 -117
- data/test/replica_set/complex_connect_test.rb +6 -7
- data/test/replica_set/connection_test.rb +46 -38
- data/test/replica_set/count_test.rb +2 -2
- data/test/replica_set/cursor_test.rb +8 -8
- data/test/replica_set/insert_test.rb +64 -2
- data/test/replica_set/max_values_test.rb +59 -10
- data/test/replica_set/pinning_test.rb +2 -2
- data/test/replica_set/query_test.rb +2 -2
- data/test/replica_set/read_preference_test.rb +6 -6
- data/test/replica_set/refresh_test.rb +7 -7
- data/test/replica_set/replication_ack_test.rb +5 -5
- data/test/replica_set/ssl_test.rb +24 -106
- data/test/sharded_cluster/basic_test.rb +43 -15
- data/test/shared/authentication/basic_auth_shared.rb +215 -0
- data/test/shared/authentication/sasl_plain_shared.rb +96 -0
- data/test/shared/ssl_shared.rb +173 -0
- data/test/test_helper.rb +31 -199
- data/test/threading/basic_test.rb +29 -3
- data/test/tools/mongo_config.rb +45 -20
- data/test/tools/mongo_config_test.rb +1 -1
- data/test/unit/client_test.rb +136 -57
- data/test/unit/collection_test.rb +31 -55
- data/test/unit/connection_test.rb +135 -72
- data/test/unit/cursor_test.rb +2 -2
- data/test/unit/db_test.rb +19 -15
- data/test/unit/grid_test.rb +2 -2
- data/test/unit/mongo_sharded_client_test.rb +17 -15
- data/test/unit/node_test.rb +2 -2
- data/test/unit/pool_manager_test.rb +7 -5
- data/test/unit/read_pref_test.rb +82 -2
- data/test/unit/read_test.rb +14 -14
- data/test/unit/safe_test.rb +9 -9
- data/test/unit/sharding_pool_manager_test.rb +11 -5
- data/test/unit/write_concern_test.rb +9 -9
- metadata +71 -56
- metadata.gz.sig +0 -0
- data/test/functional/threading_test.rb +0 -109
- data/test/shared/authentication.rb +0 -121
- data/test/unit/util_test.rb +0 -69
data/Rakefile
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (C) 2013
|
1
|
+
# Copyright (C) 2009-2013 MongoDB, Inc.
|
2
2
|
#
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
4
|
# you may not use this file except in compliance with the License.
|
@@ -17,13 +17,15 @@ require 'rubygems'
|
|
17
17
|
begin
|
18
18
|
require 'bundler'
|
19
19
|
rescue LoadError
|
20
|
-
raise '[FAIL] Bundler not found! Install it with `gem install bundler
|
20
|
+
raise '[FAIL] Bundler not found! Install it with `gem install bundler && bundle`.'
|
21
21
|
end
|
22
22
|
|
23
|
-
|
23
|
+
rake_tasks = Dir.glob(File.join('tasks', '**', '*.rake')).sort
|
24
|
+
if ENV.keys.any? { |k| k.end_with?('_CI') }
|
24
25
|
Bundler.require(:default, :testing)
|
26
|
+
rake_tasks.reject! { |r| r =~ /deploy/ }
|
25
27
|
else
|
26
28
|
Bundler.require(:default, :testing, :deploy, :development)
|
27
29
|
end
|
28
30
|
|
29
|
-
|
31
|
+
rake_tasks.each { |rake| load File.expand_path(rake) }
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.10.0.rc0
|
data/bin/mongo_console
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
# Copyright (C) 2013
|
3
|
+
# Copyright (C) 2009-2013 MongoDB, Inc.
|
4
4
|
#
|
5
5
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
6
|
# you may not use this file except in compliance with the License.
|
@@ -17,11 +17,9 @@
|
|
17
17
|
org_argv = ARGV.dup
|
18
18
|
ARGV.clear
|
19
19
|
|
20
|
-
require 'irb'
|
21
|
-
|
22
20
|
$LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
|
23
|
-
require 'mongo'
|
24
21
|
|
22
|
+
require 'mongo'
|
25
23
|
include Mongo
|
26
24
|
|
27
25
|
host = org_argv[0] || ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
@@ -32,5 +30,14 @@ puts "Connecting to #{host}:#{port} (CLIENT) on with database #{dbnm} (DB)"
|
|
32
30
|
CLIENT = MongoClient.new(host, port)
|
33
31
|
DB = CLIENT.db(dbnm)
|
34
32
|
|
35
|
-
|
36
|
-
|
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
|
41
|
+
|
42
|
+
puts "Starting #{CONSOLE_CLASS.name} session..."
|
43
|
+
CONSOLE_CLASS.start(__FILE__)
|
data/lib/mongo.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (C) 2013
|
1
|
+
# Copyright (C) 2009-2013 MongoDB, Inc.
|
2
2
|
#
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
4
|
# you may not use this file except in compliance with the License.
|
@@ -56,39 +56,34 @@ module Mongo
|
|
56
56
|
REPLY_SHARD_CONFIG_STALE = 2 ** 2
|
57
57
|
REPLY_AWAIT_CAPABLE = 2 ** 3
|
58
58
|
end
|
59
|
+
|
60
|
+
module ErrorCode # MongoDB Core Server src/mongo/base/error_codes.err
|
61
|
+
BAD_VALUE = 2
|
62
|
+
UNKNOWN_ERROR = 8
|
63
|
+
INVALID_BSON = 22
|
64
|
+
COMMAND_NOT_FOUND = 59
|
65
|
+
WRITE_CONCERN_FAILED = 64
|
66
|
+
MULTIPLE_ERRORS_OCCURRED = 65
|
67
|
+
end
|
59
68
|
end
|
60
69
|
|
61
70
|
require 'bson'
|
62
71
|
|
63
|
-
require '
|
64
|
-
require '
|
65
|
-
require 'mongo/util/support'
|
66
|
-
require 'mongo/util/read_preference'
|
67
|
-
require 'mongo/util/write_concern'
|
68
|
-
require 'mongo/util/core_ext'
|
69
|
-
require 'mongo/util/logging'
|
70
|
-
require 'mongo/util/node'
|
71
|
-
require 'mongo/util/pool'
|
72
|
-
require 'mongo/util/pool_manager'
|
73
|
-
require 'mongo/util/sharding_pool_manager'
|
74
|
-
require 'mongo/util/server_version'
|
75
|
-
require 'mongo/util/socket_util'
|
76
|
-
require 'mongo/util/ssl_socket'
|
77
|
-
require 'mongo/util/tcp_socket'
|
78
|
-
require 'mongo/util/unix_socket'
|
79
|
-
require 'mongo/util/uri_parser'
|
80
|
-
|
72
|
+
require 'set'
|
73
|
+
require 'thread'
|
81
74
|
|
75
|
+
require 'mongo/utils'
|
76
|
+
require 'mongo/exception'
|
77
|
+
require 'mongo/functional'
|
78
|
+
require 'mongo/connection'
|
79
|
+
require 'mongo/collection_writer'
|
80
|
+
require 'mongo/collection'
|
81
|
+
require 'mongo/bulk_write_collection_view'
|
82
|
+
require 'mongo/cursor'
|
83
|
+
require 'mongo/db'
|
84
|
+
require 'mongo/gridfs'
|
82
85
|
require 'mongo/networking'
|
83
86
|
require 'mongo/mongo_client'
|
84
87
|
require 'mongo/mongo_replica_set_client'
|
85
88
|
require 'mongo/mongo_sharded_client'
|
86
89
|
require 'mongo/legacy'
|
87
|
-
require 'mongo/collection'
|
88
|
-
require 'mongo/cursor'
|
89
|
-
require 'mongo/db'
|
90
|
-
require 'mongo/exceptions'
|
91
|
-
require 'mongo/gridfs/grid_ext'
|
92
|
-
require 'mongo/gridfs/grid'
|
93
|
-
require 'mongo/gridfs/grid_io'
|
94
|
-
require 'mongo/gridfs/grid_file_system'
|
@@ -0,0 +1,352 @@
|
|
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 => {}}
|
22
|
+
MULTIPLE_ERRORS_MSG = "batch item errors occurred"
|
23
|
+
|
24
|
+
attr_reader :collection, :options, :ops, :op_args
|
25
|
+
|
26
|
+
# Initialize a bulk-write-view object to a collection with default query selector {}.
|
27
|
+
#
|
28
|
+
# A bulk write operation is initialized from a collection object.
|
29
|
+
# For example, for an ordered bulk write view:
|
30
|
+
#
|
31
|
+
# bulk = collection.initialize_ordered_bulk_op
|
32
|
+
#
|
33
|
+
# or for an unordered bulk write view:
|
34
|
+
#
|
35
|
+
# bulk = collection.initialize_unordered_bulk_op
|
36
|
+
#
|
37
|
+
# The bulk write view collects individual write operations together so that they can be
|
38
|
+
# executed as a batch for significant performance gains.
|
39
|
+
# The ordered bulk operation will execute each operation serially in order.
|
40
|
+
# Execution will stop at the first occurrence of an error for an ordered bulk operation.
|
41
|
+
# The unordered bulk operation will be executed and may take advantage of parallelism.
|
42
|
+
# There are no guarantees for the order of execution of the operations on the server.
|
43
|
+
# Execution will continue even if there are errors for an unordered bulk operation.
|
44
|
+
#
|
45
|
+
# A bulk operation is programmed as a sequence of individual operations.
|
46
|
+
# An individual operation is composed of a method chain of modifiers or setters terminated by a write method.
|
47
|
+
# A modify method sets a value on the current object.
|
48
|
+
# A set methods returns a duplicate of the current object with a value set.
|
49
|
+
# A terminator write method appends a write operation to the bulk batch collected in the view.
|
50
|
+
#
|
51
|
+
# The API supports mixing of write operation types in a bulk operation.
|
52
|
+
# However, server support affects the implementation and performance of bulk operations.
|
53
|
+
#
|
54
|
+
# MongoDB version 2.6 servers currently support only bulk commands of the same type.
|
55
|
+
# With an ordered bulk operation,
|
56
|
+
# contiguous individual ops of the same type can be batched into the same db request,
|
57
|
+
# and the next op of a different type must be sent separately in the next request.
|
58
|
+
# Performance will improve if you can arrange your ops to reduce the number of db requests.
|
59
|
+
# With an unordered bulk operation,
|
60
|
+
# individual ops can be grouped by type and sent in at most three requests,
|
61
|
+
# one each per insert, update, or delete.
|
62
|
+
#
|
63
|
+
# MongoDB pre-version 2.6 servers do not support bulk write commands.
|
64
|
+
# The bulk operation must be sent one request per individual op.
|
65
|
+
# This also applies to inserts in order to have accurate counts and error reporting.
|
66
|
+
#
|
67
|
+
# Important note on pre-2.6 performance:
|
68
|
+
# Performance is very poor compared to version 2.6.
|
69
|
+
# We recommend bulk operation with pre-2.6 only for compatibility or
|
70
|
+
# for development in preparation for version 2.6.
|
71
|
+
# For better performance with pre-version 2.6, use bulk insertion with Collection#insert.
|
72
|
+
#
|
73
|
+
# @param [Collection] collection the parent collection object
|
74
|
+
#
|
75
|
+
# @option opts [Boolean] :ordered (true) Set bulk execution for ordered or unordered
|
76
|
+
#
|
77
|
+
# @return [BulkWriteCollectionView]
|
78
|
+
def initialize(collection, options = {})
|
79
|
+
@collection = collection
|
80
|
+
@options = options
|
81
|
+
@ops = []
|
82
|
+
@op_args = DEFAULT_OP_ARGS.dup
|
83
|
+
end
|
84
|
+
|
85
|
+
def inspect
|
86
|
+
vars = [:@options, :@ops, :@op_args]
|
87
|
+
vars_inspect = vars.collect{|var| "#{var}=#{instance_variable_get(var).inspect}"}
|
88
|
+
"#<Mongo::BulkWriteCollectionView:0x#{self.object_id} " <<
|
89
|
+
"@collection=#<Mongo::Collection:0x#{@collection.object_id}>, #{vars_inspect.join(', ')}>"
|
90
|
+
end
|
91
|
+
|
92
|
+
# Modify the query selector for subsequent bulk write operations.
|
93
|
+
# The default query selector on creation of the bulk write view is {}.
|
94
|
+
#
|
95
|
+
# @param [Hash] q the query selector
|
96
|
+
#
|
97
|
+
# @return [BulkWriteCollectionView]
|
98
|
+
def find(q)
|
99
|
+
op_args_set(:q, q)
|
100
|
+
end
|
101
|
+
|
102
|
+
# Modify the upsert option argument for subsequent bulk write operations.
|
103
|
+
#
|
104
|
+
# @param [Boolean] value (true) the upsert option value
|
105
|
+
#
|
106
|
+
# @return [BulkWriteCollectionView]
|
107
|
+
def upsert!(value = true)
|
108
|
+
op_args_set(:upsert, value)
|
109
|
+
end
|
110
|
+
|
111
|
+
# Set the upsert option argument for subsequent bulk write operations.
|
112
|
+
#
|
113
|
+
# @param [Boolean] value (true) the upsert option value
|
114
|
+
#
|
115
|
+
# @return [BulkWriteCollectionView] a duplicated object
|
116
|
+
def upsert(value = true)
|
117
|
+
dup.upsert!(value)
|
118
|
+
end
|
119
|
+
|
120
|
+
# Update one document matching the selector.
|
121
|
+
#
|
122
|
+
# bulk.find({"a" => 1}).update_one({"$inc" => {"x" => 1}})
|
123
|
+
#
|
124
|
+
# Use the upsert! or upsert method to specify an upsert. For example:
|
125
|
+
#
|
126
|
+
# bulk.find({"a" => 1}).upsert.updateOne({"$inc" => {"x" => 1}})
|
127
|
+
#
|
128
|
+
# @param [Hash] u the update document
|
129
|
+
#
|
130
|
+
# @return [BulkWriteCollectionView]
|
131
|
+
def update_one(u)
|
132
|
+
raise MongoArgumentError, "document must start with an operator" unless update_doc?(u)
|
133
|
+
op_push([:update, @op_args.merge(:u => u, :multi => false)])
|
134
|
+
end
|
135
|
+
|
136
|
+
# Update all documents matching the selector. For example:
|
137
|
+
#
|
138
|
+
# bulk.find({"a" => 2}).update({"$inc" => {"x" => 2}})
|
139
|
+
#
|
140
|
+
# Use the upsert! or upsert method to specify an upsert. For example:
|
141
|
+
#
|
142
|
+
# bulk.find({"a" => 2}).upsert.update({"$inc" => {"x" => 2}})
|
143
|
+
#
|
144
|
+
# @param [Hash] u the update document
|
145
|
+
#
|
146
|
+
# @return [BulkWriteCollectionView]
|
147
|
+
def update(u)
|
148
|
+
raise MongoArgumentError, "document must start with an operator" unless update_doc?(u)
|
149
|
+
op_push([:update, @op_args.merge(:u => u, :multi => true)])
|
150
|
+
end
|
151
|
+
|
152
|
+
# Replace entire document (update with whole doc replace). For example:
|
153
|
+
#
|
154
|
+
# bulk.find({"a" => 3}).replace_one({"x" => 3})
|
155
|
+
#
|
156
|
+
# @param [Hash] u the replacement document
|
157
|
+
#
|
158
|
+
# @return [BulkWriteCollectionView]
|
159
|
+
def replace_one(u)
|
160
|
+
raise MongoArgumentError, "document must not contain any operators" unless replace_doc?(u)
|
161
|
+
op_push([:update, @op_args.merge(:u => u, :multi => false)])
|
162
|
+
end
|
163
|
+
|
164
|
+
# Remove a single document matching the selector. For example:
|
165
|
+
#
|
166
|
+
# bulk.find({"a" => 4}).remove_one;
|
167
|
+
#
|
168
|
+
# @return [BulkWriteCollectionView]
|
169
|
+
def remove_one
|
170
|
+
op_push([:delete, @op_args.merge(:limit => 1)])
|
171
|
+
end
|
172
|
+
|
173
|
+
# Remove all documents matching the selector. For example:
|
174
|
+
#
|
175
|
+
# bulk.find({"a" => 5}).remove;
|
176
|
+
#
|
177
|
+
# @return [BulkWriteCollectionView]
|
178
|
+
def remove
|
179
|
+
op_push([:delete, @op_args.merge(:limit => 0)])
|
180
|
+
end
|
181
|
+
|
182
|
+
# Insert a document. For example:
|
183
|
+
#
|
184
|
+
# bulk.insert({"x" => 4})
|
185
|
+
#
|
186
|
+
# @return [BulkWriteCollectionView]
|
187
|
+
def insert(document)
|
188
|
+
# TODO - check keys
|
189
|
+
op_push([:insert, {:d => document}])
|
190
|
+
end
|
191
|
+
|
192
|
+
# Execute the bulk operation, with an optional write concern overwriting the default w:1.
|
193
|
+
# For example:
|
194
|
+
#
|
195
|
+
# write_concern = {:w => 1, :j => 1}
|
196
|
+
# bulk.execute({write_concern})
|
197
|
+
#
|
198
|
+
# On return from execute, the bulk operation is cleared,
|
199
|
+
# but the selector and upsert settings are preserved.
|
200
|
+
#
|
201
|
+
# @return [BulkWriteCollectionView]
|
202
|
+
def execute(opts = {})
|
203
|
+
write_concern = get_write_concern(opts, @collection)
|
204
|
+
@ops.each_with_index{|op, index| op.last.merge!(:ord => index)} # infuse ordinal here to avoid issues with upsert
|
205
|
+
if @collection.db.connection.use_write_command?(write_concern)
|
206
|
+
errors, exchanges = @collection.command_writer.bulk_execute(@ops, @options, opts)
|
207
|
+
else
|
208
|
+
errors, exchanges = @collection.operation_writer.bulk_execute(@ops, @options, opts)
|
209
|
+
end
|
210
|
+
@ops = []
|
211
|
+
return true if exchanges.first[:response] == true # w 0 without GLE
|
212
|
+
result = merge_result(errors, exchanges)
|
213
|
+
raise BulkWriteError.new(MULTIPLE_ERRORS_MSG, Mongo::ErrorCode::MULTIPLE_ERRORS_OCCURRED, result) if !errors.empty? || result["writeConcernError"]
|
214
|
+
result
|
215
|
+
end
|
216
|
+
|
217
|
+
private
|
218
|
+
|
219
|
+
def hash_except(h, *keys)
|
220
|
+
keys.each { |key| h.delete(key) }
|
221
|
+
h
|
222
|
+
end
|
223
|
+
|
224
|
+
def hash_select(h, *keys)
|
225
|
+
Hash[*keys.zip(h.values_at(*keys)).flatten]
|
226
|
+
end
|
227
|
+
|
228
|
+
def tally(h, key, n)
|
229
|
+
h[key] = h.fetch(key, 0) + n
|
230
|
+
end
|
231
|
+
|
232
|
+
def append(h, key, obj)
|
233
|
+
h[key] = h.fetch(key, []) << obj
|
234
|
+
end
|
235
|
+
|
236
|
+
def concat(h, key, a)
|
237
|
+
h[key] = h.fetch(key, []) + a
|
238
|
+
end
|
239
|
+
|
240
|
+
def merge_index(h, exchange)
|
241
|
+
h.merge("index" => exchange[:batch][h.fetch("index", 0)][:ord])
|
242
|
+
end
|
243
|
+
|
244
|
+
def merge_indexes(a, exchange)
|
245
|
+
a.collect{|h| merge_index(h, exchange)}
|
246
|
+
end
|
247
|
+
|
248
|
+
def merge_result(errors, exchanges)
|
249
|
+
ok = 0
|
250
|
+
result = {"ok" => 0, "n" => 0}
|
251
|
+
unless errors.empty?
|
252
|
+
unless (writeErrors = errors.select { |error| error.class != Mongo::OperationFailure }).empty? # assignment
|
253
|
+
concat(result, "writeErrors",
|
254
|
+
writeErrors.collect { |error|
|
255
|
+
{"index" => error.result[:ord], "code" => error.error_code, "errmsg" => error.result[:error].message}
|
256
|
+
})
|
257
|
+
end
|
258
|
+
result.merge!("code" => Mongo::ErrorCode::MULTIPLE_ERRORS_OCCURRED, "errmsg" => MULTIPLE_ERRORS_MSG)
|
259
|
+
end
|
260
|
+
exchanges.each do |exchange|
|
261
|
+
response = exchange[:response]
|
262
|
+
ok += response["ok"].to_i
|
263
|
+
n = response["n"] || 0
|
264
|
+
op_type = exchange[:op_type]
|
265
|
+
if op_type == :insert
|
266
|
+
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
|
267
|
+
tally(result, "nInserted", n)
|
268
|
+
elsif op_type == :update
|
269
|
+
n_upserted = 0
|
270
|
+
if (upserted = response.fetch("upserted", nil)) # assignment
|
271
|
+
upserted = [{"_id" => upserted}] if upserted.class == BSON::ObjectId # OP_UPDATE non-array
|
272
|
+
n_upserted = upserted.size
|
273
|
+
concat(result, "upserted", merge_indexes(upserted, exchange))
|
274
|
+
end
|
275
|
+
tally(result, "nUpserted", n_upserted) if n_upserted > 0
|
276
|
+
tally(result, "nMatched", n - n_upserted)
|
277
|
+
tally(result, "nModified", response["nModified"] || n - n_upserted)
|
278
|
+
elsif op_type == :delete
|
279
|
+
tally(result, "nRemoved", n)
|
280
|
+
end
|
281
|
+
result["n"] += n
|
282
|
+
writeConcernError = nil
|
283
|
+
errmsg = response["errmsg"] || response["err"] # top level
|
284
|
+
if (writeErrors = response["writeErrors"] || response["errDetails"]) # assignment
|
285
|
+
concat(result, "writeErrors", merge_indexes(writeErrors, exchange))
|
286
|
+
elsif response["err"] == "timeout" # errmsg == "timed out waiting for slaves" # OP_*
|
287
|
+
writeConcernError = {"errmsg" => errmsg, "code" => Mongo::ErrorCode::WRITE_CONCERN_FAILED,
|
288
|
+
"errInfo" => {"wtimeout" => response["wtimeout"]}} # OP_* does not have "code"
|
289
|
+
elsif errmsg == "norepl" # OP_*
|
290
|
+
writeConcernError = {"errmsg" => errmsg, "code" => Mongo::ErrorCode::WRITE_CONCERN_FAILED} # OP_* does not have "code"
|
291
|
+
elsif errmsg # OP_INSERT, OP_UPDATE have "err"
|
292
|
+
append(result, "writeErrors", merge_index({"errmsg" => errmsg, "code" => response["code"]}, exchange))
|
293
|
+
end
|
294
|
+
if response["writeConcernError"]
|
295
|
+
writeConcernError = response["writeConcernError"]
|
296
|
+
elsif (wnote = response["wnote"]) # assignment - OP_*
|
297
|
+
writeConcernError = {"errmsg" => wnote, "code" => Mongo::ErrorCode::WRITE_CONCERN_FAILED} # OP_* does not have "code"
|
298
|
+
elsif (jnote = response["jnote"]) # assignment - OP_*
|
299
|
+
writeConcernError = {"errmsg" => jnote, "code" => Mongo::ErrorCode::BAD_VALUE} # OP_* does not have "code"
|
300
|
+
end
|
301
|
+
append(result, "writeConcernError", merge_index(writeConcernError, exchange)) if writeConcernError
|
302
|
+
end
|
303
|
+
result.merge!("ok" => [ok + result["n"], 1].min)
|
304
|
+
end
|
305
|
+
|
306
|
+
def initialize_copy(other)
|
307
|
+
other.instance_variable_set(:@options, other.options.dup)
|
308
|
+
end
|
309
|
+
|
310
|
+
def op_args_set(op, value)
|
311
|
+
@op_args[op] = value
|
312
|
+
self
|
313
|
+
end
|
314
|
+
|
315
|
+
def op_push(op)
|
316
|
+
@ops << op
|
317
|
+
self
|
318
|
+
end
|
319
|
+
|
320
|
+
def update_doc?(doc)
|
321
|
+
!doc.empty? && doc.keys.first.to_s =~ /^\$/
|
322
|
+
end
|
323
|
+
|
324
|
+
def replace_doc?(doc)
|
325
|
+
doc.keys.all?{|key| key !~ /^\$/}
|
326
|
+
end
|
327
|
+
|
328
|
+
end
|
329
|
+
|
330
|
+
class Collection
|
331
|
+
|
332
|
+
# Initialize an ordered bulk write view for this collection
|
333
|
+
# Execution will stop at the first occurrence of an error for an ordered bulk operation.
|
334
|
+
#
|
335
|
+
# @return [BulkWriteCollectionView]
|
336
|
+
def initialize_ordered_bulk_op
|
337
|
+
BulkWriteCollectionView.new(self, :ordered => true)
|
338
|
+
end
|
339
|
+
|
340
|
+
# Initialize an unordered bulk write view for this collection
|
341
|
+
# The unordered bulk operation will be executed and may take advantage of parallelism.
|
342
|
+
# There are no guarantees for the order of execution of the operations on the server.
|
343
|
+
# Execution will continue even if there are errors for an unordered bulk operation.
|
344
|
+
#
|
345
|
+
# @return [BulkWriteCollectionView]
|
346
|
+
def initialize_unordered_bulk_op
|
347
|
+
BulkWriteCollectionView.new(self, :ordered => false)
|
348
|
+
end
|
349
|
+
|
350
|
+
end
|
351
|
+
|
352
|
+
end
|