jmongo 1.0.3 → 1.1.0

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 (96) hide show
  1. data/Gemfile +8 -0
  2. data/Gemfile.lock +43 -0
  3. data/Rakefile +72 -0
  4. data/jmongo.gemspec +84 -6
  5. data/lib/jmongo.rb +6 -14
  6. data/lib/jmongo/collection.rb +196 -114
  7. data/lib/jmongo/connection.rb +39 -13
  8. data/lib/jmongo/cursor.rb +161 -63
  9. data/lib/jmongo/db.rb +119 -30
  10. data/lib/jmongo/exceptions.rb +39 -0
  11. data/lib/jmongo/mongo-2.6.5.gb1.jar +0 -0
  12. data/lib/jmongo/mongo/bson.rb +130 -0
  13. data/lib/jmongo/mongo/collection.rb +185 -0
  14. data/lib/jmongo/mongo/connection.rb +45 -0
  15. data/lib/jmongo/mongo/db.rb +31 -0
  16. data/lib/jmongo/mongo/jmongo.rb +44 -0
  17. data/lib/jmongo/mongo/mongo.rb +98 -0
  18. data/lib/jmongo/mongo/ruby_ext.rb +38 -0
  19. data/lib/jmongo/mongo/utils.rb +136 -0
  20. data/lib/jmongo/version.rb +1 -1
  21. data/test-results.txt +98 -0
  22. data/test/auxillary/1.4_features.rb +166 -0
  23. data/test/auxillary/authentication_test.rb +68 -0
  24. data/test/auxillary/autoreconnect_test.rb +41 -0
  25. data/test/auxillary/fork_test.rb +30 -0
  26. data/test/auxillary/repl_set_auth_test.rb +58 -0
  27. data/test/auxillary/slave_connection_test.rb +36 -0
  28. data/test/auxillary/threaded_authentication_test.rb +101 -0
  29. data/test/bson/binary_test.rb +15 -0
  30. data/test/bson/bson_test.rb +657 -0
  31. data/test/bson/byte_buffer_test.rb +208 -0
  32. data/test/bson/hash_with_indifferent_access_test.rb +38 -0
  33. data/test/bson/json_test.rb +17 -0
  34. data/test/bson/object_id_test.rb +138 -0
  35. data/test/bson/ordered_hash_test.rb +245 -0
  36. data/test/bson/test_helper.rb +46 -0
  37. data/test/bson/timestamp_test.rb +46 -0
  38. data/test/collection_test.rb +933 -0
  39. data/test/connection_test.rb +325 -0
  40. data/test/conversions_test.rb +121 -0
  41. data/test/cursor_fail_test.rb +75 -0
  42. data/test/cursor_message_test.rb +43 -0
  43. data/test/cursor_test.rb +547 -0
  44. data/test/data/empty_data +0 -0
  45. data/test/data/sample_data +0 -0
  46. data/test/data/sample_file.pdf +0 -0
  47. data/test/data/small_data.txt +1 -0
  48. data/test/db_api_test.rb +739 -0
  49. data/test/db_connection_test.rb +15 -0
  50. data/test/db_test.rb +325 -0
  51. data/test/grid_file_system_test.rb +260 -0
  52. data/test/grid_io_test.rb +210 -0
  53. data/test/grid_test.rb +259 -0
  54. data/test/load/thin/config.ru +6 -0
  55. data/test/load/thin/config.yml.template +6 -0
  56. data/test/load/thin/load.rb +24 -0
  57. data/test/load/unicorn/config.ru +6 -0
  58. data/test/load/unicorn/load.rb +23 -0
  59. data/test/load/unicorn/unicorn.rb.template +29 -0
  60. data/test/replica_sets/connect_test.rb +111 -0
  61. data/test/replica_sets/connection_string_test.rb +29 -0
  62. data/test/replica_sets/count_test.rb +36 -0
  63. data/test/replica_sets/insert_test.rb +54 -0
  64. data/test/replica_sets/pooled_insert_test.rb +58 -0
  65. data/test/replica_sets/query_secondaries.rb +109 -0
  66. data/test/replica_sets/query_test.rb +52 -0
  67. data/test/replica_sets/read_preference_test.rb +43 -0
  68. data/test/replica_sets/refresh_test.rb +123 -0
  69. data/test/replica_sets/replication_ack_test.rb +71 -0
  70. data/test/replica_sets/rs_test_helper.rb +27 -0
  71. data/test/safe_test.rb +68 -0
  72. data/test/support/hash_with_indifferent_access.rb +186 -0
  73. data/test/support/keys.rb +45 -0
  74. data/test/support_test.rb +19 -0
  75. data/test/test_helper.rb +111 -0
  76. data/test/threading/threading_with_large_pool_test.rb +90 -0
  77. data/test/threading_test.rb +88 -0
  78. data/test/tools/auth_repl_set_manager.rb +14 -0
  79. data/test/tools/keyfile.txt +1 -0
  80. data/test/tools/repl_set_manager.rb +377 -0
  81. data/test/unit/collection_test.rb +128 -0
  82. data/test/unit/connection_test.rb +85 -0
  83. data/test/unit/cursor_test.rb +127 -0
  84. data/test/unit/db_test.rb +96 -0
  85. data/test/unit/grid_test.rb +51 -0
  86. data/test/unit/node_test.rb +73 -0
  87. data/test/unit/pool_manager_test.rb +47 -0
  88. data/test/unit/pool_test.rb +9 -0
  89. data/test/unit/read_test.rb +101 -0
  90. data/test/unit/safe_test.rb +125 -0
  91. data/test/uri_test.rb +92 -0
  92. metadata +170 -99
  93. data/lib/jmongo/ajrb.rb +0 -189
  94. data/lib/jmongo/jmongo_jext.rb +0 -302
  95. data/lib/jmongo/mongo-2.6.3.jar +0 -0
  96. data/lib/jmongo/utils.rb +0 -61
