mongoid 9.0.7 → 9.0.9
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
- data/lib/mongoid/association/embedded/batchable.rb +11 -10
- data/lib/mongoid/association/embedded/embeds_many/proxy.rb +64 -29
- data/lib/mongoid/association/nested/many.rb +2 -0
- data/lib/mongoid/association/nested/one.rb +1 -1
- data/lib/mongoid/association/referenced/has_and_belongs_to_many/proxy.rb +0 -6
- data/lib/mongoid/association/referenced/has_many/enumerable.rb +40 -0
- data/lib/mongoid/association/referenced/has_many/proxy.rb +17 -5
- data/lib/mongoid/attributes.rb +19 -1
- data/lib/mongoid/changeable.rb +10 -1
- data/lib/mongoid/clients/sessions.rb +3 -4
- data/lib/mongoid/config.rb +1 -1
- data/lib/mongoid/contextual/aggregable/mongo.rb +6 -1
- data/lib/mongoid/contextual/mongo.rb +8 -89
- data/lib/mongoid/pluckable.rb +132 -0
- data/lib/mongoid/railties/bson_object_id_serializer.rb +7 -0
- data/lib/mongoid/reloadable.rb +6 -0
- data/lib/mongoid/traversable.rb +0 -2
- data/lib/mongoid/version.rb +1 -1
- data/spec/integration/associations/embeds_many_spec.rb +110 -0
- data/spec/integration/associations/has_and_belongs_to_many_spec.rb +81 -0
- data/spec/integration/associations/has_many_spec.rb +56 -0
- data/spec/integration/associations/has_one_spec.rb +55 -3
- data/spec/mongoid/association/referenced/has_many/enumerable_spec.rb +394 -0
- data/spec/mongoid/association/referenced/has_many_models.rb +24 -0
- data/spec/mongoid/association/referenced/has_one_models.rb +10 -2
- data/spec/mongoid/attributes_spec.rb +13 -0
- data/spec/mongoid/clients/transactions_spec.rb +162 -1
- data/spec/mongoid/clients/transactions_spec_models.rb +93 -0
- data/spec/mongoid/contextual/aggregable/mongo_spec.rb +33 -0
- data/spec/mongoid/reloadable_spec.rb +24 -0
- data/spec/shared/CANDIDATE.md +28 -0
- data/spec/shared/lib/mrss/spec_organizer.rb +32 -3
- data/spec/shared/shlib/server.sh +1 -1
- data/spec/support/models/company.rb +2 -0
- data/spec/support/models/passport.rb +1 -0
- data/spec/support/models/product.rb +2 -0
- data/spec/support/models/seo.rb +2 -0
- metadata +7 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c5dca0df68ed7086b6c641b1021e556812e23872d5bd52a9777093e2d8cf1e64
|
|
4
|
+
data.tar.gz: 24630d19104369a922b3a5a152012ae8eba3719576ef3c9e5f97304b6896a1c4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 03013a0c28cda90cfc3b99c26d22e7672e8ce1c6449e599d7ae39a42d19ba742f37dea7510411ca004c7d5d5f25af80e9230bac63fef8ce43515711d7f669343
|
|
7
|
+
data.tar.gz: 8bd5b0c4faa4f5f16fcc56ff612bae5a14fd40fa01513fda545469d225c6cd4909022c066719950147dc116dc1a7bdb79b69d48ad40454aabdb45481994dd69b
|
|
@@ -313,18 +313,19 @@ module Mongoid
|
|
|
313
313
|
#
|
|
314
314
|
# @return [ Array<Hash> ] The documents as an array of hashes.
|
|
315
315
|
def pre_process_batch_insert(docs)
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
316
|
+
[].tap do |results|
|
|
317
|
+
append_many(docs) do |doc|
|
|
318
|
+
if persistable? && !_assigning?
|
|
319
|
+
self.path = doc.atomic_path unless path
|
|
320
|
+
if doc.valid?(:create)
|
|
321
|
+
doc.run_before_callbacks(:save, :create)
|
|
322
|
+
else
|
|
323
|
+
self.inserts_valid = false
|
|
324
|
+
end
|
|
325
325
|
end
|
|
326
|
+
|
|
327
|
+
results << doc.send(:as_attributes)
|
|
326
328
|
end
|
|
327
|
-
doc.send(:as_attributes)
|
|
328
329
|
end
|
|
329
330
|
end
|
|
330
331
|
|
|
@@ -14,6 +14,7 @@ module Mongoid
|
|
|
14
14
|
# the array of child documents.
|
|
15
15
|
class Proxy < Association::Many
|
|
16
16
|
include Batchable
|
|
17
|
+
extend Forwardable
|
|
17
18
|
|
|
18
19
|
# Class-level methods for the Proxy class.
|
|
19
20
|
module ClassMethods
|
|
@@ -54,6 +55,8 @@ module Mongoid
|
|
|
54
55
|
|
|
55
56
|
extend ClassMethods
|
|
56
57
|
|
|
58
|
+
def_delegators :criteria, :find, :pluck
|
|
59
|
+
|
|
57
60
|
# Instantiate a new embeds_many association.
|
|
58
61
|
#
|
|
59
62
|
# @example Create the new association.
|
|
@@ -312,35 +315,6 @@ module Mongoid
|
|
|
312
315
|
end
|
|
313
316
|
end
|
|
314
317
|
|
|
315
|
-
# Finds a document in this association through several different
|
|
316
|
-
# methods.
|
|
317
|
-
#
|
|
318
|
-
# This method delegates to +Mongoid::Criteria#find+. If this method is
|
|
319
|
-
# not given a block, it returns one or many documents for the provided
|
|
320
|
-
# _id values.
|
|
321
|
-
#
|
|
322
|
-
# If this method is given a block, it returns the first document
|
|
323
|
-
# of those found by the current Criteria object for which the block
|
|
324
|
-
# returns a truthy value.
|
|
325
|
-
#
|
|
326
|
-
# @example Find a document by its id.
|
|
327
|
-
# person.addresses.find(BSON::ObjectId.new)
|
|
328
|
-
#
|
|
329
|
-
# @example Find documents for multiple ids.
|
|
330
|
-
# person.addresses.find([ BSON::ObjectId.new, BSON::ObjectId.new ])
|
|
331
|
-
#
|
|
332
|
-
# @example Finds the first matching document using a block.
|
|
333
|
-
# person.addresses.find { |addr| addr.state == 'CA' }
|
|
334
|
-
#
|
|
335
|
-
# @param [ Object... ] *args Various arguments.
|
|
336
|
-
# @param &block Optional block to pass.
|
|
337
|
-
# @yield [ Object ] Yields each enumerable element to the block.
|
|
338
|
-
#
|
|
339
|
-
# @return [ Document | Array<Document> | nil ] A document or matching documents.
|
|
340
|
-
def find(...)
|
|
341
|
-
criteria.find(...)
|
|
342
|
-
end
|
|
343
|
-
|
|
344
318
|
# Get all the documents in the association that are loaded into memory.
|
|
345
319
|
#
|
|
346
320
|
# @example Get the in memory documents.
|
|
@@ -443,6 +417,67 @@ module Mongoid
|
|
|
443
417
|
execute_callback :after_add, document
|
|
444
418
|
end
|
|
445
419
|
|
|
420
|
+
# Returns a unique id for the document, which is either
|
|
421
|
+
# its _id or its object_id.
|
|
422
|
+
def id_of(doc)
|
|
423
|
+
doc._id || doc.object_id
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
# Optimized version of #append that handles multiple documents
|
|
427
|
+
# in a more efficient way.
|
|
428
|
+
#
|
|
429
|
+
# @param [ Array<Document> ] documents The documents to append.
|
|
430
|
+
#
|
|
431
|
+
# @return [ EmbedsMany::Proxy ] This proxy instance.
|
|
432
|
+
def append_many(documents, &block)
|
|
433
|
+
unique_set = process_incoming_docs(documents, &block)
|
|
434
|
+
|
|
435
|
+
_unscoped.concat(unique_set)
|
|
436
|
+
_target.push(*scope(unique_set))
|
|
437
|
+
update_attributes_hash
|
|
438
|
+
|
|
439
|
+
unique_set.each { |doc| execute_callback :after_add, doc }
|
|
440
|
+
|
|
441
|
+
self
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
# Processes the list of documents, building a list of those
|
|
445
|
+
# that are not already in the association, and preparing
|
|
446
|
+
# each unique document to be integrated into the association.
|
|
447
|
+
#
|
|
448
|
+
# The :before_add callback is executed for each unique document
|
|
449
|
+
# as part of this step.
|
|
450
|
+
#
|
|
451
|
+
# @param [ Array<Document> ] documents The incoming documents to
|
|
452
|
+
# process.
|
|
453
|
+
#
|
|
454
|
+
# @yield [ Document ] Optional block to call for each unique
|
|
455
|
+
# document.
|
|
456
|
+
#
|
|
457
|
+
# @return [ Array<Document> ] The list of unique documents that
|
|
458
|
+
# do not yet exist in the association.
|
|
459
|
+
def process_incoming_docs(documents, &block)
|
|
460
|
+
visited_docs = Set.new(_target.map { |doc| id_of(doc) })
|
|
461
|
+
next_index = _unscoped.size
|
|
462
|
+
|
|
463
|
+
documents.select do |doc|
|
|
464
|
+
next unless doc
|
|
465
|
+
|
|
466
|
+
id = id_of(doc)
|
|
467
|
+
next if visited_docs.include?(id)
|
|
468
|
+
|
|
469
|
+
execute_callback :before_add, doc
|
|
470
|
+
|
|
471
|
+
visited_docs.add(id)
|
|
472
|
+
integrate(doc)
|
|
473
|
+
|
|
474
|
+
doc._index = next_index
|
|
475
|
+
next_index += 1
|
|
476
|
+
|
|
477
|
+
block&.call(doc) || true
|
|
478
|
+
end
|
|
479
|
+
end
|
|
480
|
+
|
|
446
481
|
# Instantiate the binding associated with this association.
|
|
447
482
|
#
|
|
448
483
|
# @example Create the binding.
|
|
@@ -53,8 +53,6 @@ module Mongoid
|
|
|
53
53
|
# @param [ Document... ] *args Any number of documents.
|
|
54
54
|
#
|
|
55
55
|
# @return [ Array<Document> ] The loaded docs.
|
|
56
|
-
#
|
|
57
|
-
# rubocop:disable Metrics/AbcSize
|
|
58
56
|
def <<(*args)
|
|
59
57
|
docs = args.flatten
|
|
60
58
|
return concat(docs) if docs.size > 1
|
|
@@ -89,7 +87,6 @@ module Mongoid
|
|
|
89
87
|
end
|
|
90
88
|
unsynced(_base, foreign_key) and self
|
|
91
89
|
end
|
|
92
|
-
# rubocop:enable Metrics/AbcSize
|
|
93
90
|
|
|
94
91
|
alias push <<
|
|
95
92
|
|
|
@@ -360,8 +357,6 @@ module Mongoid
|
|
|
360
357
|
# in bulk
|
|
361
358
|
# @param [ Array ] inserts the list of Hashes of attributes that will
|
|
362
359
|
# be inserted (corresponding to the ``docs`` list)
|
|
363
|
-
#
|
|
364
|
-
# rubocop:disable Metrics/AbcSize
|
|
365
360
|
def append_document(doc, ids, docs, inserts)
|
|
366
361
|
return unless doc
|
|
367
362
|
|
|
@@ -379,7 +374,6 @@ module Mongoid
|
|
|
379
374
|
unsynced(_base, foreign_key)
|
|
380
375
|
end
|
|
381
376
|
end
|
|
382
|
-
# rubocop:enable Metrics/AbcSize
|
|
383
377
|
end
|
|
384
378
|
end
|
|
385
379
|
end
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
# rubocop:todo all
|
|
3
3
|
|
|
4
|
+
require 'mongoid/pluckable'
|
|
5
|
+
|
|
4
6
|
module Mongoid
|
|
5
7
|
module Association
|
|
6
8
|
module Referenced
|
|
@@ -12,6 +14,7 @@ module Mongoid
|
|
|
12
14
|
class Enumerable
|
|
13
15
|
extend Forwardable
|
|
14
16
|
include ::Enumerable
|
|
17
|
+
include Pluckable
|
|
15
18
|
|
|
16
19
|
# The three main instance variables are collections of documents.
|
|
17
20
|
#
|
|
@@ -374,6 +377,43 @@ module Mongoid
|
|
|
374
377
|
@_added, @_loaded, @_unloaded, @executed = data
|
|
375
378
|
end
|
|
376
379
|
|
|
380
|
+
# Plucks the given field names from the documents in the target.
|
|
381
|
+
# If the collection has been loaded, it plucks from the loaded
|
|
382
|
+
# documents; otherwise, it plucks from the unloaded criteria.
|
|
383
|
+
# Regardless, it also plucks from any added documents.
|
|
384
|
+
#
|
|
385
|
+
# @param [ Symbol... ] *fields The field names to pluck.
|
|
386
|
+
#
|
|
387
|
+
# @return [ Array | Array<Array> ] The array of field values. If
|
|
388
|
+
# multiple fields are given, an array of arrays is returned.
|
|
389
|
+
def pluck(*keys)
|
|
390
|
+
[].tap do |results|
|
|
391
|
+
if _loaded? || _added.any?
|
|
392
|
+
klass = @_association.klass
|
|
393
|
+
prepared = prepare_pluck(keys, document_class: klass)
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
if _loaded?
|
|
397
|
+
docs = _loaded.values.map { |v| BSON::Document.new(v.attributes) }
|
|
398
|
+
results.concat pluck_from_documents(docs, prepared[:field_names], document_class: klass)
|
|
399
|
+
elsif _unloaded
|
|
400
|
+
criteria = if _added.any?
|
|
401
|
+
ids_to_exclude = _added.keys
|
|
402
|
+
_unloaded.not(:_id.in => ids_to_exclude)
|
|
403
|
+
else
|
|
404
|
+
_unloaded
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
results.concat criteria.pluck(*keys)
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
if _added.any?
|
|
411
|
+
docs = _added.values.map { |v| BSON::Document.new(v.attributes) }
|
|
412
|
+
results.concat pluck_from_documents(docs, prepared[:field_names], document_class: klass)
|
|
413
|
+
end
|
|
414
|
+
end
|
|
415
|
+
end
|
|
416
|
+
|
|
377
417
|
# Reset the enumerable back to its persisted state.
|
|
378
418
|
#
|
|
379
419
|
# @example Reset the enumerable.
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# TODO: consider refactoring this Proxy class, to satisfy the following
|
|
4
|
-
# cops...
|
|
5
|
-
# rubocop:disable Metrics/ClassLength
|
|
6
3
|
module Mongoid
|
|
7
4
|
module Association
|
|
8
5
|
module Referenced
|
|
@@ -36,7 +33,7 @@ module Mongoid
|
|
|
36
33
|
|
|
37
34
|
extend ClassMethods
|
|
38
35
|
|
|
39
|
-
|
|
36
|
+
def_delegators :criteria, :count
|
|
40
37
|
def_delegators :_target, :first, :in_memory, :last, :reset, :uniq
|
|
41
38
|
|
|
42
39
|
# Instantiate a new references_many association. Will set the foreign key
|
|
@@ -284,6 +281,22 @@ module Mongoid
|
|
|
284
281
|
|
|
285
282
|
alias nullify_all nullify
|
|
286
283
|
|
|
284
|
+
# Plucks the given field names from the documents in the
|
|
285
|
+
# association. It is safe to use whether the association is
|
|
286
|
+
# loaded or not, and whether there are unsaved documents in the
|
|
287
|
+
# association or not.
|
|
288
|
+
#
|
|
289
|
+
# @example Pluck the titles of all posts.
|
|
290
|
+
# person.posts.pluck(:title)
|
|
291
|
+
#
|
|
292
|
+
# @param [ Symbol... ] *fields The field names to pluck.
|
|
293
|
+
#
|
|
294
|
+
# @return [ Array | Array<Array> ] The array of field values. If
|
|
295
|
+
# multiple fields are given, an array of arrays is returned.
|
|
296
|
+
def pluck(*fields)
|
|
297
|
+
_target.pluck(*fields)
|
|
298
|
+
end
|
|
299
|
+
|
|
287
300
|
# Clear the association. Will delete the documents from the db if they are
|
|
288
301
|
# already persisted.
|
|
289
302
|
#
|
|
@@ -588,4 +601,3 @@ module Mongoid
|
|
|
588
601
|
end
|
|
589
602
|
end
|
|
590
603
|
end
|
|
591
|
-
# rubocop:enable Metrics/ClassLength
|
data/lib/mongoid/attributes.rb
CHANGED
|
@@ -175,7 +175,7 @@ module Mongoid
|
|
|
175
175
|
localized = fields[field_name].try(:localized?)
|
|
176
176
|
attributes_before_type_cast[name.to_s] = value
|
|
177
177
|
typed_value = typed_value_for(field_name, value)
|
|
178
|
-
unless
|
|
178
|
+
unless attribute_will_not_change?(field_name, typed_value) || attribute_changed?(field_name)
|
|
179
179
|
attribute_will_change!(field_name)
|
|
180
180
|
end
|
|
181
181
|
if localized
|
|
@@ -366,5 +366,23 @@ module Mongoid
|
|
|
366
366
|
end
|
|
367
367
|
value.present?
|
|
368
368
|
end
|
|
369
|
+
|
|
370
|
+
# If `value` is a `BSON::Decimal128`, convert it to a `BigDecimal` for
|
|
371
|
+
# comparison purposes. This is necessary because `BSON::Decimal128` does
|
|
372
|
+
# not implement `#==` in a way that is compatible with `BigDecimal`.
|
|
373
|
+
def normalize_value(value)
|
|
374
|
+
value.is_a?(BSON::Decimal128) ? BigDecimal(value.to_s) : value
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
# Determine if the attribute will not change, by comparing the current
|
|
378
|
+
# value with the new value. The values are normalized to account for
|
|
379
|
+
# types that do not implement `#==` in a way that is compatible with
|
|
380
|
+
# each other, such as `BSON::Decimal128` and `BigDecimal`.
|
|
381
|
+
def attribute_will_not_change?(field_name, typed_value)
|
|
382
|
+
normalized_attribute = normalize_value(attributes[field_name])
|
|
383
|
+
normalized_typed_value = normalize_value(typed_value)
|
|
384
|
+
|
|
385
|
+
normalized_attribute == normalized_typed_value
|
|
386
|
+
end
|
|
369
387
|
end
|
|
370
388
|
end
|
data/lib/mongoid/changeable.rb
CHANGED
|
@@ -15,6 +15,14 @@ module Mongoid
|
|
|
15
15
|
changed_attributes.keys.select { |attr| attribute_change(attr) }
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
+
# Indicates that the children of this document may have changed, and
|
|
19
|
+
# ought to be checked when the document is validated.
|
|
20
|
+
#
|
|
21
|
+
# @api private
|
|
22
|
+
def children_may_have_changed!
|
|
23
|
+
@children_may_have_changed = true
|
|
24
|
+
end
|
|
25
|
+
|
|
18
26
|
# Has the document changed?
|
|
19
27
|
#
|
|
20
28
|
# @example Has the document changed?
|
|
@@ -31,7 +39,7 @@ module Mongoid
|
|
|
31
39
|
#
|
|
32
40
|
# @return [ true | false ] If any children have changed.
|
|
33
41
|
def children_changed?
|
|
34
|
-
_children.any?(&:changed?)
|
|
42
|
+
@children_may_have_changed || _children.any?(&:changed?)
|
|
35
43
|
end
|
|
36
44
|
|
|
37
45
|
# Get the attribute changes.
|
|
@@ -69,6 +77,7 @@ module Mongoid
|
|
|
69
77
|
@previous_changes = changes
|
|
70
78
|
@attributes_before_last_save = @previous_attributes
|
|
71
79
|
@previous_attributes = attributes.dup
|
|
80
|
+
@children_may_have_changed = false
|
|
72
81
|
reset_atomic_updates!
|
|
73
82
|
changed_attributes.clear
|
|
74
83
|
end
|
|
@@ -92,8 +92,7 @@ module Mongoid
|
|
|
92
92
|
begin
|
|
93
93
|
session.with_transaction(options) do
|
|
94
94
|
yield
|
|
95
|
-
end
|
|
96
|
-
run_commit_callbacks(session)
|
|
95
|
+
end.tap { run_commit_callbacks(session) }
|
|
97
96
|
rescue *transactions_not_supported_exceptions
|
|
98
97
|
raise Mongoid::Errors::TransactionsNotSupported
|
|
99
98
|
rescue Mongoid::Errors::Rollback
|
|
@@ -213,8 +212,8 @@ module Mongoid
|
|
|
213
212
|
|
|
214
213
|
# Transforms custom options for after_commit and after_rollback callbacks
|
|
215
214
|
# into options for +set_callback+.
|
|
216
|
-
def set_options_for_callbacks!(args)
|
|
217
|
-
options = args.extract_options
|
|
215
|
+
def set_options_for_callbacks!(args, enforced_options = {})
|
|
216
|
+
options = args.extract_options!.merge(enforced_options)
|
|
218
217
|
args << options
|
|
219
218
|
|
|
220
219
|
if options[:on]
|
data/lib/mongoid/config.rb
CHANGED
|
@@ -102,7 +102,7 @@ module Mongoid
|
|
|
102
102
|
#
|
|
103
103
|
# - :immediate - Initializes a single +Concurrent::ImmediateExecutor+
|
|
104
104
|
# - :global_thread_pool - Initializes a single +Concurrent::ThreadPoolExecutor+
|
|
105
|
-
# that uses the +
|
|
105
|
+
# that uses the +global_executor_concurrency+ for the +max_threads+ value.
|
|
106
106
|
option :async_query_executor, default: :immediate
|
|
107
107
|
|
|
108
108
|
# Defines how many asynchronous queries can be executed concurrently.
|
|
@@ -27,7 +27,12 @@ module Mongoid
|
|
|
27
27
|
# If no documents are found, then returned Hash will have
|
|
28
28
|
# count, sum of 0 and max, min, avg of nil.
|
|
29
29
|
def aggregates(field)
|
|
30
|
-
result = collection.aggregate(
|
|
30
|
+
result = collection.aggregate(
|
|
31
|
+
pipeline(field),
|
|
32
|
+
session: _session,
|
|
33
|
+
hint: view.hint
|
|
34
|
+
).to_a
|
|
35
|
+
|
|
31
36
|
if result.empty?
|
|
32
37
|
Aggregable::EMPTY_RESULT.dup
|
|
33
38
|
else
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
# rubocop:todo all
|
|
3
3
|
|
|
4
4
|
require 'mongoid/atomic_update_preparer'
|
|
5
|
+
require 'mongoid/pluckable'
|
|
5
6
|
require "mongoid/contextual/mongo/documents_loader"
|
|
6
7
|
require "mongoid/contextual/atomic"
|
|
7
8
|
require "mongoid/contextual/aggregable/mongo"
|
|
@@ -22,6 +23,7 @@ module Mongoid
|
|
|
22
23
|
include Atomic
|
|
23
24
|
include Association::EagerLoadable
|
|
24
25
|
include Queryable
|
|
26
|
+
include Pluckable
|
|
25
27
|
|
|
26
28
|
# Options constant.
|
|
27
29
|
OPTIONS = [ :hint,
|
|
@@ -331,23 +333,12 @@ module Mongoid
|
|
|
331
333
|
# in the array will be a single value. Otherwise, each
|
|
332
334
|
# result in the array will be an array of values.
|
|
333
335
|
def pluck(*fields)
|
|
334
|
-
# Multiple fields can map to the same field name. For example,
|
|
335
|
-
# a field and its _translations field map to the same
|
|
336
|
-
# because of this, we need to
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
normalized_field_names.push(db_fn)
|
|
341
|
-
hash[klass.cleanse_localized_field_names(f)] = true
|
|
342
|
-
hash
|
|
343
|
-
end
|
|
344
|
-
|
|
345
|
-
view.projection(normalized_select).reduce([]) do |plucked, doc|
|
|
346
|
-
values = normalized_field_names.map do |n|
|
|
347
|
-
extract_value(doc, n)
|
|
348
|
-
end
|
|
349
|
-
plucked << (values.size == 1 ? values.first : values)
|
|
350
|
-
end
|
|
336
|
+
# Multiple fields can map to the same field name. For example,
|
|
337
|
+
# plucking a field and its _translations field map to the same
|
|
338
|
+
# field in the database. because of this, we need to prepare the
|
|
339
|
+
# projection specifically.
|
|
340
|
+
prep = prepare_pluck(fields, prepare_projection: true)
|
|
341
|
+
pluck_from_documents(view.projection(prep[:projection]), prep[:field_names])
|
|
351
342
|
end
|
|
352
343
|
|
|
353
344
|
# Pick the single field values from the database.
|
|
@@ -893,78 +884,6 @@ module Mongoid
|
|
|
893
884
|
collection.write_concern.nil? || collection.write_concern.acknowledged?
|
|
894
885
|
end
|
|
895
886
|
|
|
896
|
-
# Fetch the element from the given hash and demongoize it using the
|
|
897
|
-
# given field. If the obj is an array, map over it and call this method
|
|
898
|
-
# on all of its elements.
|
|
899
|
-
#
|
|
900
|
-
# @param [ Hash | Array<Hash> ] obj The hash or array of hashes to fetch from.
|
|
901
|
-
# @param [ String ] meth The key to fetch from the hash.
|
|
902
|
-
# @param [ Field ] field The field to use for demongoization.
|
|
903
|
-
#
|
|
904
|
-
# @return [ Object ] The demongoized value.
|
|
905
|
-
#
|
|
906
|
-
# @api private
|
|
907
|
-
def fetch_and_demongoize(obj, meth, field)
|
|
908
|
-
if obj.is_a?(Array)
|
|
909
|
-
obj.map { |doc| fetch_and_demongoize(doc, meth, field) }
|
|
910
|
-
else
|
|
911
|
-
res = obj.try(:fetch, meth, nil)
|
|
912
|
-
field ? field.demongoize(res) : res.class.demongoize(res)
|
|
913
|
-
end
|
|
914
|
-
end
|
|
915
|
-
|
|
916
|
-
# Extracts the value for the given field name from the given attribute
|
|
917
|
-
# hash.
|
|
918
|
-
#
|
|
919
|
-
# @param [ Hash ] attrs The attributes hash.
|
|
920
|
-
# @param [ String ] field_name The name of the field to extract.
|
|
921
|
-
#
|
|
922
|
-
# @param [ Object ] The value for the given field name
|
|
923
|
-
def extract_value(attrs, field_name)
|
|
924
|
-
i = 1
|
|
925
|
-
num_meths = field_name.count('.') + 1
|
|
926
|
-
curr = attrs.dup
|
|
927
|
-
|
|
928
|
-
klass.traverse_association_tree(field_name) do |meth, obj, is_field|
|
|
929
|
-
field = obj if is_field
|
|
930
|
-
is_translation = false
|
|
931
|
-
# If no association or field was found, check if the meth is an
|
|
932
|
-
# _translations field.
|
|
933
|
-
if obj.nil? & tr = meth.match(/(.*)_translations\z/)&.captures&.first
|
|
934
|
-
is_translation = true
|
|
935
|
-
meth = tr
|
|
936
|
-
end
|
|
937
|
-
|
|
938
|
-
# 1. If curr is an array fetch from all elements in the array.
|
|
939
|
-
# 2. If the field is localized, and is not an _translations field
|
|
940
|
-
# (_translations fields don't show up in the fields hash).
|
|
941
|
-
# - If this is the end of the methods, return the translation for
|
|
942
|
-
# the current locale.
|
|
943
|
-
# - Otherwise, return the whole translations hash so the next method
|
|
944
|
-
# can select the language it wants.
|
|
945
|
-
# 3. If the meth is an _translations field, do not demongoize the
|
|
946
|
-
# value so the full hash is returned.
|
|
947
|
-
# 4. Otherwise, fetch and demongoize the value for the key meth.
|
|
948
|
-
curr = if curr.is_a? Array
|
|
949
|
-
res = fetch_and_demongoize(curr, meth, field)
|
|
950
|
-
res.empty? ? nil : res
|
|
951
|
-
elsif !is_translation && field&.localized?
|
|
952
|
-
if i < num_meths
|
|
953
|
-
curr.try(:fetch, meth, nil)
|
|
954
|
-
else
|
|
955
|
-
fetch_and_demongoize(curr, meth, field)
|
|
956
|
-
end
|
|
957
|
-
elsif is_translation
|
|
958
|
-
curr.try(:fetch, meth, nil)
|
|
959
|
-
else
|
|
960
|
-
fetch_and_demongoize(curr, meth, field)
|
|
961
|
-
end
|
|
962
|
-
|
|
963
|
-
i += 1
|
|
964
|
-
end
|
|
965
|
-
curr
|
|
966
|
-
end
|
|
967
|
-
|
|
968
887
|
# Recursively demongoize the given value. This method recursively traverses
|
|
969
888
|
# the class tree to find the correct field to use to demongoize the value.
|
|
970
889
|
#
|