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
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source "http://rubygems.org"
2
+ gemspec
3
+
4
+ gem 'rake'
5
+ gem 'shoulda'
6
+ gem 'mocha'
7
+
8
+
@@ -0,0 +1,43 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ jmongo (1.0.3)
5
+ require_all (~> 1.2)
6
+
7
+ GEM
8
+ remote: http://rubygems.org/
9
+ specs:
10
+ awesome_print (0.4.0)
11
+ diff-lcs (1.1.3)
12
+ fuubar (0.0.6)
13
+ rspec (~> 2.0)
14
+ rspec-instafail (~> 0.1.8)
15
+ ruby-progressbar (~> 0.0.10)
16
+ metaclass (0.0.1)
17
+ mocha (0.10.0)
18
+ metaclass (~> 0.0.1)
19
+ rake (0.9.2)
20
+ require_all (1.2.0)
21
+ rspec (2.6.0)
22
+ rspec-core (~> 2.6.0)
23
+ rspec-expectations (~> 2.6.0)
24
+ rspec-mocks (~> 2.6.0)
25
+ rspec-core (2.6.4)
26
+ rspec-expectations (2.6.0)
27
+ diff-lcs (~> 1.1.2)
28
+ rspec-instafail (0.1.8)
29
+ rspec-mocks (2.6.0)
30
+ ruby-progressbar (0.0.10)
31
+ shoulda (2.11.3)
32
+
33
+ PLATFORMS
34
+ java
35
+
36
+ DEPENDENCIES
37
+ awesome_print (~> 0.4)
38
+ fuubar (~> 0.0)
39
+ jmongo!
40
+ mocha
41
+ rake
42
+ rspec (~> 2.6)
43
+ shoulda
data/Rakefile CHANGED
@@ -116,3 +116,75 @@ task :validate do
116
116
  exit!
117
117
  end
118
118
  end
119
+
120
+ require 'rake/testtask'
121
+
122
+ task :test do
123
+ puts "\nTo test the pure jruby driver: \nrake test:jruby\n\n"
124
+ end
125
+
126
+ namespace :test do
127
+
128
+ desc "Test the driver using pure jruby (no C extension)"
129
+ task :jruby do
130
+ ENV['C_EXT'] = nil
131
+ if ENV['TEST']
132
+ Rake::Task['test:functional'].invoke
133
+ else
134
+ Rake::Task['test:unit'].invoke
135
+ Rake::Task['test:functional'].invoke
136
+ Rake::Task['test:bson'].invoke
137
+ Rake::Task['test:pooled_threading'].invoke
138
+ Rake::Task['test:drop_databases'].invoke
139
+ end
140
+ end
141
+
142
+ desc "Run the replica set test suite"
143
+ Rake::TestTask.new(:rs) do |t|
144
+ t.test_files = FileList['test/replica_sets/*_test.rb']
145
+ t.verbose = true
146
+ t.ruby_opts << '-w'
147
+ end
148
+
149
+ Rake::TestTask.new(:unit) do |t|
150
+ t.test_files = FileList['test/unit/*_test.rb']
151
+ t.verbose = true
152
+ t.ruby_opts << '-w'
153
+ end
154
+
155
+ Rake::TestTask.new(:functional) do |t|
156
+ t.test_files = FileList['test/*_test.rb']
157
+ t.verbose = true
158
+ t.ruby_opts << '-w'
159
+ end
160
+
161
+ Rake::TestTask.new(:pooled_threading) do |t|
162
+ t.test_files = FileList['test/threading/*_test.rb']
163
+ t.verbose = true
164
+ t.ruby_opts << '-w'
165
+ end
166
+
167
+ Rake::TestTask.new(:auto_reconnect) do |t|
168
+ t.test_files = FileList['test/auxillary/autoreconnect_test.rb']
169
+ t.verbose = true
170
+ t.ruby_opts << '-w'
171
+ end
172
+
173
+ Rake::TestTask.new(:authentication) do |t|
174
+ t.test_files = FileList['test/auxillary/authentication_test.rb']
175
+ t.verbose = true
176
+ t.ruby_opts << '-w'
177
+ end
178
+
179
+ Rake::TestTask.new(:new_features) do |t|
180
+ t.test_files = FileList['test/auxillary/1.4_features.rb']
181
+ t.verbose = true
182
+ t.ruby_opts << '-w'
183
+ end
184
+
185
+ Rake::TestTask.new(:bson) do |t|
186
+ t.test_files = FileList['test/bson/*_test.rb']
187
+ t.verbose = true
188
+ t.ruby_opts << '-w'
189
+ end
190
+ end
@@ -1,7 +1,7 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'jmongo'
3
- s.version = '1.0.3'
4
- s.date = '2011-09-16'
3
+ s.version = '1.1.0'
4
+ s.date = '2011-10-03'
5
5
  s.platform = Gem::Platform::RUBY
