mongoid 1.0.6 → 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 (79) hide show
  1. data/HISTORY +39 -0
  2. data/Rakefile +1 -1
  3. data/VERSION +1 -1
  4. data/lib/mongoid.rb +3 -1
  5. data/lib/mongoid/associations.rb +5 -5
  6. data/lib/mongoid/associations/has_many.rb +8 -2
  7. data/lib/mongoid/associations/has_many_related.rb +10 -4
  8. data/lib/mongoid/attributes.rb +19 -13
  9. data/lib/mongoid/commands.rb +12 -6
  10. data/lib/mongoid/commands/create.rb +2 -2
  11. data/lib/mongoid/commands/delete_all.rb +1 -1
  12. data/lib/mongoid/commands/save.rb +2 -2
  13. data/lib/mongoid/components.rb +1 -0
  14. data/lib/mongoid/contexts.rb +4 -0
  15. data/lib/mongoid/contexts/enumerable.rb +105 -0
  16. data/lib/mongoid/contexts/mongo.rb +228 -0
  17. data/lib/mongoid/contexts/paging.rb +42 -0
  18. data/lib/mongoid/criteria.rb +42 -191
  19. data/lib/mongoid/document.rb +19 -13
  20. data/lib/mongoid/extensions.rb +1 -0
  21. data/lib/mongoid/extensions/array/accessors.rb +3 -1
  22. data/lib/mongoid/extensions/float/conversions.rb +1 -1
  23. data/lib/mongoid/extensions/hash/accessors.rb +1 -1
  24. data/lib/mongoid/extensions/integer/conversions.rb +1 -0
  25. data/lib/mongoid/fields.rb +6 -5
  26. data/lib/mongoid/matchers.rb +36 -0
  27. data/lib/mongoid/matchers/all.rb +11 -0
  28. data/lib/mongoid/matchers/default.rb +20 -0
  29. data/lib/mongoid/matchers/exists.rb +13 -0
  30. data/lib/mongoid/matchers/gt.rb +11 -0
  31. data/lib/mongoid/matchers/gte.rb +11 -0
  32. data/lib/mongoid/matchers/in.rb +11 -0
  33. data/lib/mongoid/matchers/lt.rb +11 -0
  34. data/lib/mongoid/matchers/lte.rb +11 -0
  35. data/lib/mongoid/matchers/ne.rb +11 -0
  36. data/lib/mongoid/matchers/nin.rb +11 -0
  37. data/lib/mongoid/matchers/size.rb +11 -0
  38. data/lib/mongoid/scope.rb +17 -1
  39. data/mongoid.gemspec +51 -5
  40. data/spec/integration/mongoid/associations_spec.rb +67 -5
  41. data/spec/integration/mongoid/attributes_spec.rb +22 -0
  42. data/spec/integration/mongoid/commands_spec.rb +51 -12
  43. data/spec/integration/mongoid/criteria_spec.rb +3 -3
  44. data/spec/integration/mongoid/document_spec.rb +8 -8
  45. data/spec/integration/mongoid/finders_spec.rb +1 -1
  46. data/spec/integration/mongoid/inheritance_spec.rb +6 -0
  47. data/spec/integration/mongoid/named_scope_spec.rb +1 -1
  48. data/spec/spec_helper.rb +47 -6
  49. data/spec/unit/mongoid/associations/has_many_related_spec.rb +42 -0
  50. data/spec/unit/mongoid/associations/has_many_spec.rb +40 -1
  51. data/spec/unit/mongoid/attributes_spec.rb +1 -1
  52. data/spec/unit/mongoid/commands/create_spec.rb +3 -3
  53. data/spec/unit/mongoid/commands/delete_all_spec.rb +2 -2
  54. data/spec/unit/mongoid/commands/save_spec.rb +2 -2
  55. data/spec/unit/mongoid/commands_spec.rb +12 -12
  56. data/spec/unit/mongoid/contexts/enumerable_spec.rb +208 -0
  57. data/spec/unit/mongoid/contexts/mongo_spec.rb +370 -0
  58. data/spec/unit/mongoid/criteria_spec.rb +182 -21
  59. data/spec/unit/mongoid/extensions/array/accessors_spec.rb +9 -9
  60. data/spec/unit/mongoid/extensions/date/conversions_spec.rb +2 -1
  61. data/spec/unit/mongoid/extensions/float/conversions_spec.rb +4 -4
  62. data/spec/unit/mongoid/extensions/integer/conversions_spec.rb +1 -1
  63. data/spec/unit/mongoid/fields_spec.rb +3 -3
  64. data/spec/unit/mongoid/identity_spec.rb +2 -2
  65. data/spec/unit/mongoid/matchers/all_spec.rb +27 -0
  66. data/spec/unit/mongoid/matchers/default_spec.rb +27 -0
  67. data/spec/unit/mongoid/matchers/exists_spec.rb +56 -0
  68. data/spec/unit/mongoid/matchers/gt_spec.rb +39 -0
  69. data/spec/unit/mongoid/matchers/gte_spec.rb +49 -0
  70. data/spec/unit/mongoid/matchers/in_spec.rb +27 -0
  71. data/spec/unit/mongoid/matchers/lt_spec.rb +39 -0
  72. data/spec/unit/mongoid/matchers/lte_spec.rb +49 -0
  73. data/spec/unit/mongoid/matchers/ne_spec.rb +27 -0
  74. data/spec/unit/mongoid/matchers/nin_spec.rb +27 -0
  75. data/spec/unit/mongoid/matchers/size_spec.rb +27 -0
  76. data/spec/unit/mongoid/matchers_spec.rb +329 -0
  77. data/spec/unit/mongoid/scope_spec.rb +70 -0
  78. data/spec/unit/mongoid/timestamps_spec.rb +2 -2
  79. metadata +50 -4
