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