@@ -20,23 +20,30 @@ module Mongo
20
20
  include Mongo::JavaImpl::Connection_::InstanceMethods
21
21
  extend Mongo::JavaImpl::Connection_::ClassMethods
22
22
 
23
- attr_reader :connection, :logger, :auths
23
+ attr_reader :connection, :connector, :logger, :auths, :primary
24
+
25
+ DEFAULT_PORT = 27017
24
26
 
25
27
  def initialize host = nil, port = nil, opts = {}
26
28
  if opts.has_key?(:new_from_uri)
27
- @connection = opts[:new_from_uri]
29
+ @options = opts
30
+ @mongo_uri = opts[:new_from_uri]
31
+ @connection = JMongo::Mongo.new(@mongo_uri)
28
32
  else
29
33
  @logger = opts[:logger]
30
34
  @host = host || 'localhost'
31
35
  @port = port || 27017
32
- server_address = JMongo::ServerAddress.new @host, @port
33
- options = JMongo::MongoOptions.new
34
- options.connectionsPerHost = opts[:pool_size] || 1
35
- options.socketTimeout = opts[:timeout].to_i * 1000 || 5000
36
- @connection = JMongo::Mongo.new(server_address, options)
36
+ @server_address = JMongo::ServerAddress.new @host, @port
37
+ @options = JMongo::MongoOptions.new
38
+ @options.connectionsPerHost = opts[:pool_size] || 1
39
+ @options.socketTimeout = opts[:timeout].to_i * 1000 || 5000
40
+ @connection = JMongo::Mongo.new(@server_address, @options)
37
41
  end
42
+ @connector = @connection.connector
38
43
  @logger = opts[:logger]
39
44
  @auths = opts.fetch(:auths, [])
45
+ add = @connector.address
46
+ @primary = [add.host, add.port]
40
47
  end
41
48
 
