mongoid 7.4.3 → 7.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/lib/config/locales/en.yml +7 -0
- data/lib/mongoid/association/embedded/batchable.rb +3 -20
- data/lib/mongoid/association/macros.rb +20 -0
- data/lib/mongoid/association/referenced/has_many/enumerable.rb +12 -8
- data/lib/mongoid/association/referenced/has_many/proxy.rb +2 -2
- data/lib/mongoid/atomic/paths/embedded/many.rb +0 -19
- data/lib/mongoid/config.rb +6 -1
- data/lib/mongoid/contextual/memory.rb +144 -12
- data/lib/mongoid/contextual/mongo.rb +118 -26
- data/lib/mongoid/contextual/none.rb +45 -1
- data/lib/mongoid/criteria/queryable/extensions/array.rb +2 -0
- data/lib/mongoid/criteria/queryable/extensions/hash.rb +2 -0
- data/lib/mongoid/criteria/queryable/mergeable.rb +21 -0
- data/lib/mongoid/criteria/queryable/selectable.rb +26 -10
- data/lib/mongoid/criteria.rb +2 -0
- data/lib/mongoid/document.rb +2 -0
- data/lib/mongoid/equality.rb +4 -4
- data/lib/mongoid/errors/document_not_found.rb +23 -6
- data/lib/mongoid/fields.rb +145 -21
- data/lib/mongoid/findable.rb +20 -5
- data/lib/mongoid/version.rb +1 -1
- data/lib/mongoid/warnings.rb +29 -0
- data/lib/mongoid.rb +1 -0
- data/lib/rails/generators/mongoid/config/templates/mongoid.yml +4 -3
- data/spec/integration/i18n_fallbacks_spec.rb +15 -1
- data/spec/mongoid/association/embedded/embeds_many/proxy_spec.rb +0 -21
- data/spec/mongoid/association/embedded/embeds_many_models.rb +0 -121
- data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_spec.rb +0 -8
- data/spec/mongoid/association/referenced/has_many/enumerable_spec.rb +54 -0
- data/spec/mongoid/association/referenced/has_many/proxy_spec.rb +8 -24
- data/spec/mongoid/clients/options_spec.rb +1 -0
- data/spec/mongoid/config_spec.rb +10 -4
- data/spec/mongoid/contextual/memory_spec.rb +826 -65
- data/spec/mongoid/contextual/mongo_spec.rb +781 -18
- data/spec/mongoid/contextual/none_spec.rb +46 -0
- data/spec/mongoid/criteria/queryable/selectable_spec.rb +212 -39
- data/spec/mongoid/criteria_spec.rb +8 -0
- data/spec/mongoid/equality_spec.rb +12 -12
- data/spec/mongoid/errors/document_not_found_spec.rb +49 -0
- data/spec/mongoid/findable_spec.rb +30 -0
- data/spec/support/models/code.rb +2 -0
- data.tar.gz.sig +0 -0
- metadata +3 -2
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 32597c6d6b536f273850be1989db0d876026d2bffda067d7923f173c3966faa6
|
4
|
+
data.tar.gz: 97fffa62c90ae5c20e7dcf00eb151b557c26ab5ae676c45b5651efa5143cf34c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dbe0c003a7e058f5ab6ac5592170ef1a26e02bf9be729b889c55a3a70af4e0c8b6e52f7157cc9b40879545689972769bae5deabf6814ce1ebeb97fdf1a128212
|
7
|
+
data.tar.gz: d696cd95560d51b34318e7f25467450dde8461383dd17830f1177363cbf006c863b1268a2b79616e563343a3fd0e388c9d3bde67821b698b4cf0035bbfc9b719
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/lib/config/locales/en.yml
CHANGED
@@ -404,6 +404,13 @@ en:
|
|
404
404
|
\_\_\_\_\_\_default:\n
|
405
405
|
\_\_\_\_\_\_\_\_hosts:\n
|
406
406
|
\_\_\_\_\_\_\_\_\_\_- localhost:27017\n\n"
|
407
|
+
no_documents_found:
|
408
|
+
message: "Could not find a document of class %{klass}."
|
409
|
+
summary: "Mongoid attempted to find a document of the class %{klass}
|
410
|
+
but none exist."
|
411
|
+
resolution: "Create a document of class %{klass} or use a finder
|
412
|
+
method that returns nil when no documents are found instead of
|
413
|
+
raising an exception."
|
407
414
|
no_environment:
|
408
415
|
message: "Could not load the configuration since no environment
|
409
416
|
was defined."
|
@@ -80,8 +80,7 @@ module Mongoid
|
|
80
80
|
def batch_replace(docs)
|
81
81
|
if docs.blank?
|
82
82
|
if _assigning? && !empty?
|
83
|
-
_base.delayed_atomic_sets.
|
84
|
-
clear_atomic_path_cache
|
83
|
+
_base.delayed_atomic_sets.clear
|
85
84
|
_base.add_atomic_unset(first)
|
86
85
|
target_duplicate = _target.dup
|
87
86
|
pre_process_batch_remove(target_duplicate, :delete)
|
@@ -93,8 +92,7 @@ module Mongoid
|
|
93
92
|
_base.delayed_atomic_sets.clear unless _assigning?
|
94
93
|
docs = normalize_docs(docs).compact
|
95
94
|
_target.clear and _unscoped.clear
|
96
|
-
_base.delayed_atomic_unsets.
|
97
|
-
clear_atomic_path_cache
|
95
|
+
_base.delayed_atomic_unsets.clear
|
98
96
|
inserts = execute_batch_set(docs)
|
99
97
|
add_atomic_sets(inserts)
|
100
98
|
end
|
@@ -236,22 +234,7 @@ module Mongoid
|
|
236
234
|
#
|
237
235
|
# @return [ String ] The atomic path.
|
238
236
|
def path
|
239
|
-
@path ||=
|
240
|
-
Mongoid::Atomic::Paths::Embedded::Many.position_without_document(_base, _association)
|
241
|
-
else
|
242
|
-
_unscoped.first.atomic_path
|
243
|
-
end
|
244
|
-
end
|
245
|
-
|
246
|
-
# Clear the cache for path and atomic_paths. This method is used when
|
247
|
-
# the path method is used, and the association has not been set on the
|
248
|
-
# document yet, which can cause path and atomic_paths to be calculated
|
249
|
-
# incorrectly later.
|
250
|
-
#
|
251
|
-
# @api private
|
252
|
-
def clear_atomic_path_cache
|
253
|
-
self.path = nil
|
254
|
-
_base.instance_variable_set("@atomic_paths", nil)
|
237
|
+
@path ||= _unscoped.first.atomic_path
|
255
238
|
end
|
256
239
|
|
257
240
|
# Set the atomic path.
|
@@ -12,6 +12,26 @@ module Mongoid
|
|
12
12
|
class_attribute :embedded, instance_reader: false
|
13
13
|
class_attribute :embedded_relations
|
14
14
|
class_attribute :relations
|
15
|
+
|
16
|
+
# A hash that maps aliases to their associations. This hash maps the
|
17
|
+
# associations "in database name" to its "in code" name. This is used when
|
18
|
+
# associations specify the `store_as` option, or on a referenced association.
|
19
|
+
# On a referenced association, this is used to map the foreign key to
|
20
|
+
# the association's name. For example, if we had the following
|
21
|
+
# relationship:
|
22
|
+
#
|
23
|
+
# User has_many Accounts
|
24
|
+
#
|
25
|
+
# User will have an entry in the aliased associations hash:
|
26
|
+
#
|
27
|
+
# account_ids => accounts
|
28
|
+
#
|
29
|
+
# Note that on the belongs_to associations, the mapping from
|
30
|
+
# foreign key => name is not in the aliased_associations hash, but a
|
31
|
+
# mapping from name => foreign key is in the aliased_fields hash.
|
32
|
+
#
|
33
|
+
# @return [ Hash<String, String> ] The aliased associations hash.
|
34
|
+
#
|
15
35
|
# @api private
|
16
36
|
class_attribute :aliased_associations
|
17
37
|
self.embedded = false
|
@@ -243,14 +243,16 @@ module Mongoid
|
|
243
243
|
# use the option { id_sort: :none }.
|
244
244
|
# Be aware that #first/#last won't guarantee order in this case.
|
245
245
|
#
|
246
|
-
# @param [ Hash ]
|
246
|
+
# @param [ Integer | Hash ] limit_or_opts The number of documents to
|
247
|
+
# return, or a hash of options.
|
247
248
|
#
|
248
|
-
# @option
|
249
|
+
# @option limit_or_opts [ :none ] :id_sort This option is deprecated.
|
250
|
+
# Don't apply a sort on _id if no other sort is defined on the criteria.
|
249
251
|
#
|
250
252
|
# @return [ Document ] The first document found.
|
251
|
-
def first(
|
253
|
+
def first(limit_or_opts = nil)
|
252
254
|
_loaded.try(:values).try(:first) ||
|
253
|
-
_added[(ul = _unloaded.try(:first,
|
255
|
+
_added[(ul = _unloaded.try(:first, limit_or_opts)).try(:_id)] ||
|
254
256
|
ul ||
|
255
257
|
_added.values.try(:first)
|
256
258
|
end
|
@@ -330,15 +332,17 @@ module Mongoid
|
|
330
332
|
# use the option { id_sort: :none }.
|
331
333
|
# Be aware that #first/#last won't guarantee order in this case.
|
332
334
|
#
|
333
|
-
# @param [ Hash ]
|
335
|
+
# @param [ Integer | Hash ] limit_or_opts The number of documents to
|
336
|
+
# return, or a hash of options.
|
334
337
|
#
|
335
|
-
# @option
|
338
|
+
# @option limit_or_opts [ :none ] :id_sort This option is deprecated.
|
339
|
+
# Don't apply a sort on _id if no other sort is defined on the criteria.
|
336
340
|
#
|
337
341
|
# @return [ Document ] The last document found.
|
338
|
-
def last(
|
342
|
+
def last(limit_or_opts = nil)
|
339
343
|
_added.values.try(:last) ||
|
340
344
|
_loaded.try(:values).try(:last) ||
|
341
|
-
_added[(ul = _unloaded.try(:last,
|
345
|
+
_added[(ul = _unloaded.try(:last, limit_or_opts)).try(:_id)] ||
|
342
346
|
ul
|
343
347
|
end
|
344
348
|
|
@@ -469,8 +469,8 @@ module Mongoid
|
|
469
469
|
selector = conditions || {}
|
470
470
|
removed = klass.send(method, selector.merge!(criteria.selector))
|
471
471
|
_target.delete_if do |doc|
|
472
|
-
doc._matches?(selector)
|
473
|
-
unbind_one(doc)
|
472
|
+
if doc._matches?(selector)
|
473
|
+
unbind_one(doc) and true
|
474
474
|
end
|
475
475
|
end
|
476
476
|
removed
|
@@ -34,25 +34,6 @@ module Mongoid
|
|
34
34
|
locator = document.new_record? ? "" : ".#{document._index}"
|
35
35
|
"#{pos}#{"." unless pos.blank?}#{document._association.store_as}#{locator}"
|
36
36
|
end
|
37
|
-
|
38
|
-
class << self
|
39
|
-
|
40
|
-
# Get the position of where the document would go for the given
|
41
|
-
# association. The use case for this function is when trying to
|
42
|
-
# persist an empty list for an embedded association. All of the
|
43
|
-
# existing functions for getting the position to store a document
|
44
|
-
# require passing in a document to store, which we don't have when
|
45
|
-
# trying to store the empty list.
|
46
|
-
#
|
47
|
-
# @param [ Document ] parent The parent document to store in.
|
48
|
-
# @param [ Association ] association The association.
|
49
|
-
#
|
50
|
-
# @return [ String ] The position string.
|
51
|
-
def position_without_document(parent, association)
|
52
|
-
pos = parent.atomic_position
|
53
|
-
"#{pos}#{"." unless pos.blank?}#{association.store_as}"
|
54
|
-
end
|
55
|
-
end
|
56
37
|
end
|
57
38
|
end
|
58
39
|
end
|
data/lib/mongoid/config.rb
CHANGED
@@ -23,7 +23,8 @@ module Mongoid
|
|
23
23
|
# database name is not explicitly defined.
|
24
24
|
option :app_name, default: nil
|
25
25
|
|
26
|
-
#
|
26
|
+
# (Deprecated) In MongoDB 4.0 and earlier, set whether to create
|
27
|
+
# indexes in the background by default. (default: false)
|
27
28
|
option :background_indexing, default: false
|
28
29
|
|
29
30
|
# Mark belongs_to associations as required by default, so that saving a
|
@@ -111,6 +112,10 @@ module Mongoid
|
|
111
112
|
# demongoize the values on returning them.
|
112
113
|
option :legacy_pluck_distinct, default: true
|
113
114
|
|
115
|
+
# Combine chained operators, which use the same field and operator,
|
116
|
+
# using and's instead of overwriting them.
|
117
|
+
option :overwrite_chained_operators, default: true
|
118
|
+
|
114
119
|
# Has Mongoid been configured? This is checking that at least a valid
|
115
120
|
# client config exists.
|
116
121
|
#
|
@@ -78,7 +78,11 @@ module Mongoid
|
|
78
78
|
#
|
79
79
|
# @return [ Array<Object> ] The distinct values for the field.
|
80
80
|
def distinct(field)
|
81
|
-
|
81
|
+
if Mongoid.legacy_pluck_distinct
|
82
|
+
documents.map{ |doc| doc.send(field) }.uniq
|
83
|
+
else
|
84
|
+
pluck(field).uniq
|
85
|
+
end
|
82
86
|
end
|
83
87
|
|
84
88
|
# Iterate over the context. If provided a block, yield to a Mongoid
|
@@ -116,9 +120,16 @@ module Mongoid
|
|
116
120
|
# @example Get the first document.
|
117
121
|
# context.first
|
118
122
|
#
|
123
|
+
# @param [ Integer | Hash ] limit_or_opts The number of documents to
|
124
|
+
# return, or a hash of options.
|
125
|
+
#
|
119
126
|
# @return [ Document ] The first document.
|
120
|
-
def first(
|
121
|
-
|
127
|
+
def first(limit_or_opts = nil)
|
128
|
+
if limit_or_opts.nil? || limit_or_opts.is_a?(Hash)
|
129
|
+
eager_load([documents.first]).first
|
130
|
+
else
|
131
|
+
eager_load(documents.first(limit_or_opts))
|
132
|
+
end
|
122
133
|
end
|
123
134
|
alias :one :first
|
124
135
|
alias :find_first :first
|
@@ -159,9 +170,52 @@ module Mongoid
|
|
159
170
|
# @example Get the last document.
|
160
171
|
# context.last
|
161
172
|
#
|
173
|
+
# @param [ Integer | Hash ] limit_or_opts The number of documents to
|
174
|
+
# return, or a hash of options.
|
175
|
+
#
|
176
|
+
# @option limit_or_opts [ :none ] :id_sort This option is deprecated.
|
177
|
+
# Don't apply a sort on _id if no other sort is defined on the criteria.
|
178
|
+
#
|
162
179
|
# @return [ Document ] The last document.
|
163
|
-
def last
|
164
|
-
|
180
|
+
def last(limit_or_opts = nil)
|
181
|
+
if limit_or_opts.nil? || limit_or_opts.is_a?(Hash)
|
182
|
+
eager_load([documents.last]).first
|
183
|
+
else
|
184
|
+
eager_load(documents.last(limit_or_opts))
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
# Take the given number of documents from the database.
|
189
|
+
#
|
190
|
+
# @example Take a document.
|
191
|
+
# context.take
|
192
|
+
#
|
193
|
+
# @param [ Integer | nil ] limit The number of documents to take or nil.
|
194
|
+
#
|
195
|
+
# @return [ Document ] The document.
|
196
|
+
def take(limit = nil)
|
197
|
+
if limit
|
198
|
+
eager_load(documents.take(limit))
|
199
|
+
else
|
200
|
+
eager_load([documents.first]).first
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
# Take the given number of documents from the database.
|
205
|
+
#
|
206
|
+
# @example Take a document.
|
207
|
+
# context.take
|
208
|
+
#
|
209
|
+
# @return [ Document ] The document.
|
210
|
+
#
|
211
|
+
# @raises [ Mongoid::Errors::DocumentNotFound ] raises when there are no
|
212
|
+
# documents to take.
|
213
|
+
def take!
|
214
|
+
if documents.empty?
|
215
|
+
raise Errors::DocumentNotFound.new(klass, nil, nil)
|
216
|
+
else
|
217
|
+
eager_load([documents.first]).first
|
218
|
+
end
|
165
219
|
end
|
166
220
|
|
167
221
|
# Get the length of matching documents in the context.
|
@@ -189,14 +243,35 @@ module Mongoid
|
|
189
243
|
end
|
190
244
|
|
191
245
|
def pluck(*fields)
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
246
|
+
if Mongoid.legacy_pluck_distinct
|
247
|
+
documents.pluck(*fields)
|
248
|
+
else
|
249
|
+
documents.map do |d|
|
250
|
+
if fields.length == 1
|
251
|
+
retrieve_value_at_path(d, fields.first)
|
252
|
+
else
|
253
|
+
fields.map do |field|
|
254
|
+
retrieve_value_at_path(d, field)
|
255
|
+
end
|
256
|
+
end
|
198
257
|
end
|
199
|
-
end
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
# Tally the field values in memory.
|
262
|
+
#
|
263
|
+
# @example Get the counts of values in memory.
|
264
|
+
# context.tally(:name)
|
265
|
+
#
|
266
|
+
# @param [ String | Symbol ] field Field to tally.
|
267
|
+
#
|
268
|
+
# @return [ Hash ] The hash of counts.
|
269
|
+
def tally(field)
|
270
|
+
return documents.each_with_object({}) do |d, acc|
|
271
|
+
v = retrieve_value_at_path(d, field)
|
272
|
+
acc[v] ||= 0
|
273
|
+
acc[v] += 1
|
274
|
+
end
|
200
275
|
end
|
201
276
|
|
202
277
|
# Skips the provided number of documents.
|
@@ -414,6 +489,63 @@ module Mongoid
|
|
414
489
|
def _session
|
415
490
|
@criteria.send(:_session)
|
416
491
|
end
|
492
|
+
|
493
|
+
# Retrieve the value for the current document at the given field path.
|
494
|
+
#
|
495
|
+
# For example, if I have the following models:
|
496
|
+
#
|
497
|
+
# User has_many Accounts
|
498
|
+
# address is a hash on Account
|
499
|
+
#
|
500
|
+
# u = User.new(accounts: [ Account.new(address: { street: "W 50th" }) ])
|
501
|
+
# retrieve_value_at_path(u, "user.accounts.address.street")
|
502
|
+
# # => [ "W 50th" ]
|
503
|
+
#
|
504
|
+
# Note that the result is in an array since accounts is an array. If it
|
505
|
+
# was nested in two arrays the result would be in a 2D array.
|
506
|
+
#
|
507
|
+
# @param [ Object ] document The object to traverse the field path.
|
508
|
+
# @param [ String ] field_path The dotted string that represents the path
|
509
|
+
# to the value.
|
510
|
+
#
|
511
|
+
# @return [ Object | nil ] The value at the given field path or nil if it
|
512
|
+
# doesn't exist.
|
513
|
+
def retrieve_value_at_path(document, field_path)
|
514
|
+
return if field_path.blank? || !document
|
515
|
+
segment, remaining = field_path.to_s.split('.', 2)
|
516
|
+
|
517
|
+
curr = if document.is_a?(Document)
|
518
|
+
# Retrieves field for segment to check localization. Only does one
|
519
|
+
# iteration since there's no dots
|
520
|
+
res = if remaining
|
521
|
+
field = document.class.traverse_association_tree(segment)
|
522
|
+
# If this is a localized field, and there are remaining, get the
|
523
|
+
# _translations hash so that we can get the specified translation in
|
524
|
+
# the remaining
|
525
|
+
if field&.localized?
|
526
|
+
document.send("#{segment}_translations")
|
527
|
+
end
|
528
|
+
end
|
529
|
+
meth = klass.aliased_associations[segment] || segment
|
530
|
+
res.nil? ? document.try(meth) : res
|
531
|
+
elsif document.is_a?(Hash)
|
532
|
+
# TODO: Remove the indifferent access when implementing MONGOID-5410.
|
533
|
+
document.key?(segment.to_s) ?
|
534
|
+
document[segment.to_s] :
|
535
|
+
document[segment.to_sym]
|
536
|
+
else
|
537
|
+
nil
|
538
|
+
end
|
539
|
+
|
540
|
+
return curr unless remaining
|
541
|
+
|
542
|
+
if curr.is_a?(Array)
|
543
|
+
# compact is used for consistency with server behavior.
|
544
|
+
curr.map { |d| retrieve_value_at_path(d, remaining) }.compact
|
545
|
+
else
|
546
|
+
retrieve_value_at_path(curr, remaining)
|
547
|
+
end
|
548
|
+
end
|
417
549
|
end
|
418
550
|
end
|
419
551
|
end
|
@@ -42,6 +42,7 @@ module Mongoid
|
|
42
42
|
#
|
43
43
|
# @return [ true, false ] If the context is cached.
|
44
44
|
def cached?
|
45
|
+
Mongoid::Warnings.warn_criteria_cache_deprecated
|
45
46
|
!!@cache
|
46
47
|
end
|
47
48
|
|
@@ -251,25 +252,28 @@ module Mongoid
|
|
251
252
|
# and have no sort defined on the criteria, use the option { id_sort: :none }.
|
252
253
|
# Be aware that #first/#last won't guarantee order in this case.
|
253
254
|
#
|
254
|
-
# @param [ Hash ]
|
255
|
+
# @param [ Integer | Hash ] limit_or_opts The number of documents to
|
256
|
+
# return, or a hash of options.
|
255
257
|
#
|
256
|
-
# @option
|
257
|
-
# is defined on the criteria.
|
258
|
+
# @option limit_or_opts [ :none ] :id_sort This option is deprecated.
|
259
|
+
# Don't apply a sort on _id if no other sort is defined on the criteria.
|
258
260
|
#
|
259
261
|
# @return [ Document ] The first document.
|
260
|
-
def first(
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
262
|
+
def first(limit_or_opts = nil)
|
263
|
+
limit = limit_or_opts unless limit_or_opts.is_a?(Hash)
|
264
|
+
if cached? && cache_loaded?
|
265
|
+
return limit ? documents.first(limit) : documents.first
|
266
|
+
end
|
267
|
+
try_numbered_cache(:first, limit) do
|
268
|
+
if limit_or_opts.try(:key?, :id_sort)
|
269
|
+
Mongoid::Warnings.warn_id_sort_deprecated
|
270
|
+
end
|
271
|
+
sorted_view = view
|
272
|
+
if sort = view.sort || ({ _id: 1 } unless limit_or_opts.try(:fetch, :id_sort) == :none)
|
273
|
+
sorted_view = view.sort(sort)
|
274
|
+
end
|
275
|
+
if raw_docs = sorted_view.limit(limit || 1).to_a
|
276
|
+
process_raw_docs(raw_docs, limit)
|
273
277
|
end
|
274
278
|
end
|
275
279
|
end
|
@@ -325,6 +329,10 @@ module Mongoid
|
|
325
329
|
#
|
326
330
|
# @return [ Array ] The result of mapping.
|
327
331
|
def map(field = nil, &block)
|
332
|
+
if !field.nil?
|
333
|
+
Mongoid::Warnings.warn_map_field_deprecated
|
334
|
+
end
|
335
|
+
|
328
336
|
if block_given?
|
329
337
|
super(&block)
|
330
338
|
else
|
@@ -360,19 +368,26 @@ module Mongoid
|
|
360
368
|
# and have no sort defined on the criteria, use the option { id_sort: :none }.
|
361
369
|
# Be aware that #first/#last won't guarantee order in this case.
|
362
370
|
#
|
363
|
-
# @param [ Hash ]
|
371
|
+
# @param [ Integer | Hash ] limit_or_opts The number of documents to
|
372
|
+
# return, or a hash of options.
|
364
373
|
#
|
365
|
-
# @option
|
366
|
-
# is defined on the criteria.
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
374
|
+
# @option limit_or_opts [ :none ] :id_sort This option is deprecated.
|
375
|
+
# Don't apply a sort on _id if no other sort is defined on the criteria.
|
376
|
+
#
|
377
|
+
# @return [ Document ] The last document.
|
378
|
+
def last(limit_or_opts = nil)
|
379
|
+
limit = limit_or_opts unless limit_or_opts.is_a?(Hash)
|
380
|
+
if cached? && cache_loaded?
|
381
|
+
return limit ? documents.last(limit) : documents.last
|
382
|
+
end
|
383
|
+
res = try_numbered_cache(:last, limit) do
|
384
|
+
with_inverse_sorting(limit_or_opts) do
|
385
|
+
if raw_docs = view.limit(limit || 1).to_a
|
386
|
+
process_raw_docs(raw_docs, limit)
|
373
387
|
end
|
374
388
|
end
|
375
389
|
end
|
390
|
+
res.is_a?(Array) ? res.reverse : res
|
376
391
|
end
|
377
392
|
|
378
393
|
# Get's the number of documents matching the query selector.
|
@@ -398,6 +413,44 @@ module Mongoid
|
|
398
413
|
@view = view.limit(value) and self
|
399
414
|
end
|
400
415
|
|
416
|
+
# Take the given number of documents from the database.
|
417
|
+
#
|
418
|
+
# @example Take 10 documents
|
419
|
+
# context.take(10)
|
420
|
+
#
|
421
|
+
# @param [ Integer | nil ] limit The number of documents to return or nil.
|
422
|
+
#
|
423
|
+
# @return [ Document | Array<Document> ] The list of documents, or one
|
424
|
+
# document if no value was given.
|
425
|
+
def take(limit = nil)
|
426
|
+
if limit
|
427
|
+
limit(limit).to_a
|
428
|
+
else
|
429
|
+
# Do to_a first so that the Mongo#first method is not used and the
|
430
|
+
# result is not sorted.
|
431
|
+
limit(1).to_a.first
|
432
|
+
end
|
433
|
+
end
|
434
|
+
|
435
|
+
# Take one document from the database and raise an error if there are none.
|
436
|
+
#
|
437
|
+
# @example Take a document
|
438
|
+
# context.take!
|
439
|
+
#
|
440
|
+
# @return [ Document ] The document.
|
441
|
+
#
|
442
|
+
# @raises [ Mongoid::Errors::DocumentNotFound ] raises when there are no
|
443
|
+
# documents to take.
|
444
|
+
def take!
|
445
|
+
# Do to_a first so that the Mongo#first method is not used and the
|
446
|
+
# result is not sorted.
|
447
|
+
if fst = limit(1).to_a.first
|
448
|
+
fst
|
449
|
+
else
|
450
|
+
raise Errors::DocumentNotFound.new(klass, nil, nil)
|
451
|
+
end
|
452
|
+
end
|
453
|
+
|
401
454
|
# Initiate a map/reduce operation from the context.
|
402
455
|
#
|
403
456
|
# @example Initiate a map/reduce.
|
@@ -534,6 +587,31 @@ module Mongoid
|
|
534
587
|
end
|
535
588
|
end
|
536
589
|
|
590
|
+
# yield the block given or return the cached value
|
591
|
+
#
|
592
|
+
# @param [ String, Symbol ] key The instance variable name
|
593
|
+
# @param [ Integer | nil ] n The number of documents requested or nil
|
594
|
+
# if none is requested.
|
595
|
+
#
|
596
|
+
# @return [ Object ] The result of the block.
|
597
|
+
def try_numbered_cache(key, n, &block)
|
598
|
+
unless cached?
|
599
|
+
yield if block_given?
|
600
|
+
else
|
601
|
+
len = n || 1
|
602
|
+
ret = instance_variable_get("@#{key}")
|
603
|
+
if !ret || ret.length < len
|
604
|
+
instance_variable_set("@#{key}", ret = Array.wrap(yield))
|
605
|
+
elsif !n
|
606
|
+
ret.is_a?(Array) ? ret.first : ret
|
607
|
+
elsif ret.length > len
|
608
|
+
ret.first(n)
|
609
|
+
else
|
610
|
+
ret
|
611
|
+
end
|
612
|
+
end
|
613
|
+
end
|
614
|
+
|
537
615
|
# Update the documents for the provided method.
|
538
616
|
#
|
539
617
|
# @api private
|
@@ -598,8 +676,10 @@ module Mongoid
|
|
598
676
|
# @example Apply the inverse sorting params to the given block
|
599
677
|
# context.with_inverse_sorting
|
600
678
|
def with_inverse_sorting(opts = {})
|
679
|
+
Mongoid::Warnings.warn_id_sort_deprecated if opts.try(:key?, :id_sort)
|
680
|
+
|
601
681
|
begin
|
602
|
-
if sort = criteria.options[:sort] || ( { _id: 1 } unless opts
|
682
|
+
if sort = criteria.options[:sort] || ( { _id: 1 } unless opts.try(:fetch, :id_sort) == :none )
|
603
683
|
@view = view.sort(Hash[sort.map{|k, v| [k, -1*v]}])
|
604
684
|
end
|
605
685
|
yield
|
@@ -793,6 +873,18 @@ module Mongoid
|
|
793
873
|
end
|
794
874
|
end
|
795
875
|
end
|
876
|
+
|
877
|
+
# Process the raw documents retrieved for #first/#last.
|
878
|
+
#
|
879
|
+
# @return [ Array<Document> | Document ] The list of documents or a
|
880
|
+
# single document.
|
881
|
+
def process_raw_docs(raw_docs, limit)
|
882
|
+
docs = raw_docs.map do |d|
|
883
|
+
Factory.from_db(klass, d, criteria)
|
884
|
+
end
|
885
|
+
docs = eager_load(docs)
|
886
|
+
limit ? docs : docs.first
|
887
|
+
end
|
796
888
|
end
|
797
889
|
end
|
798
890
|
end
|
@@ -105,13 +105,57 @@ module Mongoid
|
|
105
105
|
@criteria, @klass = criteria, criteria.klass
|
106
106
|
end
|
107
107
|
|
108
|
+
# Always returns nil.
|
109
|
+
#
|
110
|
+
# @example Get the first document in null context.
|
111
|
+
# context.first
|
112
|
+
#
|
113
|
+
# @param [ Integer | Hash ] limit_or_opts The number of documents to
|
114
|
+
# return, or a hash of options.
|
115
|
+
#
|
116
|
+
# @return [ nil ] Always nil.
|
117
|
+
def first(limit_or_opts = nil)
|
118
|
+
if !limit_or_opts.nil? && !limit_or_opts.is_a?(Hash)
|
119
|
+
[]
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
108
123
|
# Always returns nil.
|
109
124
|
#
|
110
125
|
# @example Get the last document in null context.
|
111
126
|
# context.last
|
112
127
|
#
|
128
|
+
# @param [ Integer | Hash ] limit_or_opts The number of documents to
|
129
|
+
# return, or a hash of options.
|
130
|
+
#
|
113
131
|
# @return [ nil ] Always nil.
|
114
|
-
def last
|
132
|
+
def last(limit_or_opts = nil)
|
133
|
+
if !limit_or_opts.nil? && !limit_or_opts.is_a?(Hash)
|
134
|
+
[]
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# Returns nil or empty array.
|
139
|
+
#
|
140
|
+
# @example Take a document in null context.
|
141
|
+
# context.take
|
142
|
+
#
|
143
|
+
# @param [ Integer | nil ] limit The number of documents to take or nil.
|
144
|
+
#
|
145
|
+
# @return [ [] | nil ] Empty array or nil.
|
146
|
+
def take(limit = nil)
|
147
|
+
limit ? [] : nil
|
148
|
+
end
|
149
|
+
|
150
|
+
# Always raises an error.
|
151
|
+
#
|
152
|
+
# @example Take a document in null context.
|
153
|
+
# context.take!
|
154
|
+
#
|
155
|
+
# @raises [ Mongoid::Errors::DocumentNotFound ] always raises.
|
156
|
+
def take!
|
157
|
+
raise Errors::DocumentNotFound.new(klass, nil, nil)
|
158
|
+
end
|
115
159
|
|
116
160
|
# Always returns zero.
|
117
161
|
#
|