6
6
  s.authors = ["Chuck Remes","Guy Boertje", "Lee Henson"]
7
7
  s.email = ["cremes@mac.com", "guyboertje@gmail.com", "lee.m.henson@gmail.com"]
@@ -11,6 +11,8 @@ Gem::Specification.new do |s|
11
11
 
12
12
  # = MANIFEST =
13
13
  s.files = %w[
14
+ Gemfile
15
+ Gemfile.lock
14
16
  History.txt
15
17
  LICENSE.txt
16
18
  README.txt
@@ -18,18 +20,94 @@ Gem::Specification.new do |s|
18
20
  bin/jmongo
19
21
  jmongo.gemspec
20
22
  lib/jmongo.rb
21
- lib/jmongo/ajrb.rb
22
23
  lib/jmongo/collection.rb
23
24
  lib/jmongo/connection.rb
24
25
  lib/jmongo/cursor.rb
25
26
  lib/jmongo/db.rb
26
27
  lib/jmongo/exceptions.rb
27
- lib/jmongo/jmongo_jext.rb
28
- lib/jmongo/mongo-2.6.3.jar
29
- lib/jmongo/utils.rb
28
+ lib/jmongo/mongo-2.6.5.gb1.jar
29
+ lib/jmongo/mongo/bson.rb
30
+ lib/jmongo/mongo/collection.rb
31
+ lib/jmongo/mongo/connection.rb
32
+ lib/jmongo/mongo/db.rb
33
+ lib/jmongo/mongo/jmongo.rb
34
+ lib/jmongo/mongo/mongo.rb
35
+ lib/jmongo/mongo/ruby_ext.rb
36
+ lib/jmongo/mongo/utils.rb
30
37
  lib/jmongo/version.rb
31
38
  spec/jmongo_spec.rb
32
39
  spec/spec_helper.rb
40
+ test-results.txt
41
+ test/auxillary/1.4_features.rb
42
+ test/auxillary/authentication_test.rb
43
+ test/auxillary/autoreconnect_test.rb
44
+ test/auxillary/fork_test.rb
45
+ test/auxillary/repl_set_auth_test.rb
46
+ test/auxillary/slave_connection_test.rb
47
+ test/auxillary/threaded_authentication_test.rb
48
+ test/bson/binary_test.rb
49
+ test/bson/bson_test.rb
50
+ test/bson/byte_buffer_test.rb
51
+ test/bson/hash_with_indifferent_access_test.rb
52
+ test/bson/json_test.rb
53
+ test/bson/object_id_test.rb
54
+ test/bson/ordered_hash_test.rb
55
+ test/bson/test_helper.rb
56
+ test/bson/timestamp_test.rb
57
+ test/collection_test.rb
58
+ test/connection_test.rb
59
+ test/conversions_test.rb
60
+ test/cursor_fail_test.rb
61
+ test/cursor_message_test.rb
62
+ test/cursor_test.rb
63
+ test/data/empty_data
64
+ test/data/sample_data
65
+ test/data/sample_file.pdf
66
+ test/data/small_data.txt
67
+ test/db_api_test.rb
68
+ test/db_connection_test.rb
69
+ test/db_test.rb
70
+ test/grid_file_system_test.rb
71
+ test/grid_io_test.rb
72
+ test/grid_test.rb
73
+ test/load/thin/config.ru
74
+ test/load/thin/config.yml.template
75
+ test/load/thin/load.rb
76
+ test/load/unicorn/config.ru
77
+ test/load/unicorn/load.rb
78
+ test/load/unicorn/unicorn.rb.template
79
+ test/replica_sets/connect_test.rb
80
+ test/replica_sets/connection_string_test.rb
81
+ test/replica_sets/count_test.rb
82
+ test/replica_sets/insert_test.rb
83
+ test/replica_sets/pooled_insert_test.rb
84
+ test/replica_sets/query_secondaries.rb
85
+ test/replica_sets/query_test.rb
86
+ test/replica_sets/read_preference_test.rb
87
+ test/replica_sets/refresh_test.rb
88
+ test/replica_sets/replication_ack_test.rb
89
+ test/replica_sets/rs_test_helper.rb
90
+ test/safe_test.rb
91
+ test/support/hash_with_indifferent_access.rb
92
+ test/support/keys.rb
93
+ test/support_test.rb
94
+ test/test_helper.rb
95
+ test/threading/threading_with_large_pool_test.rb
96
+ test/threading_test.rb
97
+ test/tools/auth_repl_set_manager.rb
98
+ test/tools/keyfile.txt
99
+ test/tools/repl_set_manager.rb
100
+ test/unit/collection_test.rb
101
+ test/unit/connection_test.rb
102
+ test/unit/cursor_test.rb
103
+ test/unit/db_test.rb
104
+ test/unit/grid_test.rb
105
+ test/unit/node_test.rb
106
+ test/unit/pool_manager_test.rb
107
+ test/unit/pool_test.rb
108
+ test/unit/read_test.rb
109
+ test/unit/safe_test.rb
110
+ test/uri_test.rb
33
111
  ]