42
49
  def self.paired(nodes, opts={})
@@ -147,7 +154,7 @@ module Mongo
147
154
  #
148
155
  # @core databases []-instance_method
149
156
  def [](db_name)
150
- DB.new db_name, self
157
+ db db_name
151
158
  end
152
159
 
153
160
  # Drop a database.
@@ -176,11 +183,18 @@ module Mongo
176
183
  raise_not_implemented
177
184
  end
178
185
 
186
+ # Checks if a server is alive. This command will return immediately
187
+ # even if the server is in a lock.
188
+ #
189
+ # @return [Hash]
190
+ def ping
191
+ db("admin").command('ping')
192
+ end
179
193
  # Get the build information for the current connection.
180
194
  #
181
195
  # @return [Hash]
182
196
  def server_info
183
- raise_not_implemented
197
+ db("admin").command('buildinfo')
184
198
  end
185
199
 
186
200
  # Get the build version of the current server.
@@ -188,7 +202,7 @@ module Mongo
188
202
  # @return [Mongo::ServerVersion]
189
203
  # object allowing easy comparability of version.
190
204
  def server_version
191
- _server_version
205
+ ServerVersion.new(server_info["version"])
192
206
  end
193
207
 
194
208
  # Is it okay to connect to a slave?
@@ -230,15 +244,27 @@ module Mongo
230
244
  end
231
245
 
232
246
  def connect_to_master
233
- raise_not_implemented
247
+ connect
234
248
  end
235
249
 
236
250
  def connected?
237
- raise_not_implemented
251
+ @connection && @connector && @connector.is_open
252
+ end
253
+
254
+ def connect
255
+ close
256
+ if @mongo_uri
257
+ @connection = JMongo::Mongo.new(@mongo_uri)
258
+ else
259
+ @connection = JMongo::Mongo.new(@server_address, @options)
260
+ end
261
+ @connector = @connection.connector
238
262
  end
263
+ alias :reconnect :connect
239
264
 
240
265
  def close
241
- raise_not_implemented
266
+ @connection.close if @connection
267
+ @connection = @connector = nil
242
268
  end
243
269
 
244
270
  end # class Connection
@@ -17,90 +17,173 @@ module Mongo
17
17
  class Cursor
18
18
  include Mongo::JavaImpl::Utils
19
19
 
20
- attr_reader :j_cursor
20
+ attr_reader :j_cursor, :collection, :selector, :fields,
21
+ :order, :hint, :snapshot, :timeout,
22
+ :full_collection_name, :transformer,
23
+ :options
21
24
 
22
25
  def initialize(collection, options={})
26
+ @collection = collection
23
27
  @j_collection = collection.j_collection
24
-
28
+ @query_run = false
25
29
  @selector = convert_selector_for_query(options[:selector])
26
30
  @fields = convert_fields_for_query(options[:fields])
27
- @admin = options[:admin] || false
28
- @skip = options[:skip] || 0
29
- @limit = options[:limit] || 0
30
- @order = _sort(options[:order])
31
+ @admin = options.fetch(:admin, false)
32
+ @order = nil
33
+ @batch_size = Mongo::DEFAULT_BATCH_SIZE
34
+ @skip = 0
35
+ @limit = 0
36
+ _skip options[:skip]
37
+ _limit options[:limit]
38
+ _sort options[:order]
39
+ _batch_size options[:batch_size]
31
40
  @hint = options[:hint]
32
41
  @snapshot = options[:snapshot]
33
42
  @explain = options[:explain]
34
43
  @socket = options[:socket]
35
- @batch_size = options[:batch_size] || Mongo::Constants::DEFAULT_BATCH_SIZE
36
- @timeout = options[:timeout] || false
37
- @tailable = options[:tailable] || false
44
+ @timeout = options.fetch(:timeout, true)
45
+ @tailable = options.fetch(:tailable, false)
46
+ @transformer = options[:transformer]
38
47
 