@@ -0,0 +1,228 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Contexts #:nodoc:
4
+ class Mongo
5
+ include Paging
6
+ attr_reader :selector, :options, :klass
7
+
8
+ AGGREGATE_REDUCE = "function(obj, prev) { prev.count++; }"
9
+ # Aggregate the context. This will take the internally built selector and options
10
+ # and pass them on to the Ruby driver's +group()+ method on the collection. The
11
+ # collection itself will be retrieved from the class provided, and once the
12
+ # query has returned it will provided a grouping of keys with counts.
13
+ #
14
+ # Example:
15
+ #
16
+ # <tt>context.aggregate</tt>
17
+ #
18
+ # Returns:
19
+ #
20
+ # A +Hash+ with field values as keys, counts as values
21
+ def aggregate
22
+ @klass.collection.group(@options[:fields], @selector, { :count => 0 }, AGGREGATE_REDUCE, true)
23
+ end
24
+
25
+ # Get the count of matching documents in the database for the context.
26
+ #
27
+ # Example:
28
+ #
29
+ # <tt>context.count</tt>
30
+ #
31
+ # Returns:
32
+ #
33
+ # An +Integer+ count of documents.
34
+ def count
35
+ @count ||= @klass.collection.find(@selector, process_options).count
36
+ end
37
+
38
+ GROUP_REDUCE = "function(obj, prev) { prev.group.push(obj); }"
39
+ # Groups the context. This will take the internally built selector and options
40
+ # and pass them on to the Ruby driver's +group()+ method on the collection. The
41
+ # collection itself will be retrieved from the class provided, and once the
42
+ # query has returned it will provided a grouping of keys with objects.
43
+ #
44
+ # Example:
45
+ #
46
+ # <tt>context.group</tt>
47
+ #
48
+ # Returns:
49
+ #
50
+ # A +Hash+ with field values as keys, arrays of documents as values.
51
+ def group
52
+ @klass.collection.group(
53
+ @options[:fields],
54
+ @selector,
55
+ { :group => [] },
56
+ GROUP_REDUCE,
57
+ true
58
+ ).collect do |docs|
59
+ docs["group"] = docs["group"].collect do |attrs|
60
+ instantiate(attrs)
61
+ end
62
+ docs
63
+ end
64
+ end
65
+
66
+ # Execute the context. This will take the selector and options
67
+ # and pass them on to the Ruby driver's +find()+ method on the collection. The
68
+ # collection itself will be retrieved from the class provided, and once the
69
+ # query has returned new documents of the type of class provided will be instantiated.
70
+ #
71
+ # Example:
72
+ #
73
+ # <tt>mongo.execute</tt>
74
+ #
75
+ # Returns:
76
+ #
77
+ # An +Array+ of documents
78
+ def execute
79
+ attributes = @klass.collection.find(@selector, process_options)
80
+ if attributes
81
+ @count = attributes.count
82
+ attributes.collect { |doc| instantiate(doc) }
83
+ else
84
+ []
85
+ end
86
+ end
87
+
88
+ # Create the new mongo context. This will execute the queries given the
89
+ # selector and options against the database.
90
+ #
91
+ # Example:
92
+ #
93
+ # <tt>Mongoid::Contexts::Mongo.new(selector, options, klass)</tt>
94
+ def initialize(selector, options, klass)
95
+ @selector, @options, @klass = selector, options, klass
96
+ if klass.hereditary
97
+ @hereditary = true
98
+ end
99
+ end
100
+
101
+ # Return the last result for the +Context+. Essentially does a find_one on
102
+ # the collection with the sorting reversed. If no sorting parameters have
103
+ # been provided it will default to ids.
104
+ #
105
+ # Example:
106
+ #
107
+ # <tt>context.last</tt>
108
+ #
109
+ # Returns:
110
+ #
111
+ # The last document in the collection.
112
+ def last
113
+ opts = process_options
114
+ sorting = opts[:sort]
115
+ sorting = [[:_id, :asc]] unless sorting
116
+ opts[:sort] = sorting.collect { |option| [ option[0], option[1].invert ] }
117
+ attributes = @klass.collection.find_one(@selector, opts)
118
+ attributes ? instantiate(attributes) : nil
119
+ end
120
+
121
+ MAX_REDUCE = "function(obj, prev) { if (prev.max == 'start') { prev.max = obj.[field]; } " +
122
+ "if (prev.max < obj.[field]) { prev.max = obj.[field]; } }"
123
+ # Return the max value for a field.
124
+ #
125
+ # This will take the internally built selector and options
126
+ # and pass them on to the Ruby driver's +group()+ method on the collection. The
127
+ # collection itself will be retrieved from the class provided, and once the
128
+ # query has returned it will provided a grouping of keys with sums.
129
+ #
130
+ # Example:
131
+ #
132
+ # <tt>context.max(:age)</tt>
133
+ #
134
+ # Returns:
135
+ #
136
+ # A numeric max value.
137
+ def max(field)
138
+ grouped(:max, field.to_s, MAX_REDUCE)
139
+ end
140
+
141
+ MIN_REDUCE = "function(obj, prev) { if (prev.min == 'start') { prev.min = obj.[field]; } " +
142
+ "if (prev.min > obj.[field]) { prev.min = obj.[field]; } }"
143
+ # Return the min value for a field.
144
+ #
145
+ # This will take the internally built selector and options
146
+ # and pass them on to the Ruby driver's +group()+ method on the collection. The
147
+ # collection itself will be retrieved from the class provided, and once the
148
+ # query has returned it will provided a grouping of keys with sums.
149
+ #
150
+ # Example:
151
+ #
152
+ # <tt>context.min(:age)</tt>
153
+ #
154
+ # Returns:
155
+ #
156
+ # A numeric minimum value.
157
+ def min(field)
158
+ grouped(:min, field.to_s, MIN_REDUCE)
159
+ end
160
+
161
+ # Return the first result for the +Context+.
162
+ #
163
+ # Example:
164
+ #
165
+ # <tt>context.one</tt>
166
+ #
167
+ # Return:
168
+ #
169
+ # The first document in the collection.
170
+ def one
171
+ attributes = @klass.collection.find_one(@selector, process_options)
172
+ attributes ? instantiate(attributes) : nil
173
+ end
174
+
175
+ alias :first :one
176
+
177
+ SUM_REDUCE = "function(obj, prev) { if (prev.sum == 'start') { prev.sum = 0; } prev.sum += obj.[field]; }"
178
+ # Sum the context.
179
+ #
180
+ # This will take the internally built selector and options
181
+ # and pass them on to the Ruby driver's +group()+ method on the collection. The
182
+ # collection itself will be retrieved from the class provided, and once the
183
+ # query has returned it will provided a grouping of keys with sums.
184
+ #
185
+ # Example:
186
+ #
187
+ # <tt>context.sum(:age)</tt>
188
+ #
189
+ # Returns:
190
+ #
191
+ # A numeric value that is the sum.
192
+ def sum(field)
193
+ grouped(:sum, field.to_s, SUM_REDUCE)
194
+ end
195
+
196
+ protected
197
+ # Common functionality for grouping operations. Currently used by min, max
198
+ # and sum. Will gsub the field name in the supplied reduce function.
199
+ def grouped(start, field, reduce)
200
+ collection = @klass.collection.group(
201
+ nil,
202
+ @selector,
203
+ { start => "start" },
204
+ reduce.gsub("[field]", field),
205
+ true
206
+ )
207
+ collection.first[start.to_s]
208
+ end
209
+
210
+ # If hereditary instantiate by _type otherwise use the klass.
211
+ def instantiate(attrs)
212
+ @hereditary ? attrs["_type"].constantize.instantiate(attrs) : @klass.instantiate(attrs)
213
+ end
214
+
215
+ # Filters the field list. If no fields have been supplied, then it will be
216
+ # empty. If fields have been defined then _type will be included as well.
217
+ def process_options
218
+ fields = @options[:fields]
219
+ if fields && fields.size > 0 && !fields.include?(:_type)
220
+ fields << :_type
221
+ @options[:fields] = fields
222
+ end
223
+ @options.dup
224
+ end
225
+
226
+ end
227
+ end
228
+ end
@@ -0,0 +1,42 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Contexts #:nodoc:
4
+ module Paging
5
+ # Paginates the documents.
6
+ #
7
+ # Example:
8
+ #
9
+ # <tt>context.paginate</tt>
10
+ #
11
+ # Returns:
12
+ #
13
+ # A collection of documents paginated.
14
+ def paginate
15
+ @collection ||= execute
16
+ WillPaginate::Collection.create(page, per_page, count) do |pager|
17
+ pager.replace(@collection)
18
+ end
19
+ end
20
+
21
+ # Either returns the page option and removes it from the options, or
22
+ # returns a default value of 1.
23
+ #
24
+ # Returns:
25
+ #
26
+ # An +Integer+ page number.
27
+ def page
28
+ skips, limits = @options[:skip], @options[:limit]
29
+ (skips && limits) ? (skips + limits) / limits : 1
30
+ end
31
+
32
+ # Get the number of results per page or the default of 20.
33
+ #
34
+ # Returns:
35
+ #
36
+ # The +Integer+ number of documents in each page.
37
+ def per_page
38
+ (@options[:limit] || 20).to_i
39
+ end
40
+ end
41
+ end
42
+ end
@@ -17,8 +17,36 @@ module Mongoid #:nodoc:
17
17
  class Criteria