34
112
  # = MANIFEST =
35
113
 
@@ -1,7 +1,7 @@
1
- # Copyright (C) 2010 Chuck Remes
1
+ # Copyright (C) 2010 Chuck Remes, Guy Boertje
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
- # you may not use this file except in compliance with the License.
4
+ # you may not use this file except in coSmpliance with the License.
5
5
  # You may obtain a copy of the License at
6
6
  #
7
7
  # http://www.apache.org/licenses/LICENSE-2.0
@@ -17,19 +17,11 @@ unless RUBY_PLATFORM =~ /java/
17
17
  exit 255
18
18
  end
19
19
 
20
+ require 'timeout'
21
+ require 'java'
22
+
20
23
  require 'require_all'
21
24
  require_rel 'jmongo/*.jar'
22
25
 
23
- # import all of the java packages we'll need into the JMongo namespace
24
- require 'jmongo/jmongo_jext'
25
- require_rel 'jmongo/*.rb'
26
-
27
- module Mongo
28
- ASCENDING = 1
29
- DESCENDING = -1
30
- GEO2D = '2d'
26
+ require_rel 'jmongo/**/*.rb'
31
27
 
32
- module Constants
33
- DEFAULT_BATCH_SIZE = 100
34
- end
35
- end
@@ -35,30 +35,31 @@ module Mongo
35
35
  # @return [Collection]
36
36
  #
37
37
  # @core collections constructor_details
38
- def initialize(db, name, pk_factory=nil)
39
- case name
40
- when Symbol, String
41
- else
42
- raise TypeError, "new_name must be a string or symbol"
38
+ #db, name, options=nil, j_collection=nil
39
+ def initialize(*args)
40
+ j_collection = nil
41
+ @opts = {}
42
+ if args.size == 4
43
+ j_collection = args.pop
43
44
  end