39
- #@full_collection_name = "#{@collection.db.name}.#{@collection.name}"
40
- @query_run = false
48
+ @full_collection_name = "#{@collection.db.name}.#{@collection.name}"
49
+
50
+ spawn_cursor
51
+ end
41
52
 
53
+ def rewind!
54
+ close
55
+ @query_run = false
42
56
  spawn_cursor
43
57
  end
58
+
59
+ def close
60
+ @query_run = true
61
+ @j_cursor.close
62
+ end
63
+
64
+ def cursor_id
65
+ @j_cursor.get_cursor_id
66
+ end
67
+
68
+ def closed?
69
+ cursor_id == 0
70
+ end
71
+
72
+ def alive?
73
+ cursor_id != 0
74
+ end
75
+
76
+ def add_option(opt)
77
+ check_modifiable
78
+ @j_cursor.addOption(opt)
79
+ options
80
+ end
81
+
82
+ def options
83
+ @j_cursor.getOptions
84
+ end
85
+
86
+ def query_opts
87
+ warn "The method Cursor#query_opts has been deprecated " +
88
+ "and will removed in v2.0. Use Cursor#options instead."
89
+ options
90
+ end
91
+
92
+ def remove_option(opt)
93
+ check_modifiable
94
+ @j_cursor.setOptions(options & ~opt)
95
+ options
96
+ end
97
+
44
98
  def current_document
45
- if !@query_run
46
- next_document
47
- else
48
- from_dbobject(@j_cursor.curr)
49
- end
99
+ _xform(from_dbobject(@j_cursor.curr))
50
100
  end
51
101
 
52
102
  def next_document
53
- @query_run = true
54
- @j_cursor.has_next? ? from_dbobject(@j_cursor.next) : BSON::OrderedHash.new
103
+ _xform(has_next? ? __next : nil)
55
104
  end
105
+ alias :next :next_document
106
+
107
+ def _xform(doc)
108
+ if @transformer && @transformer.respond_to?('call')
109
+ @transformer.call(doc)
110
+ else
111
+ doc
112
+ end
113
+ end
114
+ private :_xform
56
115
 
57
116
  def has_next?
58
117
  @j_cursor.has_next?
59
118
  end
119
+
60
120
  # iterate directly from the mongo db
61
121
  def each
62
122
  check_modifiable
63
- #raise "Backtrace..."
64
- while @j_cursor.has_next?
123
+ while has_next?
65
124
  yield next_document
66
125
  end
67
126
  end
68
127
 
69
- def limit(number_to_return=nil)
70
- return @limit unless number_to_return
128
+ def _batch_size(size=nil)
129
+ return if size.nil?
71
130
  check_modifiable
72
- raise ArgumentError, "limit requires an integer" unless number_to_return.is_a? Integer
131
+ raise ArgumentError, "batch_size requires an integer" unless size.is_a? Integer
132
+ @batch_size = size
133
+ end
134
+ private :_batch_size
73
135
 
74
- @limit = number_to_return
75
- @j_cursor = @j_cursor.limit(@limit)
136
+ def batch_size(size=nil)
137
+ _batch_size(size)
138
+ @j_cursor = @j_cursor.batchSize(@batch_size) if @batch_size
76
139
  self
77
140
  end
78
141
 
79
- def skip(number_to_skip=nil)
80
- return @skip unless number_to_skip
142
+ def _limit(number_to_return=nil)
143
+ return if number_to_return.nil?
81
144
  check_modifiable
82
- raise ArgumentError, "skip requires an integer" unless number_to_skip.is_a? Integer
145
+ raise ArgumentError, "limit requires an integer" unless number_to_return.is_a? Integer
146
+ @limit = number_to_return
147
+ end
148
+ private :_limit
83
149
 