18
18
  include Enumerable
19
19
 
20
+ attr_accessor :documents
20
21
  attr_reader :klass, :options, :selector
21
22
 
23
+ delegate \
24
+ :aggregate,
25
+ :count,
26
+ :execute,
27
+ :first,
28
+ :group,
29
+ :last,
30
+ :max,
31
+ :min,
32
+ :one,
33
+ :page,
34
+ :paginate,
35
+ :per_page,
36
+ :sum, :to => :context
37
+
38
+ # Concatinate the criteria with another enumerable. If the other is a
39
+ # +Criteria+ then it needs to get the collection from it.
40
+ def +(other)
41
+ entries + (other.is_a?(Criteria) ? other.entries : other)
42
+ end
43
+
44
+ # Returns the difference between the criteria and another enumerable. If
45
+ # the other is a +Criteria+ then it needs to get the collection from it.
46
+ def -(other)
47
+ entries - (other.is_a?(Criteria) ? other.entries : other)
48
+ end
49
+
22
50
  # Returns true if the supplied +Enumerable+ or +Criteria+ is equal to the results
23
51
  # of this +Criteria+ or the criteria itself.
24
52
  #
@@ -39,19 +67,6 @@ module Mongoid #:nodoc:
39
67
  end
40
68
  end
41
69
 