44
-
45
- name = name.to_s
46
-
47
- if name.empty? or name.include? ".."
48
- raise Mongo::InvalidNSName, "collection names cannot be empty"
45
+ if args.size == 3
46
+ @opts = args.pop
49
47
  end
50
- if name.include? "$"
51
- raise Mongo::InvalidNSName, "collection names must not contain '$'" unless name =~ /((^\$cmd)|(oplog\.\$main))/
48
+ if args.size < 2
49
+ raise ArgumentError.new("Must supply at least name and db parameters")
52
50
  end
53
- if name.match(/^\./) or name.match(/\.$/)
54
- raise Mongo::InvalidNSName, "collection names must not start or end with '.'"
51
+ if args.first.respond_to?('collection_names')
52
+ db, name = args
53
+ else
54
+ name, db = args
55
55
  end
56
56
 
57
- @db, @j_db, @name = db, db.j_db, name
57
+ @name = validate_name(name)
58
+ @db, @j_db = db, db.j_db
58
59
  @connection = @db.connection
59
- @pk_factory = pk_factory || BSON::ObjectId
60
+ @pk_factory = @opts[:pk] || BSON::ObjectId
60
61
  @hint = nil
61
- @j_collection = @j_db.getCollection @name
62
+ @j_collection = j_collection || @j_db.get_collection(@name)
62
63
  end
63
64
 
64
65
  # Return a sub-collection of this collection by name. If 'users' is a collection, then
@@ -73,8 +74,14 @@ module Mongo
73
74
  # @return [Collection]
74
75
  # the specified sub-collection
75
76
  def [](name)
77
+ new_name = "#{self.name}.#{name}"
78
+ validate_name new_name
79
+ @db.create_collection(new_name, @opts)
76
80
  end
77
81
 
82
+ def capped?
83
+ @j_collection.isCapped
84
+ end
78
85
  # Set a hint field for query optimizer. Hint may be a single field
79
86
  # name, array of field names, or a hash (preferably an [OrderedHash]).
80
87
  # If using MongoDB > 1.1, you probably don't ever need to set a hint.
@@ -131,29 +138,34 @@ module Mongo
131
138
  #
132
139
  # @core find find-instance_method
133
140
  def find(selector={}, opts={})
134
- fields = opts.delete(:fields)
135
- fields = ["_id"] if fields && fields.empty?
141
+ fields = prep_fields(opts.delete(:fields))
136
142
  skip = opts.delete(:skip) || skip || 0
137
143
  limit = opts.delete(:limit) || 0
138
144
  sort = opts.delete(:sort)
139
145
  hint = opts.delete(:hint)
140
146
  snapshot = opts.delete(:snapshot)
141
147
  batch_size = opts.delete(:batch_size)
142
- if opts[:timeout] == false && !block_given?
148
+ timeout = (opts.delete(:timeout) == false) ? false : true
149
+ transformer = opts.delete(:transformer)
150
+ if timeout == false && !block_given?
143
151
  raise ArgumentError, "Timeout can be set to false only when #find is invoked with a block."
144
152
  end
145
- timeout = block_given? ? (opts.delete(:timeout) || true) : true
153
+
146
154
  if hint
147
155
  hint = normalize_hint_fields(hint)
148
156
  else
149
157
  hint = @hint # assumed to be normalized already
150
158
  end
159
+
151
160
  raise RuntimeError, "Unknown options [#{opts.inspect}]" unless opts.empty?
152
161
 
153
162
  cursor = Cursor.new(self, :selector => selector, :fields => fields, :skip => skip, :limit => limit,
154
- :order => sort, :hint => hint, :snapshot => snapshot, :timeout => timeout, :batch_size => batch_size)
163
+ :order => sort, :hint => hint, :snapshot => snapshot,
164
+ :batch_size => batch_size, :timeout => timeout,
165
+ :transformer => transformer)
155
166
  if block_given?
156
167
  yield cursor
168
+ cursor.close
157
169
  nil
158
170
  else
159
171
  cursor
@@ -186,7 +198,11 @@ module Mongo
186
198
  else