84
- @skip = number_to_skip
85
- @j_cursor = @j_cursor.skip(@skip)
150
+ def limit(number_to_return=nil)
151
+ _limit(number_to_return)
152
+ wrap_invalid_op do
153
+ @j_cursor = @j_cursor.limit(@limit) if @limit
154
+ end
86
155
  self
87
156
  end
88
157
 
89
- def sort(key_or_list, direction=nil)
158
+ def _skip(number_to_skip=nil)
159
+ return if number_to_skip.nil?
90
160
  check_modifiable
91
- @ord = _sort(key_or_list, direction)
92
- @j_cursor = @j_cursor.sort(@ord)
161
+ raise ArgumentError, "skip requires an integer" unless number_to_skip.is_a? Integer
162
+ @skip = number_to_skip
163
+ end
164
+ private :_skip
165
+
166
+ def skip(number_to_skip=nil)
167
+ _skip(number_to_skip)
168
+ wrap_invalid_op do
169
+ @j_cursor = @j_cursor.skip(@skip) if @skip
170
+ end
93
171
  self
94
172
  end
95
173
 
96
- def _sort(key_or_list, direction=nil)
174
+ def _sort(key_or_list=nil, direction=nil)
97
175
  return if key_or_list.nil?
98
- if !direction.nil?
99
- order = [[key_or_list, direction]]
100
- else
101
- order = [key_or_list]
176
+ check_modifiable
177
+ @order = prep_sort(key_or_list, direction)
178
+ end
179
+ private :_sort
180
+
181
+ def sort(key_or_list, direction=nil)
182
+ _sort(key_or_list, direction)
183
+ wrap_invalid_op do
184
+ @j_cursor = @j_cursor.sort(@order) if @order
102
185
  end
103
- to_dbobject(Hash[*order.flatten])
186
+ self
104
187
  end
105
188
 
106
189
  def size
@@ -110,9 +193,9 @@ module Mongo
110
193
  def count(skip_and_limit = false)
111
194
  if skip_and_limit && @skip && @limit
112
195
  check_modifiable
113
- @j_cursor.skip(@skip).limit(@limit).size
114
- else
115
196
  @j_cursor.size
197
+ else
198
+ @j_cursor.count
116
199
  end
117
200
  end
118
201
 
@@ -122,38 +205,38 @@ module Mongo
122
205
 
123
206
  def map(&block)
124
207
  ret = []
125
- check_modifiable
126
- while @j_cursor.has_next?
127
- ret << block.call(from_dbobject(@j_cursor.next))
208
+ rewind! unless has_next?
209
+ while has_next?
210
+ ret << block.call(__next)
128
211
  end
129
212
  ret
130
213
  end
131
214
 
132
215
  def to_a
133
216
  ret = []
134
- check_modifiable
135
- while @j_cursor.has_next?
136
- ret << from_dbobject(@j_cursor.next)
217
+ rewind! unless has_next?
218
+ while has_next?
219
+ ret << __next
137
220
  end
138
221
  ret
139
222
  end
223
+
224
+ def to_set
225
+ Set.new self.to_a
226
+ end
227
+
140
228
  private
141
229
 
230
+ def __next
231
+ @query_run = true
232
+ from_dbobject(@j_cursor.next)
233
+ end
234
+
142
235
  # Convert the +:fields+ parameter from a single field name or an array
143
236
  # of fields names to a hash, with the field names for keys and '1' for each
144
237
  # value.
145
238
  def convert_fields_for_query(fields)
146
- case fields
147
- when String, Symbol
148
- to_dbobject({fields => 1})
149
- when Array
150
- return nil if fields.length.zero?
151
- hash = {}
152
- fields.each { |field| hash[field] = 1 }
153
- to_dbobject hash
154
- when Hash
155
- to_dbobject fields
156
- end
239
+ to_dbobject prep_fields(fields)
157
240
  end
158
241
 
159
242
  # Set the query selector hash.
@@ -166,14 +249,18 @@ module Mongo
166
249
  end
167
250
  end