42
- AGGREGATE_REDUCE = "function(obj, prev) { prev.count++; }"
43
- # Aggregate the criteria. This will take the internally built selector and options
44
- # and pass them on to the Ruby driver's +group()+ method on the collection. The
45
- # collection itself will be retrieved from the class provided, and once the
46
- # query has returned it will provided a grouping of keys with counts.
47
- #
48
- # Example:
49
- #
50
- # <tt>criteria.select(:field1).where(:field1 => "Title").aggregate(Person)</tt>
51
- def aggregate
52
- @klass.collection.group(@options[:fields], @selector, { :count => 0 }, AGGREGATE_REDUCE, true)
53
- end
54
-
55
70
  # Adds a criterion to the +Criteria+ that specifies values that must all
56
71
  # be matched in order to return results. Similar to an "in" clause but the
57
72
  # underlying conditional logic is an "AND" and not an "OR". The MongoDB
@@ -92,15 +107,12 @@ module Mongoid #:nodoc:
92
107
  where(selector)
93
108
  end
94
109
 
95
- # Get the count of matching documents in the database for the +Criteria+.
96
- #
97
- # Example:
98
- #
99
- # <tt>criteria.count</tt>
110
+ # Return or create the context in which this criteria should be executed.
100
111
  #
101
- # Returns: <tt>Integer</tt>
102
- def count
103
- @count ||= @klass.collection.find(@selector, process_options).count
112
+ # This will return an Enumerable context if the class is embedded,
113
+ # otherwise it will return a Mongo context for root classes.
114
+ def context
115
+ @context ||= determine_context
104
116
  end