187
199
  raise TypeError, "spec_or_object_id must be an instance of ObjectId or Hash, or nil"
188
200
  end
189
- find_one_document(spec, opts)
201
+ begin
202
+ find_one_document(spec, opts)
203
+ rescue => ex
204
+ raise OperationFailure, ex.message
205
+ end
190
206
  end
191
207
 
192
208
  # Save a document to this collection.
@@ -225,8 +241,9 @@ module Mongo
225
241
  doc_or_docs = [doc_or_docs] unless doc_or_docs.kind_of?(Array)
226
242
  doc_or_docs.collect! { |doc| @pk_factory.create_pk(doc) }
227
243
  safe = (options[:safe] || false)
228
- result = insert_documents(doc_or_docs,safe)
229
- result.size > 1 ? result : result.first
244
+ continue = (options[:continue_on_error] || false)
245
+ docs = insert_documents(doc_or_docs, safe, continue)
246
+ docs.size == 1 ? docs.first['_id'] : docs.collect{|doc| doc['_id']}
230
247
  end
231
248
  alias_method :<<, :insert
232
249
 
@@ -279,7 +296,7 @@ module Mongo
279
296
  def update(selector, document, options={})
280
297
  upsert, multi = !!(options[:upsert]), !!(options[:multi])
281
298
  safe = (options[:safe] || false)
282
- update_documents(selector, document,upsert,multi,safe)
299
+ update_documents(selector, document, upsert, multi, safe)
283
300
  end
284
301
 
285
302
  # Create a new index.
@@ -325,17 +342,21 @@ module Mongo
325
342
  #
326
343
  # @core indexes create_index-instance_method
327
344
  def create_index(spec, opts={})
328
- create_indexes(spec,opts)
345
+ _create_indexes(spec, opts)
329
346
  end
330
347
  alias_method :ensure_index, :create_index
331
348
 
332
349
  # Drop a specified index.
333
350
  #
334
- # @param [String] name
351
+ # @param [String, Array] spec
352
+ # should be either a single field name or an array of
353
+ # [field name, direction] pairs. Directions should be specified
354
+ # as Mongo::ASCENDING, Mongo::DESCENDING, or Mongo::GEO2D.
335
355
  #
336
356
  # @core indexes
337
- def drop_index(name)
338
- @j_collection.dropIndexes(name)
357
+ def drop_index(spec)
358
+ raise MongoArgumentError, "Cannot drop index for nil name" unless name
359
+ _drop_index(spec)
339
360
  end
340
361
 
341
362
  # Drop all indexes.
@@ -369,12 +390,14 @@ module Mongo
369
390
  def find_and_modify(opts={})
370
391
  query = opts[:query] || {}
371
392
  fields = opts[:fields] || {}
372
- sort = opts[:sort] || {}
393
+ sort = prep_sort(opts[:sort] || [])
373
394
  update = opts[:update] || {}
374
395
  remove = opts[:remove] || false
375
396
  new_ = opts[:new] || false
376
397
  upsert = opts[:upsert] || false
377
- find_and_modify_document(query, fields, sort, remove, update, new_, upsert)
398
+ trap_raise(OperationFailure) do
399
+ find_and_modify_document(query, fields, sort, remove, update, new_, upsert)
400
+ end
378
401
  end
379
402
 
380
403
  # Perform a map/reduce operation on the current collection.
@@ -399,72 +422,125 @@ module Mongo
399
422
  #
400
423
  # @core mapreduce map_reduce-instance_method
401
424
  def map_reduce(map, reduce, opts={})