168
251
 
252
+ def no_fields?
253
+ @fields.nil? || @fields.empty?
254
+ end
255
+
169
256
  def spawn_cursor
170
- @j_cursor = @fields.nil? || @fields.empty? ? @j_collection.find(@selector) : @j_collection.find(@selector, @fields)
257
+ @j_cursor = no_fields? ? @j_collection.find(@selector) : @j_collection.find(@selector, @fields)
171
258
 
172
259
  if @j_cursor
173
260
  @j_cursor = @j_cursor.sort(@order) if @order
174
- @j_cursor = @j_cursor.skip(@skip) if @skip > 0
175
- @j_cursor = @j_cursor.limit(@limit) if @limit > 0
176
- @j_cursor = @j_cursor.batchSize(@batch_size)
261
+ @j_cursor = @j_cursor.skip(@skip) if @skip && @skip > 0
262
+ @j_cursor = @j_cursor.limit(@limit) if @limit && @limit > 0
263
+ @j_cursor = @j_cursor.batchSize(@batch_size) if @batch_size && @batch_size > 0
177
264
 
178
265
  @j_cursor = @j_cursor.addOption JMongo::Bytes::QUERYOPTION_NOTIMEOUT unless @timeout
179
266
  @j_cursor = @j_cursor.addOption JMongo::Bytes::QUERYOPTION_TAILABLE if @tailable
@@ -184,10 +271,21 @@ module Mongo
184
271
 
185
272
  def check_modifiable
186
273
  if @query_run
187
- raise "Cannot modify the query once it has been run or closed."
274
+ raise_invalid_op
188
275
  end
189
276
  end
190
277
 
278
+ def wrap_invalid_op
279
+ begin
280
+ yield
281
+ rescue => ex
282
+ raise_invalid_op
283
+ end
284
+ end
285
+
286
+ def raise_invalid_op
287
+ raise InvalidOperation, "Cannot modify the query once it has been run or closed."
288
+ end
191
289
  end # class Cursor
192
290
 
193
291
  end # module Mongo
@@ -22,10 +22,16 @@ module Mongo
22
22
  attr_reader :name
23
23
  attr_reader :connection
24
24
 
25
+ attr_writer :strict
26
+
27
+ ProfileLevel = {:off => 0, :slow_only => 1, :all => 2, 0 => 'off', 1 => 'slow_only', 2 => 'all'}
28
+
25
29
  def initialize(db_name, connection, options={})
26
30
  @name = db_name
27
31
  @connection = connection
28
32
  @j_db = @connection.connection.get_db db_name
33
+ @pk_factory = options[:pk]
34
+ @strict = options.fetch(:strict, false)
29
35
  end
30
36
 
31
37
  def authenticate(username, password, save_auth=true)
@@ -53,9 +59,7 @@ module Mongo
53
59
  end
54
60
 
55
61
  def collection_names
56
- names = collections_info.to_a.collect { |doc| doc['name'] || '' }
57
- names = names.delete_if {|name| name.index(@name).nil? || name.index('$')}
58
- names.map {|name| name.sub(@name + '.', '')}
62
+ @j_db.get_collection_names
59
63
  end
60
64
 
61
65
  def collections
@@ -67,15 +71,27 @@ module Mongo
67
71
  def collections_info(coll_name=nil)
68
72
  selector = {}
69
73
  selector[:name] = full_collection_name(coll_name) if coll_name
70
- Cursor.new(Collection.new(self, SYSTEM_NAMESPACE_COLLECTION), :selector => selector)
74
+ coll = self.collection(SYSTEM_NAMESPACE_COLLECTION)
75
+ coll.find selector
71
76
  end
72
77
 
73
78
  def create_collection(name, options={})
