@@ -109,7 +109,6 @@ You can safely omit type specifications when:
109
109
110
110
Types that are not supported as dynamic attributes since they cannot be cast are:
111
111
112
- - ``BigDecimal``
113
112
- ``Date``
114
113
- ``DateTime``
115
114
- ``Range``
@@ -247,10 +246,10 @@ assignment to a ``Time`` field:
247
246
248
247
class Voter
249
248
include Mongoid::Document
250
-
249
+
251
250
field :registered_at, type: Time
252
251
end
253
-
252
+
254
253
Voter.new(registered_at: Date.today)
255
254
# => #<Voter _id: 5fdd80392c97a618f07ba344, registered_at: 2020-12-18 05:00:00 UTC>
256
255
@@ -416,6 +415,107 @@ matches strings containing "hello" before a newline, besides strings ending in
416
415
This is because the meaning of ``$`` is different between PCRE and Ruby
417
416
regular expressions.
418
417
418
+ BigDecimal Fields
419
+ -----------------
420
+
421
+ The ``BigDecimal`` field type is used to store numbers with increased precision.
422
+
423
+ The ``BigDecimal`` field type stores its values in two different ways in the
424
+ database, depending on the value of the ``Mongoid.map_big_decimal_to_decimal128``
425
+ global config option. If this flag is set to false (which is the default),
426
+ the ``BigDecimal`` field will be stored as a string, otherwise it will be stored
427
+ as a ``BSON::Decimal128``.
428
+
429
+ The ``BigDecimal`` field type has some limitations when converting to and from
430
+ a ``BSON::Decimal128``:
431
+
432
+ - ``BSON::Decimal128`` has a limited range and precision, while ``BigDecimal``
433
+ has no restrictions in terms of range and precision. ``BSON::Decimal128`` has
434
+ a max value of approximately ``10^6145`` and a min value of approximately
435
+ ``-10^6145``, and has a maximum of 34 bits of precision. When attempting to
436
+ store values that don't fit into a ``BSON::Decimal128``, it is recommended to
437
+ have them stored as a string instead of a ``BSON::Decimal128``. You can do
438
+ that by setting ``Mongoid.map_big_decimal_to_decimal128`` to ``false``. If a
439
+ value that does not fit in a ``BSON::Decimal128`` is attempted to be stored
440
+ as one, an error will be raised.
441
+
442
+ - ``BSON::Decimal128`` is able to accept signed ``NaN`` values, while
443
+ ``BigDecimal`` is not. When retrieving signed ``NaN`` values from
444
+ the database using the ``BigDecimal`` field type, the ``NaN`` will be
445
+ unsigned.
446
+
447
+ - ``BSON::Decimal128`` maintains trailing zeroes when stored in the database.
448
+ ``BigDecimal``, however, does not maintain trailing zeroes, and therefore
449
+ retrieving ``BSON::Decimal128`` values using the ``BigDecimal`` field type
450
+ may result in a loss of precision.
451
+
452
+ There is an additional caveat when storing a ``BigDecimal`` in a field with no
453
+ type (i.e. a dynamically typed field) and ``Mongoid.map_big_decimal_to_decimal128``
454
+ is ``false``. In this case, the ``BigDecimal`` is stored as a string, and since a
455
+ dynamic field is being used, querying for that field with a ``BigDecimal`` will
456
+ not find the string for that ``BigDecimal``, since the query is looking for a
457
+ ``BigDecimal``. In order to query for that string, the ``BigDecimal`` must
458
+ first be converted to a string with ``to_s``. Note that this is not a problem
459
+ when the field has type ``BigDecimal``.
460
+
461
+ If you wish to avoid using ``BigDecimal`` altogether, you can set the field
462
+ type to ``BSON::Decimal128``. This will allow you to keep track of trailing
463
+ zeroes and signed ``NaN`` values.
464
+
465
+ Migration to ``decimal128``-backed ``BigDecimal`` Field
466
+ ```````````````````````````````````````````````````````
467
+ In a future major version of Mongoid, the ``Mongoid.map_big_decimal_to_decimal128``
468
+ global config option will be defaulted to ``true``. When this flag is turned on,
469
+ ``BigDecimal`` values in queries will not match to the strings that are already
470
+ stored in the database; they will only match to ``decimal128`` values that are
471
+ in the database. If you have a ``BigDecimal`` field that is backed by strings,
472
+ you have three options:
473
+
474
+ 1. The ``Mongoid.map_big_decimal_to_decimal128`` global config option can be
475
+ set to ``false``, and you can continue storing your ``BigDecimal`` values as
476
+ strings. Note that you are surrendering the advantages of storing ``BigDecimal``
477
+ values as a ``decimal128``, like being able to do queries and aggregations
478
+ based on the numerical value of the field.
479
+
480
+ 2. The ``Mongoid.map_big_decimal_to_decimal128`` global config option can be
481
+ set to ``true``, and you can convert all values for that field from strings to
482
+ ``decimal128`` values in the database. You should do this conversion before
483
+ setting the global config option to true. An example query to accomplish this
484
+ is as follows:
485
+
486
+ .. code-block:: javascript
487
+
488
+ db.bands.updateMany({
489
+ "field": { "$exists": true }
490
+ }, [
491
+ {
492
+ "$set": {
493
+ "field": { "$toDecimal": "$field" }
494
+ }
495
+ }
496
+ ])
497
+
498
+ This query updates all documents that have the given field, setting that
499
+ field to its corresponding ``decimal128`` value. Note that this query only
500
+ works in MongoDB 4.2+.
501
+
502
+ 3. The ``Mongoid.map_big_decimal_to_decimal128`` global config option can be
503
+ set to ``true``, and you can have both strings and ``decimal128`` values for
504
+ that field. This way, only ``decimal128`` values will be inserted into and
505
+ updated to the database going forward. Note that you still don't get the
506
+ full advantages of using only ``decimal128`` values, but your dataset is
507
+ slowly migrating to all ``decimal128`` values, as old string values are
508
+ updated to ``decimal128`` and new ``decimal128`` values are added. With this
509
+ setup, you can still query for ``BigDecimal`` values as follows:
510
+
511
+ .. code-block:: ruby
512
+
513
+ Mongoid.map_big_decimal_to_decimal128 = true
514
+ big_decimal = BigDecimal('2E9')
515
+ Band.in(sales: [big_decimal, big_decimal.to_s]).to_a
516
+
517
+ This query will find all values that are either a ``decimal128`` value or
518
+ a string that match that value.
419
519
420
520
.. _field-default-values:
421
521
@@ -524,17 +624,17 @@ from the aliased field:
524
624
525
625
class Band
526
626
include Mongoid::Document
527
-
627
+
528
628
field :name, type: String
529
629
alias_attribute :n, :name
530
630
end
531
-
631
+
532
632
band = Band.new(n: 'Astral Projection')
533
633
# => #<Band _id: 5fc1c1ee2c97a64accbeb5e1, name: "Astral Projection">
534
-
634
+
535
635
band.attributes
536
636
# => {"_id"=>BSON::ObjectId('5fc1c1ee2c97a64accbeb5e1'), "name"=>"Astral Projection"}
537
-
637
+
538
638
band.n
539
639
# => "Astral Projection"
540
640
@@ -559,11 +659,11 @@ This is useful for storing different values in ``id`` and ``_id`` fields:
559
659
560
660
class Band
561
661
include Mongoid::Document
562
-
662
+
563
663
unalias_attribute :id
564
664
field :id, type: String
565
665
end
566
-
666
+
567
667
Band.new(id: '42')
568
668
# => #<Band _id: 5fc1c3f42c97a6590684046c, id: "42">
569
669
@@ -691,19 +791,19 @@ getter as follows:
691
791
692
792
class DistanceMeasurement
693
793
include Mongoid::Document
694
-
794
+
695
795
field :value, type: Float
696
796
field :unit, type: String
697
-
797
+
698
798
def unit
699
799
read_attribute(:unit) || "m"
700
800
end
701
-
801
+
702
802
def to_s
703
803
"#{value} #{unit}"
704
804
end
705
805
end
706
-
806
+
707
807
measurement = DistanceMeasurement.new(value: 2)
708
808
measurement.to_s
709
809
# => "2.0 m"
@@ -717,18 +817,18 @@ may be implemented as follows:
717
817
718
818
class DistanceMeasurement
719
819
include Mongoid::Document
720
-
820
+
721
821
field :value, type: Float
722
822
field :unit, type: String
723
-
823
+
724
824
def unit=(value)
725
825
if value.blank?
726
826
value = nil
727
827
end
728
828
write_attribute(:unit, value)
729
829
end
730
830
end
731
-
831
+
732
832
measurement = DistanceMeasurement.new(value: 2, unit: "")
733
833
measurement.attributes
734
834
# => {"_id"=>BSON::ObjectId('613fa15aa15d5d617216104c'), "value"=>2.0, "unit"=>nil}
0 commit comments