402
- map = BSON::Code.new(map) unless map.is_a?(BSON::Code)
403
- reduce = BSON::Code.new(reduce) unless reduce.is_a?(BSON::Code)
404
-
405
- hash = OrderedHash.new
406
- hash['mapreduce'] = self.name
407
- hash['map'] = map
408
- hash['reduce'] = reduce
409
- hash.merge! opts
410
-
411
- result = @db.command(hash)
412
- unless result["ok"] == 1
413
- raise Mongo::OperationFailure, "map-reduce failed: #{result['errmsg']}"
425
+ query = opts.fetch(:query,{})
426
+ sort = opts.fetch(:sort,[])
427
+ limit = opts.fetch(:limit,0)
428
+ finalize = opts[:finalize]
429
+ out = opts[:out]
430
+ keeptemp = opts.fetch(:keeptemp,true)
431
+ verbose = opts.fetch(:verbose,true)
432
+ raw = opts.delete(:raw)
433
+
434
+ m = map.to_s
435
+ r = reduce.to_s
436
+
437
+ mrc = case out
438
+ when String
439
+ JMongo::MapReduceCommand.new(@j_collection, m, r, out, REPLACE, to_dbobject(query))
440
+ when Hash
441
+ if out.keys.size != 1
442
+ raise ArgumentError, "You need to specify one key value pair in the out hash"
443
+ end
444
+ out_type = out.keys.first
445
+ out_val = out[out_type]
446
+ unless MapReduceEnumHash.keys.include?(out_type)
447
+ raise ArgumentError, "Your out hash must have one of these keys: #{MapReduceEnumHash.keys}"
448
+ end
449
+ out_type_enum = MapReduceEnumHash[out_type]
450
+ out_dest = out_val.is_a?(String) ? out_val : nil
451
+ JMongo::MapReduceCommand.new(@j_collection, m, r, out_dest, out_type_enum, to_dbobject(query))
452
+ else
453
+ raise ArgumentError, "You need to specify an out parameter in the options hash"
454
+ end
455
+
456
+ mrc.verbose = verbose
457
+ mrc.sort = prep_sort(sort)
458
+ mrc.limit = limit
459
+ mrc.finalize = finalize
460
+ result = from_dbobject(@j_db.command(mrc.toDBObject))
461
+
462
+ if raw
463
+ result
464
+ elsif result["result"]
465
+ @db[result["result"]]
466
+ else
467
+ raise ArgumentError, "Could not instantiate collection from result. If you specified " +
468
+ "{:out => {:inline => true}}, then you must also specify :raw => true to get the results."
414
469
  end
415
- @db[result["result"]]
416
470
  end
417
471
  alias :mapreduce :map_reduce
418
472
 
419
473
  # Perform a group aggregation.
420
474
  #
421
- # @param [Array, String, BSON::Code, Nil] :key either 1) an array of fields to group by,
422
- # 2) a javascript function to generate the key object, or 3) nil.
423
- # @param [Hash] condition an optional document specifying a query to limit the documents over which group is run.
424
- # @param [Hash] initial initial value of the aggregation counter object
425
- # @param [String, BSON::Code] reduce aggregation function, in JavaScript
426
- # @param [String, BSON::Code] finalize :: optional. a JavaScript function that receives and modifies
427
- # each of the resultant grouped objects. Available only when group is run
428
- # with command set to true.
429
- #
430
- # @return [Array] the grouped items.
431
- def group(key, condition, initial, reduce, finalize=nil)
432
- reduce = BSON::Code.new(reduce) unless reduce.is_a?(BSON::Code)
475
+ # @param [Hash] opts the options for this group operation. The minimum required are :initial
476
+ # and :reduce.
477
+ #
478
+ # @option opts [Array, String, Symbol] :key (nil) Either the name of a field or a list of fields to group by (optional).
479
+ # @option opts [String, BSON::Code] :keyf (nil) A JavaScript function to be used to generate the grouping keys (optional).
480
+ # @option opts [String, BSON::Code] :cond ({}) A document specifying a query for filtering the documents over
481
+ # which the aggregation is run (optional).
482
+ # @option opts [Hash] :initial the initial value of the aggregation counter object (required).
483
+ # @option opts [String, BSON::Code] :reduce (nil) a JavaScript aggregation function (required).
484
+ # @option opts [String, BSON::Code] :finalize (nil) a JavaScript function that receives and modifies
485
+ # each of the resultant grouped objects. Available only when group is run with command
486
+ # set to true.
487
+ #
488
+ # @return [Array] the command response consisting of grouped items.
489
+ def group(opts, condition={}, initial={}, reduce=nil, finalize=nil)
490
+ key = keyf = false
491
+ if opts.is_a?(Hash)
492
+ reduce, finalize, initial= opts.values_at(:reduce, :finalize, :initial)
493
+ key, keyf = opts.values_at(:key, :keyf)
494
+ condition = opts.fetch(:cond, {})
495
+ unless key.nil? && keyf.nil?
496
+ unless key.is_a?(Array) || keyf.is_a?(String) || keyf.is_a?(BSON::Code)
497
+ raise MongoArgumentError, "Group takes either an array of fields to group by or a JavaScript function" +
498
+ "in the form of a String or BSON::Code."
499
+ end
500
+ end
501
+ else
502
+ warn "Collection#group no longer take a list of parameters. This usage is deprecated and will be remove in v2.0." +
503
+ "Check out the new API at http://api.mongodb.org/ruby/current/Mongo/Collection.html#group-instance_method"
504
+ case opts
505
+ when Array
506
+ key = opts
507
+ when String, BSON::Code
508
+ keyf = opts
509
+ else
510
+ raise MongoArgumentError, "Group takes either an array of fields to group by or a JavaScript function" +
511
+ "in the form of a String or BSON::Code."
512
+ end
513
+ end
514
+
515
+ if !(reduce && initial)
516
+ raise MongoArgumentError, "Group requires at minimum values for initial and reduce."
517
+ end
433
518
 
