mongoid 7.4.3 → 7.5.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.
- 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
|
#
|