105
117
 
106
118
  # Merges the supplied argument hash into a single criteria
@@ -167,30 +179,6 @@ module Mongoid #:nodoc:
167
179
  @options = extras; filter_options; self
168
180
  end
169
181
 
170
- GROUP_REDUCE = "function(obj, prev) { prev.group.push(obj); }"
171
- # Groups the criteria. This will take the internally built selector and options
172
- # and pass them on to the Ruby driver's +group()+ method on the collection. The
173
- # collection itself will be retrieved from the class provided, and once the
174
- # query has returned it will provided a grouping of keys with objects.
175
- #
176
- # Example:
177
- #
178
- # <tt>criteria.select(:field1).where(:field1 => "Title").group(Person)</tt>
179
- def group
180
- @klass.collection.group(
181
- @options[:fields],
182
- @selector,
183
- { :group => [] },
184
- GROUP_REDUCE,
185
- true
186
- ).collect do |docs|
187
- docs["group"] = docs["group"].collect do |attrs|
188
- instantiate(attrs)
189
- end
190
- docs
191
- end
192
- end
193
-
194
182
  # Adds a criterion to the +Criteria+ that specifies values where any can
195
183
  # be matched in order to return results. This is similar to an SQL "IN"
196
184
  # clause. The MongoDB conditional operator that will be used is "$in".
@@ -234,29 +222,13 @@ module Mongoid #:nodoc:
234
222
  # type: One of :all, :first:, or :last
235
223
  # klass: The class to execute on.
236
224
  def initialize(klass)
237
- @selector, @options, @klass = {}, {}, klass
225
+ @selector, @options, @klass, @documents = {}, {}, klass, []
238
226
  if klass.hereditary
239
227
  @selector = { :_type => { "$in" => klass._types } }
240
228
  @hereditary = true
241
229
  end
242
230
  end
243
231
 
