jmongo 1.0.3 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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