mongo 1.10.0-java

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.
Files changed (116) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/LICENSE +190 -0
  5. data/README.md +149 -0
  6. data/Rakefile +31 -0
  7. data/VERSION +1 -0
  8. data/bin/mongo_console +43 -0
  9. data/ext/jsasl/target/jsasl.jar +0 -0
  10. data/lib/mongo.rb +90 -0
  11. data/lib/mongo/bulk_write_collection_view.rb +380 -0
  12. data/lib/mongo/collection.rb +1164 -0
  13. data/lib/mongo/collection_writer.rb +364 -0
  14. data/lib/mongo/connection.rb +19 -0
  15. data/lib/mongo/connection/node.rb +239 -0
  16. data/lib/mongo/connection/pool.rb +347 -0
  17. data/lib/mongo/connection/pool_manager.rb +325 -0
  18. data/lib/mongo/connection/sharding_pool_manager.rb +67 -0
  19. data/lib/mongo/connection/socket.rb +18 -0
  20. data/lib/mongo/connection/socket/socket_util.rb +37 -0
  21. data/lib/mongo/connection/socket/ssl_socket.rb +95 -0
  22. data/lib/mongo/connection/socket/tcp_socket.rb +86 -0
  23. data/lib/mongo/connection/socket/unix_socket.rb +39 -0
  24. data/lib/mongo/cursor.rb +719 -0
  25. data/lib/mongo/db.rb +735 -0
  26. data/lib/mongo/exception.rb +88 -0
  27. data/lib/mongo/functional.rb +21 -0
  28. data/lib/mongo/functional/authentication.rb +318 -0
  29. data/lib/mongo/functional/logging.rb +85 -0
  30. data/lib/mongo/functional/read_preference.rb +174 -0
  31. data/lib/mongo/functional/sasl_java.rb +48 -0
  32. data/lib/mongo/functional/uri_parser.rb +374 -0
  33. data/lib/mongo/functional/write_concern.rb +66 -0
  34. data/lib/mongo/gridfs.rb +18 -0
  35. data/lib/mongo/gridfs/grid.rb +112 -0
  36. data/lib/mongo/gridfs/grid_ext.rb +53 -0
  37. data/lib/mongo/gridfs/grid_file_system.rb +163 -0
  38. data/lib/mongo/gridfs/grid_io.rb +484 -0
  39. data/lib/mongo/legacy.rb +140 -0
  40. data/lib/mongo/mongo_client.rb +702 -0
  41. data/lib/mongo/mongo_replica_set_client.rb +523 -0
  42. data/lib/mongo/mongo_sharded_client.rb +159 -0
  43. data/lib/mongo/networking.rb +370 -0
  44. data/lib/mongo/utils.rb +19 -0
  45. data/lib/mongo/utils/conversions.rb +110 -0
  46. data/lib/mongo/utils/core_ext.rb +70 -0
  47. data/lib/mongo/utils/server_version.rb +69 -0
  48. data/lib/mongo/utils/support.rb +80 -0
  49. data/lib/mongo/utils/thread_local_variable_manager.rb +25 -0
  50. data/mongo.gemspec +36 -0
  51. data/test/functional/authentication_test.rb +35 -0
  52. data/test/functional/bulk_api_stress_test.rb +133 -0
  53. data/test/functional/bulk_write_collection_view_test.rb +1129 -0
  54. data/test/functional/client_test.rb +565 -0
  55. data/test/functional/collection_test.rb +2073 -0
  56. data/test/functional/collection_writer_test.rb +83 -0
  57. data/test/functional/conversions_test.rb +163 -0
  58. data/test/functional/cursor_fail_test.rb +63 -0
  59. data/test/functional/cursor_message_test.rb +57 -0
  60. data/test/functional/cursor_test.rb +625 -0
  61. data/test/functional/db_api_test.rb +819 -0
  62. data/test/functional/db_connection_test.rb +27 -0
  63. data/test/functional/db_test.rb +344 -0
  64. data/test/functional/grid_file_system_test.rb +285 -0
  65. data/test/functional/grid_io_test.rb +252 -0
  66. data/test/functional/grid_test.rb +273 -0
  67. data/test/functional/pool_test.rb +62 -0
  68. data/test/functional/safe_test.rb +98 -0
  69. data/test/functional/ssl_test.rb +29 -0
  70. data/test/functional/support_test.rb +62 -0
  71. data/test/functional/timeout_test.rb +58 -0
  72. data/test/functional/uri_test.rb +330 -0
  73. data/test/functional/write_concern_test.rb +118 -0
  74. data/test/helpers/general.rb +50 -0
  75. data/test/helpers/test_unit.rb +317 -0
  76. data/test/replica_set/authentication_test.rb +35 -0
  77. data/test/replica_set/basic_test.rb +174 -0
  78. data/test/replica_set/client_test.rb +341 -0
  79. data/test/replica_set/complex_connect_test.rb +77 -0
  80. data/test/replica_set/connection_test.rb +138 -0
  81. data/test/replica_set/count_test.rb +64 -0
  82. data/test/replica_set/cursor_test.rb +212 -0
  83. data/test/replica_set/insert_test.rb +140 -0
  84. data/test/replica_set/max_values_test.rb +145 -0
  85. data/test/replica_set/pinning_test.rb +55 -0
  86. data/test/replica_set/query_test.rb +73 -0
  87. data/test/replica_set/read_preference_test.rb +214 -0
  88. data/test/replica_set/refresh_test.rb +175 -0
  89. data/test/replica_set/replication_ack_test.rb +94 -0
  90. data/test/replica_set/ssl_test.rb +32 -0
  91. data/test/sharded_cluster/basic_test.rb +197 -0
  92. data/test/shared/authentication/basic_auth_shared.rb +286 -0
  93. data/test/shared/authentication/bulk_api_auth_shared.rb +259 -0
  94. data/test/shared/authentication/gssapi_shared.rb +164 -0
  95. data/test/shared/authentication/sasl_plain_shared.rb +96 -0
  96. data/test/shared/ssl_shared.rb +235 -0
  97. data/test/test_helper.rb +56 -0
  98. data/test/threading/basic_test.rb +120 -0
  99. data/test/tools/mongo_config.rb +608 -0
  100. data/test/tools/mongo_config_test.rb +160 -0
  101. data/test/unit/client_test.rb +347 -0
  102. data/test/unit/collection_test.rb +166 -0
  103. data/test/unit/connection_test.rb +325 -0
  104. data/test/unit/cursor_test.rb +299 -0
  105. data/test/unit/db_test.rb +136 -0
  106. data/test/unit/grid_test.rb +76 -0
  107. data/test/unit/mongo_sharded_client_test.rb +48 -0
  108. data/test/unit/node_test.rb +93 -0
  109. data/test/unit/pool_manager_test.rb +142 -0
  110. data/test/unit/read_pref_test.rb +115 -0
  111. data/test/unit/read_test.rb +159 -0
  112. data/test/unit/safe_test.rb +158 -0
  113. data/test/unit/sharding_pool_manager_test.rb +84 -0
  114. data/test/unit/write_concern_test.rb +175 -0
  115. metadata +260 -0
  116. metadata.gz.sig +0 -0