244
- # Return the last result for the +Criteria+. Essentially does a find_one on
245
- # the collection with the sorting reversed. If no sorting parameters have
246
- # been provided it will default to ids.
247
- #
248
- # Example:
249
- #
250
- # <tt>Criteria.select(:name).where(:name = "Chrissy").last</tt>
251
- def last
252
- opts = process_options
253
- sorting = opts[:sort]
254
- sorting = [[:_id, :asc]] unless sorting
255
- opts[:sort] = sorting.collect { |option| [ option[0], option[1].invert ] }
256
- attributes = @klass.collection.find_one(@selector, opts)
257
- attributes ? instantiate(attributes) : nil
258
- end
259
-
260
232
  # Adds a criterion to the +Criteria+ that specifies the maximum number of
261
233
  # results to return. This is mostly used in conjunction with <tt>skip()</tt>
262
234
  # to handle paginated results.
@@ -274,38 +246,6 @@ module Mongoid #:nodoc:
274
246
  @options[:limit] = value; self
275
247
  end
276
248
 
277
- MIN_REDUCE = "function(obj, prev) { if (prev.min == 'start') { prev.min = obj.[field]; } " +
278
- "if (prev.min > obj.[field]) { prev.min = obj.[field]; } }"
279
- # Return the min value for a field.
280
- #
281
- # This will take the internally built selector and options
282
- # and pass them on to the Ruby driver's +group()+ method on the collection. The
283
- # collection itself will be retrieved from the class provided, and once the
284
- # query has returned it will provided a grouping of keys with sums.
285
- #
286
- # Example:
287
- #
288
- # <tt>criteria.min(:age)</tt>
289
- def min(field)
290
- grouped(:min, field.to_s, MIN_REDUCE)
291
- end
292
-
293
- MAX_REDUCE = "function(obj, prev) { if (prev.max == 'start') { prev.max = obj.[field]; } " +
294
- "if (prev.max < obj.[field]) { prev.max = obj.[field]; } }"
295
- # Return the max value for a field.
296
- #
297
- # This will take the internally built selector and options
298
- # and pass them on to the Ruby driver's +group()+ method on the collection. The
299
- # collection itself will be retrieved from the class provided, and once the
300
- # query has returned it will provided a grouping of keys with sums.
301
- #
302
- # Example:
303
- #
304
- # <tt>criteria.max(:age)</tt>
305
- def max(field)
306
- grouped(:max, field.to_s, MAX_REDUCE)
307
- end
308
-
309
249
  # Merges another object into this +Criteria+. The other object may be a
310
250
  # +Criteria+ or a +Hash+. This is used to combine multiple scopes together,
311
251
  # where a chained scope situation may be desired.
@@ -320,6 +260,7 @@ module Mongoid #:nodoc:
320
260
  def merge(other)
321
261
  @selector.update(other.selector)
322
262
  @options.update(other.options)
263
+ @documents = other.documents
323
264
  end
324
265
 
325
266
  # Used for chaining +Criteria+ scopes together in the for of class methods
@@ -357,7 +298,7 @@ module Mongoid #:nodoc:
357
298
  new_scope.merge(self)
358
299
  return new_scope
359
300
  else
360
- return collect.send(name, *args)
301
+ return entries.send(name, *args)
361
302
  end
362
303
  end
363
304
 
@@ -388,18 +329,6 @@ module Mongoid #:nodoc:
388
329
  @options[:skip]
389
330
  end
390
331
 
391
- # Return the first result for the +Criteria+.
392
- #
393
- # Example:
394
- #
395
- # <tt>Criteria.select(:name).where(:name = "Chrissy").one</tt>
396
- def one
397
- attributes = @klass.collection.find_one(@selector, process_options)
398
- attributes ? instantiate(attributes) : nil
399
- end
400
-
401
- alias :first :one
402
-
403
332
  # Adds a criterion to the +Criteria+ that specifies the fields that will
404
333
  # get returned from the Document. Used mainly for list views that do not
405
334
  # require all fields to be present. This is similar to SQL "SELECT" values.
@@ -433,30 +362,6 @@ module Mongoid #:nodoc:
433
362
  @options[:sort] = params; self
434
363
  end
435
364
 
