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.
- data/HISTORY +39 -0
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/lib/mongoid.rb +3 -1
- data/lib/mongoid/associations.rb +5 -5
- data/lib/mongoid/associations/has_many.rb +8 -2
- data/lib/mongoid/associations/has_many_related.rb +10 -4
- data/lib/mongoid/attributes.rb +19 -13
- data/lib/mongoid/commands.rb +12 -6
- data/lib/mongoid/commands/create.rb +2 -2
- data/lib/mongoid/commands/delete_all.rb +1 -1
- data/lib/mongoid/commands/save.rb +2 -2
- data/lib/mongoid/components.rb +1 -0
- data/lib/mongoid/contexts.rb +4 -0
- data/lib/mongoid/contexts/enumerable.rb +105 -0
- data/lib/mongoid/contexts/mongo.rb +228 -0
- data/lib/mongoid/contexts/paging.rb +42 -0
- data/lib/mongoid/criteria.rb +42 -191
- data/lib/mongoid/document.rb +19 -13
- data/lib/mongoid/extensions.rb +1 -0
- data/lib/mongoid/extensions/array/accessors.rb +3 -1
- data/lib/mongoid/extensions/float/conversions.rb +1 -1
- data/lib/mongoid/extensions/hash/accessors.rb +1 -1
- data/lib/mongoid/extensions/integer/conversions.rb +1 -0
- data/lib/mongoid/fields.rb +6 -5
- data/lib/mongoid/matchers.rb +36 -0
- data/lib/mongoid/matchers/all.rb +11 -0
- data/lib/mongoid/matchers/default.rb +20 -0
- data/lib/mongoid/matchers/exists.rb +13 -0
- data/lib/mongoid/matchers/gt.rb +11 -0
- data/lib/mongoid/matchers/gte.rb +11 -0
- data/lib/mongoid/matchers/in.rb +11 -0
- data/lib/mongoid/matchers/lt.rb +11 -0
- data/lib/mongoid/matchers/lte.rb +11 -0
- data/lib/mongoid/matchers/ne.rb +11 -0
- data/lib/mongoid/matchers/nin.rb +11 -0
- data/lib/mongoid/matchers/size.rb +11 -0
- data/lib/mongoid/scope.rb +17 -1
- data/mongoid.gemspec +51 -5
- data/spec/integration/mongoid/associations_spec.rb +67 -5
- data/spec/integration/mongoid/attributes_spec.rb +22 -0
- data/spec/integration/mongoid/commands_spec.rb +51 -12
- data/spec/integration/mongoid/criteria_spec.rb +3 -3
- data/spec/integration/mongoid/document_spec.rb +8 -8
- data/spec/integration/mongoid/finders_spec.rb +1 -1
- data/spec/integration/mongoid/inheritance_spec.rb +6 -0
- data/spec/integration/mongoid/named_scope_spec.rb +1 -1
- data/spec/spec_helper.rb +47 -6
- data/spec/unit/mongoid/associations/has_many_related_spec.rb +42 -0
- data/spec/unit/mongoid/associations/has_many_spec.rb +40 -1
- data/spec/unit/mongoid/attributes_spec.rb +1 -1
- data/spec/unit/mongoid/commands/create_spec.rb +3 -3
- data/spec/unit/mongoid/commands/delete_all_spec.rb +2 -2
- data/spec/unit/mongoid/commands/save_spec.rb +2 -2
- data/spec/unit/mongoid/commands_spec.rb +12 -12
- data/spec/unit/mongoid/contexts/enumerable_spec.rb +208 -0
- data/spec/unit/mongoid/contexts/mongo_spec.rb +370 -0
- data/spec/unit/mongoid/criteria_spec.rb +182 -21
- data/spec/unit/mongoid/extensions/array/accessors_spec.rb +9 -9
- data/spec/unit/mongoid/extensions/date/conversions_spec.rb +2 -1
- data/spec/unit/mongoid/extensions/float/conversions_spec.rb +4 -4
- data/spec/unit/mongoid/extensions/integer/conversions_spec.rb +1 -1
- data/spec/unit/mongoid/fields_spec.rb +3 -3
- data/spec/unit/mongoid/identity_spec.rb +2 -2
- data/spec/unit/mongoid/matchers/all_spec.rb +27 -0
- data/spec/unit/mongoid/matchers/default_spec.rb +27 -0
- data/spec/unit/mongoid/matchers/exists_spec.rb +56 -0
- data/spec/unit/mongoid/matchers/gt_spec.rb +39 -0
- data/spec/unit/mongoid/matchers/gte_spec.rb +49 -0
- data/spec/unit/mongoid/matchers/in_spec.rb +27 -0
- data/spec/unit/mongoid/matchers/lt_spec.rb +39 -0
- data/spec/unit/mongoid/matchers/lte_spec.rb +49 -0
- data/spec/unit/mongoid/matchers/ne_spec.rb +27 -0
- data/spec/unit/mongoid/matchers/nin_spec.rb +27 -0
- data/spec/unit/mongoid/matchers/size_spec.rb +27 -0
- data/spec/unit/mongoid/matchers_spec.rb +329 -0
- data/spec/unit/mongoid/scope_spec.rb +70 -0
- data/spec/unit/mongoid/timestamps_spec.rb +2 -2
- 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
|
data/lib/mongoid/criteria.rb
CHANGED
@@ -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
|
-
#
|
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
|
-
#
|
102
|
-
|
103
|
-
|
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
|
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
|
-
|
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
|
-
#
|
558
|
-
|
559
|
-
|
560
|
-
|
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)
|