@@ -0,0 +1,735 @@
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 MongoDB database.
18
+ class DB
19
+ include Mongo::WriteConcern
20
+
21
+ SYSTEM_NAMESPACE_COLLECTION = 'system.namespaces'
22
+ SYSTEM_INDEX_COLLECTION = 'system.indexes'
23
+ SYSTEM_PROFILE_COLLECTION = 'system.profile'
24
+ SYSTEM_USER_COLLECTION = 'system.users'
25
+ SYSTEM_JS_COLLECTION = 'system.js'
26
+ SYSTEM_COMMAND_COLLECTION = '$cmd'
27
+ MAX_TIME_MS_CODE = 50
28
+
29
+ PROFILE_LEVEL = {
30
+ :off => 0,
31
+ :slow_only => 1,
32
+ :all => 2
33
+ }
34
+
35
+ # Counter for generating unique request ids.
36
+ @@current_request_id = 0
37
+
38
+ # Strict mode enforces collection existence checks. When +true+,
39
+ # asking for a collection that does not exist, or trying to create a
40
+ # collection that already exists, raises an error.
41
+ #
42
+ # Strict mode is disabled by default, but enabled (+true+) at any time.
43
+ #
44
+ # @deprecated Support for strict will be removed in version 2.0 of the driver.
45
+ def strict=(value)
46
+ unless ENV['TEST_MODE']
47
+ warn "Support for strict mode has been deprecated and will be " +
48
+ "removed in version 2.0 of the driver."
49
+ end
50
+ @strict = value
51
+ end
52
+
53
+ # Returns the value of the +strict+ flag.
54
+ #
55
+ # @deprecated Support for strict will be removed in version 2.0 of the driver.
56
+ def strict?
57
+ @strict
58
+ end
59
+
60
+ # The name of the database and the local write concern options.
61
+ attr_reader :name, :write_concern
62
+
63
+ # The Mongo::MongoClient instance connecting to the MongoDB server.
64
+ attr_reader :client
65
+
66
+ # for backward compatibility
67
+ alias_method :connection, :client
68
+
69
+ # The length of time that Collection.ensure_index should cache index calls
70
+ attr_accessor :cache_time
71
+
72
+ # Read Preference
73
+ attr_accessor :read, :tag_sets, :acceptable_latency
74
+
75
+ # Instances of DB are normally obtained by calling Mongo#db.
76
+ #
77
+ # @param [String] name the database name.
78
+ # @param [Mongo::MongoClient] client a connection object pointing to MongoDB. Note
79
+ # that databases are usually instantiated via the MongoClient class. See the examples below.
80
+ #
81
+ # @option opts [Boolean] :strict (False) [DEPRECATED] If true, collections existence checks are
82
+ # performed during a number of relevant operations. See DB#collection, DB#create_collection and
83
+ # DB#drop_collection.
84
+ #
85
+ # @option opts [Object, #create_pk(doc)] :pk (BSON::ObjectId) A primary key factory object,
86
+ # which should take a hash and return a hash which merges the original hash with any primary key
87
+ # fields the factory wishes to inject. (NOTE: if the object already has a primary key,
88
+ # the factory should not inject a new key).
89
+ #
90
+ # @option opts [String, Integer, Symbol] :w (1) Set default number of nodes to which a write
91
+ # should be acknowledged.
92
+ # @option opts [Integer] :wtimeout (nil) Set replica set acknowledgement timeout.
93
+ # @option opts [Boolean] :j (false) If true, block until write operations have been committed
94
+ # to the journal. Cannot be used in combination with 'fsync'. Prior to MongoDB 2.6 this option was
95
+ # ignored if the server was running without journaling. Starting with MongoDB 2.6, write operations will
96
+ # fail with an exception if this option is used when the server is running without journaling.
97
+ # @option opts [Boolean] :fsync (false) If true, and the server is running without journaling, blocks until
98
+ # the server has synced all data files to disk. If the server is running with journaling, this acts the same as
99
+ # the 'j' option, blocking until write operations have been committed to the journal.
100
+ # Cannot be used in combination with 'j'.
101
+ #
102
+ # Notes on write concern:
103
+ # These write concern options are propagated to Collection objects instantiated off of this DB. If no
104
+ # options are provided, the default write concern set on this instance's MongoClient object will be used. This
105
+ # default can be overridden upon instantiation of any collection by explicitly setting write concern options
106
+ # on initialization or at the time of an operation.
107
+ #
108
+ # @option opts [Integer] :cache_time (300) Set the time that all ensure_index calls should cache the command.
109
+
110
+ def initialize(name, client, opts={})
111
+ # A database name of '$external' is permitted for some auth types
112
+ Support.validate_db_name(name) unless name == '$external'
113
+
114
+ @name = name
115
+ @client = client
116
+ @strict = opts[:strict]
117
+ @pk_factory = opts[:pk]
118
+
119
+ @write_concern = get_write_concern(opts, client)
120
+
121
+ @read = opts[:read] || @client.read
122
+ ReadPreference::validate(@read)
123
+
124
+ @tag_sets = opts.fetch(:tag_sets, @client.tag_sets)
125
+ @acceptable_latency = opts.fetch(:acceptable_latency,
126
+ @client.acceptable_latency)
127
+
128
+ @cache_time = opts[:cache_time] || 300 #5 minutes.
129
+ end
130
+
131
+ # Authenticate with the given username and password.
132
+ #
133
+ # @param username [String] The username.
134
+ # @param password [String] The user's password. This is not required for
135
+ # some authentication mechanisms.
136
+ # @param save_auth [Boolean]
137
+ # Save this authentication to the client object using
138
+ # MongoClient#add_auth. This will ensure that the authentication will
139
+ # be applied to all sockets and upon database reconnect.
140
+ # @param source [String] Database with user credentials. This should be
141
+ # used to authenticate against a database when the credentials exist
142
+ # elsewhere.
143
+ # @param mechanism [String] The authentication mechanism to be used.
144
+ # @param extra [Hash] A optional hash of extra options to be stored with
145
+ # the credential set.
146
+ #
147
+ # @note The ability to disable the save_auth option has been deprecated.
148
+ # With save_auth=false specified, driver authentication behavior during
149
+ # failovers and reconnections becomes unreliable. This option still
150
+ # exists for API compatibility, but it no longer has any effect if
151
+ # disabled and now always uses the default behavior (safe_auth=true).
152
+ #
153
+ # @raise [AuthenticationError] Raised if authentication fails.
154
+ # @return [Boolean] The result of the authentication operation.
155
+ def authenticate(username, password=nil, save_auth=nil, source=nil, mechanism=nil, extra=nil)
156
+ warn "[DEPRECATED] Disabling the 'save_auth' option no longer has " +
157
+ "any effect. Please see the API documentation for more details " +
158
+ "on this change." unless save_auth.nil?
159
+
160
+ @client.add_auth(self.name, username, password, source, mechanism, extra)
161
+ true
162
+ end
163
+
164
+ # Deauthorizes use for this database for this client connection. Also removes
165
+ # the saved authentication in the MongoClient class associated with this
166
+ # database.
167
+ #
168
+ # @return [Boolean]
169
+ def logout(opts={})
170
+ @client.remove_auth(self.name)
171
+ true
172
+ end
173
+
174
+ # Adds a stored Javascript function to the database which can executed
175
+ # server-side in map_reduce, db.eval and $where clauses.
176
+ #
177
+ # @param [String] function_name
178
+ # @param [String] code
179
+ #
180
+ # @return [String] the function name saved to the database
181
+ def add_stored_function(function_name, code)
182
+ self[SYSTEM_JS_COLLECTION].save(
183
+ {
184
+ "_id" => function_name,
185
+ :value => BSON::Code.new(code)
186
+ }
187
+ )
188
+ end
189
+
190
+ # Removes stored Javascript function from the database. Returns
191
+ # false if the function does not exist
192
+ #
193
+ # @param [String] function_name
194
+ #
195
+ # @return [Boolean]
196
+ def remove_stored_function(function_name)
197
+ return false unless self[SYSTEM_JS_COLLECTION].find_one({"_id" => function_name})
198
+ self[SYSTEM_JS_COLLECTION].remove({"_id" => function_name}, :w => 1)
199
+ end
200
+
201
+ # Adds a user to this database for use with authentication. If the user already
202
+ # exists in the system, the password and any additional fields provided in opts
203
+ # will be updated.
204
+ #
205
+ # @param [String] username
206
+ # @param [String] password
207
+ # @param [Boolean] read_only
208
+ # Create a read-only user.
209
+ #
210
+ # @param [Hash] opts
211
+ # Optional fields for the user document (e.g. +userSource+, or +roles+)
212
+ #
213
+ # See {http://docs.mongodb.org/manual/reference/privilege-documents}
214
+ # for more information.
215
+ #
216
+ # @note The use of the opts argument to provide or update additional fields
217
+ # on the user document requires MongoDB >= 2.4.0
218
+ #
219
+ # @return [Hash] an object representing the user.
220
+ def add_user(username, password=nil, read_only=false, opts={})
221
+ begin
222
+ user_info = command(:usersInfo => username)
223
+ # MongoDB >= 2.5.3 requires the use of commands to manage users.
224
+ # "Command not found" error didn't return an error code (59) before
225
+ # MongoDB 2.4.7 so we assume that a nil error code means the usersInfo
226
+ # command doesn't exist and we should fall back to the legacy add user code.
227
+ rescue OperationFailure => ex
228
+ raise ex unless ex.error_code == Mongo::ErrorCode::COMMAND_NOT_FOUND || ex.error_code.nil?
229
+ return legacy_add_user(username, password, read_only, opts)
230
+ end
231
+
232
+ if user_info.key?('users') && !user_info['users'].empty?
233
+ create_or_update_user(:updateUser, username, password, read_only, opts)
234
+ else
235
+ create_or_update_user(:createUser, username, password, read_only, opts)
236
+ end
237
+ end
238
+
239
+ # Remove the given user from this database. Returns false if the user
240
+ # doesn't exist in the system.
241
+ #
242
+ # @param [String] username
243
+ #
244
+ # @return [Boolean]
245
+ def remove_user(username)
246
+ begin
247
+ command(:dropUser => username)
248
+ rescue OperationFailure => ex
249
+ raise ex unless ex.error_code == Mongo::ErrorCode::COMMAND_NOT_FOUND || ex.error_code.nil?
250
+ response = self[SYSTEM_USER_COLLECTION].remove({:user => username}, :w => 1)
251
+ response.key?('n') && response['n'] > 0 ? response : false
252
+ end
253
+ end
254
+
255
+ # Get an array of collection names in this database.
256
+ #
257
+ # @return [Array]
258
+ def collection_names
259
+ names = collections_info.collect { |doc| doc['name'] || '' }
260
+ names = names.delete_if {|name| name.index(@name).nil? || name.index('$')}
261
+ names.map {|name| name.sub(@name + '.', '')}
262
+ end
263
+
264
+ # Get an array of Collection instances, one for each collection in this database.
265
+ #
266
+ # @return [Array<Mongo::Collection>]
267
+ def collections
268
+ collection_names.map do |name|
269
+ Collection.new(name, self)
270
+ end
271
+ end
272
+
273
+ # Get info on system namespaces (collections). This method returns
274
+ # a cursor which can be iterated over. For each collection, a hash
275
+ # will be yielded containing a 'name' string and, optionally, an 'options' hash.
276
+ #
277
+ # @param [String] coll_name return info for the specified collection only.
278
+ #
279
+ # @return [Mongo::Cursor]
280
+ def collections_info(coll_name=nil)
281
+ selector = {}
282
+ selector[:name] = full_collection_name(coll_name) if coll_name
283
+ Cursor.new(Collection.new(SYSTEM_NAMESPACE_COLLECTION, self), :selector => selector)
284
+ end
285
+
286
+ # Create a collection.
287
+ #
288
+ # new collection. If +strict+ is true, will raise an error if
289
+ # collection +name+ already exists.
290
+ #
291
+ # @param [String, Symbol] name the name of the new collection.
292
+ #
293
+ # @option opts [Boolean] :capped (False) created a capped collection.
294
+ #
295
+ # @option opts [Integer] :size (Nil) If +capped+ is +true+,
296
+ # specifies the maximum number of bytes for the capped collection.
297
+ # If +false+, specifies the number of bytes allocated
298
+ # for the initial extent of the collection.
299
+ #
300
+ # @option opts [Integer] :max (Nil) If +capped+ is +true+, indicates
301
+ # the maximum number of records in a capped collection.
302
+ #
303
+ # @raise [MongoDBError] raised under two conditions:
304
+ # either we're in +strict+ mode and the collection
305
+ # already exists or collection creation fails on the server.
306
+ #
307
+ # @return [Mongo::Collection]
308
+ def create_collection(name, opts={})
309
+ name = name.to_s
310
+ if strict? && collection_names.include?(name)
311
+ raise MongoDBError, "Collection '#{name}' already exists. (strict=true)"
312
+ end
313
+
314
+ begin
315
+ cmd = BSON::OrderedHash.new
316
+ cmd[:create] = name
317
+ doc = command(cmd.merge(opts || {}))
318
+ return Collection.new(name, self, :pk => @pk_factory) if ok?(doc)
319
+ rescue OperationFailure => e
320
+ return Collection.new(name, self, :pk => @pk_factory) if e.message =~ /exists/
321
+ raise e
322
+ end
323
+ raise MongoDBError, "Error creating collection: #{doc.inspect}"
324
+ end
325
+
326
+ # Get a collection by name.
327
+ #
328
+ # @param [String, Symbol] name the collection name.
329
+ # @param [Hash] opts any valid options that can be passed to Collection#new.
330
+ #
331
+ # @raise [MongoDBError] if collection does not already exist and we're in
332
+ # +strict+ mode.
333
+ #
334
+ # @return [Mongo::Collection]
335
+ def collection(name, opts={})
336
+ if strict? && !collection_names.include?(name.to_s)
337
+ raise MongoDBError, "Collection '#{name}' doesn't exist. (strict=true)"
338
+ else
339
+ opts = opts.dup
340
+ opts.merge!(:pk => @pk_factory) unless opts[:pk]
341
+ Collection.new(name, self, opts)
342
+ end
343
+ end
344
+ alias_method :[], :collection
345
+
346
+ # Drop a collection by +name+.
347
+ #
348
+ # @param [String, Symbol] name
349
+ #
350
+ # @return [Boolean] +true+ on success or +false+ if the collection name doesn't exist.
351
+ def drop_collection(name)
352
+ return false if strict? && !collection_names.include?(name.to_s)
353
+ begin
354
+ ok?(command(:drop => name))
355
+ rescue OperationFailure
356
+ false
357
+ end
358
+ end
359
+
360
+ # Run the getlasterror command with the specified replication options.
361
+ #
362
+ # @option opts [Boolean] :fsync (false)
363
+ # @option opts [Integer] :w (nil)
364
+ # @option opts [Integer] :wtimeout (nil)
365
+ # @option opts [Boolean] :j (false)
366
+ #
367
+ # @return [Hash] the entire response to getlasterror.
368
+ #
369
+ # @raise [MongoDBError] if the operation fails.
370
+ def get_last_error(opts={})
371
+ cmd = BSON::OrderedHash.new
372
+ cmd[:getlasterror] = 1
373
+ cmd.merge!(opts)
374
+ doc = command(cmd, :check_response => false)
375
+ raise MongoDBError, "Error retrieving last error: #{doc.inspect}" unless ok?(doc)
376
+ doc
377
+ end
378
+
379
+ # Return +true+ if an error was caused by the most recently executed
380
+ # database operation.
381
+ #
382
+ # @return [Boolean]
383
+ def error?
384
+ get_last_error['err'] != nil
385
+ end
386
+
387
+ # Get the most recent error to have occurred on this database.
388
+ #
389
+ # This command only returns errors that have occurred since the last call to
390
+ # DB#reset_error_history - returns +nil+ if there is no such error.
391
+ #
392
+ # @return [String, Nil] the text of the error or +nil+ if no error has occurred.
393
+ def previous_error
394
+ error = command(:getpreverror => 1)
395
+ error["err"] ? error : nil
396
+ end
397
+
398
+ # Reset the error history of this database
399
+ #
400
+ # Calls to DB#previous_error will only return errors that have occurred
401
+ # since the most recent call to this method.
402
+ #
403
+ # @return [Hash]
404
+ def reset_error_history
405
+ command(:reseterror => 1)
406
+ end
407
+
408
+ # Dereference a DBRef, returning the document it points to.
409
+ #
410
+ # @param [Mongo::DBRef] dbref
411
+ #
412
+ # @return [Hash] the document indicated by the db reference.
413
+ #
414
+ # @see http://www.mongodb.org/display/DOCS/DB+Ref MongoDB DBRef spec.
415
+ def dereference(dbref)
416
+ collection(dbref.namespace).find_one("_id" => dbref.object_id)
417
+ end
418
+
419
+ # Evaluate a JavaScript expression in MongoDB.
420
+ #
421
+ # @param [String, Code] code a JavaScript expression to evaluate server-side.
422
+ # @param [Integer, Hash] args any additional argument to be passed to the +code+ expression when
423
+ # it's run on the server.
424
+ #
425
+ # @return [String] the return value of the function.
426
+ def eval(code, *args)
427
+ unless code.is_a?(BSON::Code)
428
+ code = BSON::Code.new(code)
429
+ end
430
+
431
+ cmd = BSON::OrderedHash.new
432
+ cmd[:$eval] = code
433
+ cmd.merge!(args.pop) if args.last.respond_to?(:keys) && args.last.key?(:nolock)
434
+ cmd[:args] = args
435
+ doc = command(cmd)
436
+ doc['retval']
437
+ end
438
+
439
+ # Rename a collection.
440
+ #
441
+ # @param [String] from original collection name.
442
+ # @param [String] to new collection name.
443
+ #
444
+ # @return [True] returns +true+ on success.
445
+ #
446
+ # @raise MongoDBError if there's an error renaming the collection.
447
+ def rename_collection(from, to)
448
+ cmd = BSON::OrderedHash.new
449
+ cmd[:renameCollection] = "#{@name}.#{from}"
450
+ cmd[:to] = "#{@name}.#{to}"
451
+ doc = DB.new('admin', @client).command(cmd, :check_response => false)
452
+ ok?(doc) || raise(MongoDBError, "Error renaming collection: #{doc.inspect}")
453
+ end
454
+
455
+ # Drop an index from a given collection. Normally called from
456
+ # Collection#drop_index or Collection#drop_indexes.
457
+ #
458
+ # @param [String] collection_name
459
+ # @param [String] index_name
460
+ #
461
+ # @return [True] returns +true+ on success.
462
+ #
463
+ # @raise MongoDBError if there's an error dropping the index.
464
+ def drop_index(collection_name, index_name)
465
+ cmd = BSON::OrderedHash.new
466
+ cmd[:deleteIndexes] = collection_name
467
+ cmd[:index] = index_name.to_s
468
+ doc = command(cmd, :check_response => false)
469
+ ok?(doc) || raise(MongoDBError, "Error with drop_index command: #{doc.inspect}")
470
+ end
471
+
472
+ # Get information on the indexes for the given collection.
473
+ # Normally called by Collection#index_information.
474
+ #
475
+ # @param [String] collection_name
476
+ #
477
+ # @return [Hash] keys are index names and the values are lists of [key, type] pairs
478
+ # defining the index.
479
+ def index_information(collection_name)
480
+ sel = {:ns => full_collection_name(collection_name)}
481
+ info = {}
482
+ Cursor.new(Collection.new(SYSTEM_INDEX_COLLECTION, self), :selector => sel).each do |index|
483
+ info[index['name']] = index
484
+ end
485
+ info
486
+ end
487
+
488
+ # Return stats on this database. Uses MongoDB's dbstats command.
489
+ #
490
+ # @return [Hash]
491
+ def stats
492
+ self.command(:dbstats => 1)
493
+ end
494
+
495
+ # Return +true+ if the supplied +doc+ contains an 'ok' field with the value 1.
496
+ #
497
+ # @param [Hash] doc
498
+ #
499
+ # @return [Boolean]
500
+ def ok?(doc)
501
+ Mongo::Support.ok?(doc)
502
+ end
503
+
504
+ # Send a command to the database.
505
+ #
506
+ # Note: DB commands must start with the "command" key. For this reason,
507
+ # any selector containing more than one key must be an OrderedHash.
508
+ #
509
+ # Note also that a command in MongoDB is just a kind of query
510
+ # that occurs on the system command collection ($cmd). Examine this method's implementation
511
+ # to see how it works.
512
+ #
513
+ # @param [OrderedHash, Hash] selector an OrderedHash, or a standard Hash with just one
514
+ # key, specifying the command to be performed. In Ruby 1.9 and above, OrderedHash isn't necessary
515
+ # because hashes are ordered by default.
516
+ #
517
+ # @option opts [Boolean] :check_response (true) If +true+, raises an exception if the
518
+ # command fails.
519
+ # @option opts [Socket] :socket a socket to use for sending the command. This is mainly for internal use.
520
+ # @option opts [:primary, :secondary] :read Read preference for this command. See Collection#find for
521
+ # more details.
522
+ # @option opts [String] :comment (nil) a comment to include in profiling logs
523
+ # @option opts [Boolean] :compile_regex (true) whether BSON regex objects should be compiled into Ruby regexes.
524
+ # If false, a BSON::Regex object will be returned instead.
525
+ #
526
+ # @return [Hash]
527
+ def command(selector, opts={})
528
+ raise MongoArgumentError, "Command must be given a selector" unless selector.respond_to?(:keys) && !selector.empty?
529
+
530
+ opts = opts.dup
531
+ # deletes :check_response and returns the value, if nil defaults to the block result
532
+ check_response = opts.delete(:check_response) { true }
533
+
534
+ # build up the command hash
535
+ command = opts.key?(:socket) ? { :socket => opts.delete(:socket) } : {}
536
+ command.merge!(:comment => opts.delete(:comment)) if opts.key?(:comment)
537
+ command.merge!(:compile_regex => opts.delete(:compile_regex)) if opts.key?(:compile_regex)
538
+ command[:limit] = -1
539
+ command[:read] = Mongo::ReadPreference::cmd_read_pref(opts.delete(:read), selector) if opts.key?(:read)
540
+
541
+ if RUBY_VERSION < '1.9' && selector.class != BSON::OrderedHash
542
+ if selector.keys.length > 1
543
+ raise MongoArgumentError, "DB#command requires an OrderedHash when hash contains multiple keys"
544
+ end
545
+ if opts.keys.size > 0
546
+ # extra opts will be merged into the selector, so make sure it's an OH in versions < 1.9
547
+ selector = selector.dup
548
+ selector = BSON::OrderedHash.new.merge!(selector)
549
+ end
550
+ end
551
+
552
+ # arbitrary opts are merged into the selector
553
+ command[:selector] = selector.merge!(opts)
554
+
555
+ begin
556
+ result = Cursor.new(system_command_collection, command).next_document
557
+ rescue OperationFailure => ex
558
+ if check_response
559
+ raise ex.class.new("Database command '#{selector.keys.first}' failed: #{ex.message}", ex.error_code, ex.result)
560
+ else
561
+ result = ex.result
562
+ end
563
+ end
564
+
565
+ raise OperationFailure,
566
+ "Database command '#{selector.keys.first}' failed: returned null." unless result
567
+
568
+ if check_response && (!ok?(result) || result['writeErrors'] || result['writeConcernError'])
569
+ message = "Database command '#{selector.keys.first}' failed: ("
570
+ message << result.map do |key, value|
571
+ "#{key}: '#{value}'"
572
+ end.join('; ')
573
+ message << ').'
574
+ code = result['code'] || result['assertionCode']
575
+ raise ExecutionTimeout.new(message, code, result) if code == MAX_TIME_MS_CODE
576
+ raise OperationFailure.new(message, code, result)
577
+ end
578
+
579
+ result
580
+ end
581
+
582
+ # A shortcut returning db plus dot plus collection name.
583
+ #
584
+ # @param [String] collection_name
585
+ #
586
+ # @return [String]
587
+ def full_collection_name(collection_name)
588
+ "#{@name}.#{collection_name}"
589
+ end
590
+
591
+ # The primary key factory object (or +nil+).
592
+ #
593
+ # @return [Object, Nil]
594
+ def pk_factory
595
+ @pk_factory
596
+ end
597
+
598
+ # Specify a primary key factory if not already set.
599
+ #
600
+ # @raise [MongoArgumentError] if the primary key factory has already been set.
601
+ def pk_factory=(pk_factory)
602
+ raise MongoArgumentError,
603
+ "Cannot change primary key factory once it's been set" if @pk_factory
604
+
605
+ @pk_factory = pk_factory
606
+ end
607
+
608
+ # Return the current database profiling level. If profiling is enabled, you can
609
+ # get the results using DB#profiling_info.
610
+ #
611
+ # @return [Symbol] :off, :slow_only, or :all
612
+ def profiling_level
613
+ cmd = BSON::OrderedHash.new
614
+ cmd[:profile] = -1
615
+ doc = command(cmd, :check_response => false)
616
+
617
+ raise "Error with profile command: #{doc.inspect}" unless ok?(doc)
618
+
619
+ level_sym = PROFILE_LEVEL.invert[doc['was'].to_i]
620
+ raise "Error: illegal profiling level value #{doc['was']}" unless level_sym
621
+ level_sym
622
+ end
623
+
624
+ # Set this database's profiling level. If profiling is enabled, you can
625
+ # get the results using DB#profiling_info.
626
+ #
627
+ # @param [Symbol] level acceptable options are +:off+, +:slow_only+, or +:all+.
628
+ def profiling_level=(level)
629
+ cmd = BSON::OrderedHash.new
630
+ cmd[:profile] = PROFILE_LEVEL[level]
631
+ doc = command(cmd, :check_response => false)
632
+ ok?(doc) || raise(MongoDBError, "Error with profile command: #{doc.inspect}")
633
+ end
634
+
635
+ # Get the current profiling information.
636
+ #
637
+ # @return [Array] a list of documents containing profiling information.
638
+ def profiling_info
639
+ Cursor.new(Collection.new(SYSTEM_PROFILE_COLLECTION, self), :selector => {}).to_a
640
+ end
641
+
642
+ # Validate a named collection.
643
+ #
644
+ # @param [String] name the collection name.
645
+ #
646
+ # @return [Hash] validation information.
647
+ #
648
+ # @raise [MongoDBError] if the command fails or there's a problem with the validation
649
+ # data, or if the collection is invalid.
650
+ def validate_collection(name)
651
+ cmd = BSON::OrderedHash.new
652
+ cmd[:validate] = name
653
+ cmd[:full] = true
654
+ doc = command(cmd, :check_response => false)
655
+
656
+ raise MongoDBError, "Error with validate command: #{doc.inspect}" unless ok?(doc)
657
+
658
+ if (doc.has_key?('valid') && !doc['valid']) || (doc['result'] =~ /\b(exception|corrupt)\b/i)
659
+ raise MongoDBError, "Error: invalid collection #{name}: #{doc.inspect}"
660
+ end
661
+ doc
662
+ end
663
+
664
+ private
665
+
666
+ def system_command_collection
667
+ Collection.new(SYSTEM_COMMAND_COLLECTION, self)
668
+ end
669
+
670
+ # Create a new user.
671
+ #
672
+ # @param username [String] The username.
673
+ # @param password [String] The user's password.
674
+ # @param read_only [Boolean] Create a read-only user (deprecated in MongoDB >= 2.6)
675
+ # @param opts [Hash]
676
+ #
677
+ # @private
678
+ def create_or_update_user(command, username, password, read_only, opts)
679
+ if read_only || !opts.key?(:roles)
680
+ warn "Creating a user with the read_only option or without roles is " +
681
+ "deprecated in MongoDB >= 2.6"
682
+ end
683
+
684
+ # The password is always salted and hashed by the driver.
685
+ if opts.key?(:digestPassword)
686
+ raise MongoArgumentError,
687
+ "The digestPassword option is not available via DB#add_user. " +
688
+ "Use DB#command(:createUser => ...) instead for this option."
689
+ end
690
+
691
+ opts = opts.dup
692
+ pwd = Mongo::Authentication.hash_password(username, password) if password
693
+ cmd_opts = pwd ? { :pwd => pwd } : {}
694
+ # specify that the server shouldn't digest the password because the driver does
695
+ cmd_opts[:digestPassword] = false
696
+ unless opts.key?(:roles)
697
+ if name == 'admin'
698
+ roles = read_only ? ['readAnyDatabase'] : ['root']
699
+ else
700
+ roles = read_only ? ['read'] : ["dbOwner"]
701
+ end
702
+ cmd_opts[:roles] = roles
703
+ end
704
+ cmd_opts[:writeConcern] =
705
+ opts.key?(:writeConcern) ? opts.delete(:writeConcern) : { :w => 1 }
706
+ cmd_opts.merge!(opts)
707
+ command({ command => username }, cmd_opts)
708
+ end
709
+
710
+ # Create a user in MongoDB versions < 2.5.3.
711
+ # Called by #add_user if the 'usersInfo' command fails.
712
+ #
713
+ # @param username [String] The username.
714
+ # @param password [String] (nil) The user's password.
715
+ # @param read_only [Boolean] (false) Create a read-only user.
716
+ # @param opts [Hash]
717
+ #
718
+ # @private
719
+ def legacy_add_user(username, password=nil, read_only=false, opts={})
720
+ users = self[SYSTEM_USER_COLLECTION]
721
+ user = users.find_one(:user => username) || {:user => username}
722
+ user['pwd'] =
723
+ Mongo::Authentication.hash_password(username, password) if password
724
+ user['readOnly'] = true if read_only
725
+ user.merge!(opts)
726
+ begin
727
+ users.save(user)
728
+ rescue OperationFailure => ex
729
+ # adding first admin user fails GLE in MongoDB 2.2
730
+ raise ex unless ex.message =~ /login/
731
+ end
732
+ user
733
+ end
734
+ end
735
+ end