436
- # Either returns the page option and removes it from the options, or
437
- # returns a default value of 1.
438
- def page
439
- skips, limits = @options[:skip], @options[:limit]
440
- (skips && limits) ? (skips + limits) / limits : 1
441
- end
442
-
443
- # Executes the +Criteria+ and paginates the results.
444
- #
445
- # Example:
446
- #
447
- # <tt>criteria.paginate</tt>
448
- def paginate
449
- @collection ||= execute
450
- WillPaginate::Collection.create(page, per_page, count) do |pager|
451
- pager.replace(@collection)
452
- end
453
- end
454
-
455
- # Returns the number of results per page or the default of 20.
456
- def per_page
457
- (@options[:limit] || 20).to_i
458
- end
459
-
460
365
  # Returns the selector and options as a +Hash+ that would be passed to a
461
366
  # scope for use with named scopes.
462
367
  def scoped
@@ -481,20 +386,7 @@ module Mongoid #:nodoc:
481
386
  @options[:skip] = value; self
482
387
  end
483
388
 
484
- SUM_REDUCE = "function(obj, prev) { if (prev.sum == 'start') { prev.sum = 0; } prev.sum += obj.[field]; }"
485
- # Sum the criteria.
486
- #
487
- # This will take the internally built selector and options
488
- # and pass them on to the Ruby driver's +group()+ method on the collection. The
489
- # collection itself will be retrieved from the class provided, and once the
490
- # query has returned it will provided a grouping of keys with sums.
491
- #
492
- # Example:
493
- #
494
- # <tt>criteria.sum(:age)</tt>
495
- def sum(field)
496
- grouped(:sum, field.to_s, SUM_REDUCE)
497
- end
389
+ alias :to_ary :to_a
498
390
 
499
391
  # Translate the supplied arguments into a +Criteria+ object.
500
392
  #
@@ -554,24 +446,12 @@ module Mongoid #:nodoc:
554
446
  end
555
447
 
556
448
  protected
557
- # Execute the criteria. This will take the internally built selector and options
558
- # and pass them on to the Ruby driver's +find()+ method on the collection. The
559
- # collection itself will be retrieved from the class provided, and once the
560
- # query has returned new documents of the type of class provided will be instantiated.
561
- #
562
- # If this is a +Criteria+ to only find the first object, this will return a
563
- # single object of the type of class provided.
564
- #
565
- # If this is a +Criteria+ to find multiple results, will return an +Array+ of
566
- # objects of the type of class provided.
567
- def execute
568
- attributes = @klass.collection.find(@selector, process_options)
569
- if attributes
570
- @count = attributes.count
571
- attributes.collect { |doc| instantiate(doc) }
572
- else
573
- []
449
+ # Determines the context to be used for this criteria.
450
+ def determine_context
451
+ if @klass.embedded
452
+ return Contexts::Enumerable.new(@selector, @options, @documents)
574
453
  end
454
+ Contexts::Mongo.new(@selector, @options, @klass)
575
455
  end
576
456
 
577
457
  # Filters the unused options out of the options +Hash+. Currently this
@@ -586,35 +466,6 @@ module Mongoid #:nodoc:
586
466
  end
587
467
  end
588
468
 
589
- # Common functionality for grouping operations. Currently used by min, max
590
- # and sum. Will gsub the field name in the supplied reduce function.
591
- def grouped(start, field, reduce)
592
- collection = @klass.collection.group(
593
- nil,
594
- @selector,
595
- { start => "start" },
596
- reduce.gsub("[field]", field),
597
- true
598
- )
599
- collection.first[start.to_s]
600
- end
601
-
602
- # If hereditary instantiate by _type otherwise use the klass.
603
- def instantiate(attrs)
604
- @hereditary ? attrs["_type"].constantize.instantiate(attrs) : @klass.instantiate(attrs)
605
- end
606
-
607
- # Filters the field list. If no fields have been supplied, then it will be
608
- # empty. If fields have been defined then _type will be included as well.
609
- def process_options
610
- fields = @options[:fields]
611
- if fields && fields.size > 0 && !fields.include?(:_type)
612
- fields << :_type
613
- @options[:fields] = fields
614
- end
615
- @options.dup
616
- end
617
-
618
469
  # Update the selector setting the operator on the value for each key in the
619
470
  # supplied attributes +Hash+.
620
471
  def update_selector(attributes, operator)