74
- @j_db.createCollection(name, to_dbobject(options))
79
+ if collection_names.include?(name)
80
+ raise MongoDBError, "Collection #{name} already exists. Currently in strict mode." if @strict
81
+ collection(name, options)
82
+ else
83
+ begin
84
+ jc = @j_db.create_collection(name, to_dbobject(options))
85
+ Collection.new self, name, options, jc
86
+ rescue NativeException => ex
87
+ raise MongoDBError, "Collection #{name} creation error: " +
88
+ ex.message
89
+ end
90
+ end
75
91
  end
76
92
 
77
- def collection(name)
78
- Collection.new self, name
93
+ def collection(name, options = {})
94
+ Collection.new self, name, options
79
95
  end
80
96
  alias_method :[], :collection
81
97
 
@@ -83,24 +99,21 @@ module Mongo
83
99
  coll = collection(name).j_collection.drop
84
100
  end
85
101
 
86
- def error
87
- raise_not_implemented
88
- end
89
-
90
- def last_status
102
+ def get_last_error
91
103
  from_dbobject(@j_db.getLastError)
92
104
  end
105
+ alias :last_status :get_last_error
93
106
 
94
107
  def error?
95
- raise_not_implemented
108
+ !get_last_error['err'].nil?
96
109
  end
97
110
 
98
111
  def previous_error
99
- raise_not_implemented
112
+ exec_command :getpreverror
100
113
  end
101
114
 
102
115
  def reset_error_history
103
- raise_not_implemented
116
+ exec_command :reseterror
104
117
  end
105
118
 
106
119
  def query(collection, query, admin=false)
@@ -108,68 +121,144 @@ module Mongo
108
121
  end
109
122
 
110
123
  def dereference(dbref)
111
- raise_not_implemented
124
+ ns = dbref.namespace
125
+ raise MongoArgumentError, "No namespace for dbref: #{dbref.inspect}"
126
+ collection(ns).find_one("_id" => dbref.object_id)
112
127
  end
113
128
 
114
129
  def eval(code, *args)
115
- raise_not_implemented
130
+ doc = do_eval(code, *args)
131
+ return unless doc
132
+ return doc['retval']['value'] if doc['retval'] && doc['retval']['value']
133
+ doc['retval']
116
134
  end
117
135
 
118
136
  def rename_collection(from, to)
119
- raise_not_implemented
137
+ begin
138
+ @j_db.get_collection(from).rename(to)
139
+ rescue => ex
140
+ raise(MongoDBError, "Error renaming collection from: #{from}, to: #{to}")
141
+ end
142
+ true
120
143
  end
121
144
 
122
145
  def drop_index(collection_name, index_name)
123
- raise_not_implemented
146
+ self[collection_name].drop_index(index_name)
124
147
  end
125
148
 
126
149
  def index_information(collection_name)
127
- raise_not_implemented
150
+ info = {}
151
+ from_dbobject(@j_db.get_collection(collection_name).get_index_info).each do |index|
152
+ info[index['name']] = index
153
+ end
154
+ info
128
155
  end
129
156
 
130
157
  def stats
131
- exec_command(:dbstats)
158
+ from_dbobject exec_command(:dbstats)
132
159
  end
133
160
 
134
161
  def create_index(collection_name, field_or_spec, unique=false)
135
162
  collection(collection_name).create_indexes(field_or_spec,{:unique=>unique})
136
163
  end
164
+ # Return +true+ if an error was caused by the most recently executed
165
+ # database operation.
166
+ #
167
+ # @return [Boolean]
168
+ def error?
169
+ get_last_error['err'] != nil
170
+ end
137
171
 
138
172
  def ok?(doc)
139
- doc['ok'] == 1.0
173
+ doc['ok'] == 1.0 || doc['ok'] == true
140
174
  end
141
175
 
