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
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