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