142
- def command(selector, opts={})
143
- raise_not_implemented
176
+ def command(cmd, opts={})
177
+ selector = cmd.respond_to?('merge') ? cmd : {cmd.to_s => 1}
178
+ check_response = opts.fetch(:check_response, true)
179
+ raise MongoArgumentError, "command must be given a selector" if selector.empty?
180
+ if selector.keys.length > 1 && RUBY_VERSION < '1.9' && selector.class != BSON::OrderedHash
181
+ raise MongoArgumentError, "DB#command requires an OrderedHash when hash contains multiple keys"
182
+ end
183
+
184
+ begin
185
+ result = exec_command(selector)
186
+ rescue => ex
187
+ raise OperationFailure, "Database command '#{selector.keys.first}' failed: #{ex.message}"
188
+ end
189
+
190
+ raise OperationFailure, "Database command '#{selector.keys.first}' failed: returned null." if result.nil?
191
+
192
+ if (check_response && !ok?(result))
193
+ message = "Database command '#{selector.keys.first}' failed: (" + result.map{|k, v| "#{k}: '#{v}'"}.join('; ') + ")."
194
+ raise OperationFailure.new message
195
+ else
196
+ result
197
+ end
144
198
  end
145
199
 
146
200
  def full_collection_name(collection_name)
147
201
  "#{@name}.#{collection_name}"
148
202
  end
149
203
 
204
+ # Returns the value of the +strict+ flag.
205
+ def strict?; @strict; end
206
+
207
+ # The primary key factory object (or +nil+).
208
+ #
209
+ # @return [Object, Nil]
150
210
  def pk_factory
151
- raise_not_implemented
211
+ @pk_factory
152
212
  end
153
213
 
214
+ # Specify a primary key factory if not already set.
215
+ #
216
+ # @raise [MongoArgumentError] if the primary key factory has already been set.
154
217
  def pk_factory=(pk_factory)
155
- raise_not_implemented
218
+ if @pk_factory
219
+ raise MongoArgumentError, "Cannot change primary key factory once it's been set"
220
+ end
221
+
222
+ @pk_factory = pk_factory
156
223
  end
157
224
 
158
225
  def profiling_level
159
- raise_not_implemented
226
+ oh = BSON::OrderedHash.new
227
+ oh['profile'] = -1
228
+ doc = command(oh, :check_response => false)
229
+ raise "Error with profile command: #{doc.inspect}" unless ok?(doc) && doc['was'].kind_of?(Numeric)
230
+ was = ProfileLevel[doc['was'].to_i]
231
+ raise "Error: illegal profiling level value #{doc['was']}" if was.nil?
232
+ was.to_sym
160
233
  end
161
234
 
162
235
  def profiling_level=(level)
163
- raise_not_implemented
236
+ oh = BSON::OrderedHash.new
237
+ int_lvl = ProfileLevel[level]
238
+ raise "Error: illegal profiling level value #{level}" if int_lvl.nil?
239
+ oh['profile'] = int_lvl
240
+ doc = command(oh, :check_response => false)
241
+ ok?(doc) || raise(MongoDBError, "Error with profile command: #{doc.inspect}")
164
242
  end
165
243
 
166
244
  def profiling_info
167
- raise_not_implemented
245
+ Cursor.new(Collection.new(SYSTEM_PROFILE_COLLECTION, self), :selector => {}).to_a
168
246
  end
169
247
 
170
248
  def validate_collection(name)
171
- raise_not_implemented
249
+ cmd = BSON::OrderedHash.new
250
+ cmd['validate'] = name
251
+ cmd['full'] = true
252
+ doc = command(cmd, :check_response => false)
253
+ if !ok?(doc)
254
+ raise MongoDBError, "Error with validate command: #{doc.inspect}"
255
+ end
256
+ if (doc.has_key?('valid') && !doc['valid']) || (doc['result'] =~ /\b(exception|corrupt)\b/i)
257
+ raise MongoDBError, "Error: invalid collection #{name}: #{doc.inspect}"
258
+ end
259
+ doc
172
260
  end
261
+
173
262
  # additions to the ruby driver
174
263
  def has_collection?(name)
175
264
  has_coll name