434
- group_command = {
519
+ cmd = {
435
520
  "group" => {
436
521
  "ns" => @name,
437
- "$reduce" => reduce,
522
+ "$reduce" => reduce.to_bson_code,
438
523
  "cond" => condition,
439
524
  "initial" => initial
440
525
  }
441
526
  }
442
527
 
443
- unless key.nil?
444
- if key.is_a? Array
445
- key_type = "key"
446
- key_value = {}
447
- key.each { |k| key_value[k] = 1 }
448
- else
449
- key_type = "$keyf"
450
- key_value = key.is_a?(BSON::Code) ? key : BSON::Code.new(key)
451
- end
452
-
453
- group_command["group"][key_type] = key_value
528
+ if keyf
529
+ cmd["group"]["$keyf"] = keyf.to_bson_code
530
+ elsif key
531
+ key_hash = Hash[key.zip( [1]*key.size )]
532
+ cmd["group"]["key"] = key_hash
454
533
  end
455
534
 
456
- finalize = BSON::Code.new(finalize) if finalize.is_a?(String)
457
- if finalize.is_a?(BSON::Code)
458
- group_command['group']['finalize'] = finalize
535
+ if finalize
536
+ cmd['group']['finalize'] = finalize.to_bson_code
459
537
  end
460
538
 
461
- result = @db.command group_command
539
+ result = from_dbobject(@db.command(cmd))
462
540
 
463
- if result["ok"] == 1
464
- result["retval"]
465
- else
466
- raise OperationFailure, "group command failed: #{result['errmsg']}"
467
- end
541
+ return result["retval"] if Mongo.result_ok?(result)
542
+
543
+ raise OperationFailure, "group command failed: #{result['errmsg']}"
468
544
  end
469
545
 
470
546
  # Return a list of distinct values for +key+ across all
@@ -494,12 +570,11 @@ module Mongo
494
570
  # @return [Array] an array of distinct values.
495
571
  def distinct(key, query=nil)
496
572
  raise MongoArgumentError unless [String, Symbol].include?(key.class)
497
- command = OrderedHash.new
498
- command[:distinct] = @name
499
- command[:key] = key.to_s
500
- command[:query] = query
501
-
502
- @db.command(command)["values"]
573
+ if query
574
+ from_dbobject @j_collection.distinct(key.to_s, to_dbobject(query))
575
+ else
576
+ from_dbobject @j_collection.distinct(key.to_s)
577
+ end
503
578
  end
504
579
 
505
580
  # Rename this collection.
@@ -511,25 +586,14 @@ module Mongo
511
586
  #
512
587
  # @raise [Mongo::InvalidNSName] if +new_name+ is an invalid collection name.
513
588
  def rename(new_name)
514
- case new_name
515
- when Symbol, String
516
- else
517
- raise TypeError, "new_name must be a string or symbol"
589
+ name = validate_name(new_name)
590
+ begin
591
+ jcol = @j_collection.rename(name)
592
+ @name = name
593
+ @j_collection = jcol
594
+ rescue => ex
595
+ raise MongoDBError, "Error renaming collection: #{name}, more: #{ex.message}"
518
596
  end
519
-
520
- new_name = new_name.to_s
521
-
522
- if new_name.empty? or new_name.include? ".."
523
- raise Mongo::InvalidNSName, "collection names cannot be empty"
524
- end
525
- if new_name.include? "$"
526
- raise Mongo::InvalidNSName, "collection names must not contain '$'"
527
- end
528
- if new_name.match(/^\./) or new_name.match(/\.$/)
529
- raise Mongo::InvalidNSName, "collection names must not start or end with '.'"
530
- end
531
-
532
- @db.rename_collection(@name, new_name)
533
597
  end
534
598
 
535
599
  # Get information on the indexes for this collection.
@@ -546,7 +610,9 @@ module Mongo
546
610
  #
547
611
  # @return [Hash] options that apply to this collection.
548
612
  def options
549
- @db.collections_info(@name).next_document['options']
613
+ info = @db.collections_info(@name).to_a
614
+ ap info
615
+ info.last['options']
550
616
  end
551
617
 
552
618
  # Return stats on the collection. Uses MongoDB's collstats command.
@@ -559,14 +625,36 @@ module Mongo
559
625
  # Get the number of documents in this collection.
560
626
  #
561
627
  # @return [Integer]
562
- def count
563
- @j_collection.count()
628
+ def count(opts={})
629
+ return @j_collection.count() if opts.empty?
630
+ query = opts[:query] || opts['query'] || {}
631
+ fields = opts[:fields] || opts['fields'] || {}
632
+ limit = opts[:limit] || opts['limit'] || 0
633
+ skip = opts[:skip] || opts['skip'] || 0
634
+ @j_collection.get_count(to_dbobject(query), to_dbobject(fields), limit, skip)
564
635
  end
565
636
 
566
637
  alias :size :count
567
638
 
568
639
  protected
569
640
 
641
+ def validate_name(new_name)
642
+ raise TypeError, "new_name must be a string like" unless new_name.respond_to?(:to_s)
643
+
644
+ name = new_name.to_s
645
+
646
+ if name.empty? || name.include?("..")
647
+ raise Mongo::InvalidNSName, "collection names cannot be empty"
648
+ end
649
+ if name.include? "$"
650
+ raise Mongo::InvalidNSName, "collection names must not contain '$'" unless name =~ /((^\$cmd)|(oplog\.\$main))/
651
+ end
652
+ if name.match(/^\./) || name.match(/\.$/)
653
+ raise Mongo::InvalidNSName, "collection names must not start or end with '.'"
654
+ end
655
+ name
656
+ end
657
+
570
658
  def normalize_hint_fields(hint)
571
659
  case hint
572
660
  when String
@@ -576,16 +664,10 @@ module Mongo
576
664
  when nil
577
665
  nil
578
666
  else
579
- h = OrderedHash.new
580
- hint.to_a.each { |k| h[k] = 1 }
667
+ h = BSON::OrderedHash.new
668
+ hint.to_a.each { |k| h[k.to_s] = 1 }
581
669
  h
582
670
  end
583
671
  end
584
-
585
- private
586
-
587
- def generate_index_name(spec)
588
- @j_collection.genIndexName(to_dbobject(spec))
589
- end
590
672
  end
591
673
  end