Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MONGOID-5212 Support for Decimal128 type #5125

Merged
merged 15 commits into from
Jan 17, 2022

Conversation

Neilshweky
Copy link
Contributor

@Neilshweky Neilshweky commented Jan 6, 2022

No description provided.

@Neilshweky Neilshweky changed the title MONGOID-4237 Squashed commit of the following: MONGOID-4237 Support for Decimal128 type Jan 6, 2022
@Neilshweky Neilshweky force-pushed the MONGOID-4237 branch 2 times, most recently from 1d69224 to bf73d24 Compare January 6, 2022 15:29
@Neilshweky Neilshweky changed the title MONGOID-4237 Support for Decimal128 type MONGOID-4237 MONGOID-5212 Support for Decimal128 type Jan 6, 2022
lib/mongoid/config.rb Show resolved Hide resolved
if object
if object.is_a?(BSON::Decimal128)
object.to_big_decimal
elsif object.numeric?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like the numeric? condition to be dropped and this path be taken for non-numeric input also. I see that non-numeric input was already trashed previously but I think this is not good behavior.

Test with a time field:

  class Foo
    include Mongoid::Document

    field :a
  end

  Foo.delete_all

  Foo.create!(a: 'zz')

  class Foo
    field :a, type: BigDecimal
  end

  p Foo.first, Foo.first.a

produces:

NoMethodError: undefined method `getlocal' for "zz":String

Test with the code as proposed with BigDecimal field:

  class Foo
    include Mongoid::Document

    field :a
  end

  Foo.delete_all

  Foo.create!(a: 'zz')

  class Foo
    field :a, type: Time
  end

  p Foo.first, Foo.first.a

produces:

#<Foo _id: 61d76a95a15d5d4e9c038186, a: "zz">
nil

I think it would be better to always attempt to construct a BigDecimal, allowing that call to raise whatever exceptions on problems.

@comandeo thoughts?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I disagree. I think demongoize should return nil unless the object can be represented as a BigDecimal. Should check the behavior of demongoize on other classes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@p-mongo I kind of agree with @johnnyshields here... Here are some examples of invalid values to demongoize:

irb(main):004:0> Float.demongoize("1.2asda")
=> 0.0
irb(main):005:0> Integer.demongoize("1234rqwdd")
=> 0

Now, I'm not sure why 0 is the right return value here but the important thing is that it doesn't raise an error. Let me know what you think...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Neilshweky is correct should make this return 0. Important thing is that it doesn't raise error.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually after rereading your comment, I agree @p-mongo. Let's let BigDecimal throw an error

Copy link
Contributor

@johnnyshields johnnyshields Jan 8, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the two existing numeric types (Float and Integer) currently return 0, I would be inclined to say BigDecimal should return 0. Fine to raise a follow-up ticket/PR if we wish to change it later.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think for now it's better practice to return nil, and we should file a ticket to follow upon Float and Integer.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created https://jira.mongodb.org/browse/MONGOID-5221 to address the demongoization issue across all types.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am unresolving this discussion thread because it's referenced from https://jira.mongodb.org/browse/MONGOID-5221.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've crossed out my earlier comment here--BigDecimal correctly returns nil today for non-numeric string, we should do the same for Integer/Float. It's ready to merge in PR: #5220

lib/mongoid/extensions/big_decimal.rb Outdated Show resolved Hide resolved
lib/mongoid/extensions/big_decimal.rb Outdated Show resolved Hide resolved
lib/mongoid/extensions/big_decimal.rb Show resolved Hide resolved
lib/mongoid/extensions/big_decimal.rb Show resolved Hide resolved
lib/mongoid/extensions/big_decimal.rb Show resolved Hide resolved
spec/mongoid/extensions/big_decimal_spec.rb Outdated Show resolved Hide resolved
spec/mongoid/fields_spec.rb Show resolved Hide resolved
@Neilshweky
Copy link
Contributor Author

The tests in this PR will not work until BSON is upgraded to 4.14.0

end

let(:from_db) do
Band.where(sales: sales).first
context 'when map_big_decimal_to_decimal128 was false and is now true' do
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test does not work. It asserts that a BigDecimal that was originally stored as a string, can be queried with a BigDecimal, with map_big_decimal_to_decimal128 turned on. We have to fix this. Here are the solutions I can think of:

  1. Change the implementation of searching for a BigDecimal field to search for both the string representation and the BigDecimal representation.
    A problem arises when some people purposely have BigDecimals and Strings stored in the database under the same field, and don't want querying for a BigDecimal to return the string. But I think this is a pretty niche case.
  2. We can just leave it how it is. Instruct the user how to query for both string and BigDecimal representations of their data if that's what they want. This is a little annoying for the user.
  3. I also wrote up this quick little query that changes all values in a db to a BigDecimal but this seems a little reckless, especially for big data sets:
db.bands.updateMany({ 
  "field": { "$exists": true } 
}, [
  { "$set": { 
    "field": { "$toDecimal": "$field" } 
  } }
])

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The first idea would be a really good one but I expect it to be either difficult or perhaps not even possible.

There are two contexts where attribute values are used: implicit equality (e.g. {field: 42}) and operators (e.g. {field: {$eq: 42}}). While the implicit equality can be transformed to e.g. {field: {$in: ['42', Decimal128('42')]}}, this won't work in the operator context.

mongoize currently doesn't have context knowledge at all. There is probably a way to communicate some contexts to it but Mongoid does generally permit users to provide MQL in a bunch of places and I'm thinking those might be impossible to change programmatically short of doing much more rewriting of MQL than Mongoid is presently doing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So does that leave us with option 2?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think at this point what we need is migration guidance and documentation for how to move from string to bigdecimal field storage. The migration query above is excellent and can go into the migration guidance. Additionally it would be good to have a similar snippet for how to query on individual fields with type conversion into bigdecimal, e.g. if I want to find all documents where the value is > 0 with both types how I would do that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay! I can't put that all into the documentation PR.... That leaves us with no further code changes then.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test then can be changed to assert that the document is not found when its type in the database is different from how Mongoid is presently configured, for our future reference.

Copy link
Contributor

@p-mongo p-mongo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, I'll leave this unapproved as a reminder that this PR is blocked on bson release.

end

let(:from_db) do
Band.where(sales: sales).first
context 'when map_big_decimal_to_decimal128 was false and is now true' do
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test then can be changed to assert that the document is not found when its type in the database is different from how Mongoid is presently configured, for our future reference.

@Neilshweky Neilshweky changed the title MONGOID-4237 MONGOID-5212 Support for Decimal128 type MONGOID-5212 Support for Decimal128 type Jan 13, 2022
estolfo and others added 13 commits January 14, 2022 13:09
commit dd5c3f0
Author: Emily <emily@mongodb.com>
Date:   Fri Dec 16 16:21:49 2016 +0100

    MONGOID-4237 Warn about loss in precision when converting from BSON::Decimal128 to BigDecimal

commit 1ac03bf
Author: Emily <emily@mongodb.com>
Date:   Fri Dec 16 10:02:26 2016 +0100

    MONGOID-4237 Clearer logic using helper methods in BigDecimal extension

commit 59476b9
Author: Emily <emily@mongodb.com>
Date:   Thu Dec 15 17:03:43 2016 +0100

    MONGOID-4237 Test all possible BigDecimal and BSON::Decimal128 scenarios when (de)mongoizing

commit 6a58fe6
Author: Emily <emily@mongodb.com>
Date:   Wed Dec 14 18:20:40 2016 +0100

    MONGOID-4237 Rename config option to map_big_decimal_to_decimal128

commit c3594de
Author: Emily <emily@mongodb.com>
Date:   Wed Dec 14 16:13:39 2016 +0100

    MONGOID-4237 Evolve BigDecimal types to Decimal128s

commit 43f0f3f
Author: Emily <emily@mongodb.com>
Date:   Wed Dec 14 16:11:03 2016 +0100

    MONGOID-4237 Add serialize_big_decimal_to_decimal128 to mongoid config
@johnnyshields
Copy link
Contributor

Looks good to me

@Neilshweky Neilshweky merged commit 19494e0 into mongodb:master Jan 17, 2022
p-mongo pushed a commit to johnnyshields/mongoid that referenced this pull request Jan 21, 2022
* master:
  MONGOID-5098 Bug fix: Range.mongoize should mongoize its members (mongodb#5108)
  MONGOID-5212 Support for Decimal128 type (mongodb#5125)
  MONGOID-5213 Document changes to BigDecimal type and addition of global flag (mongodb#5126)
  MONGOID-5209 Document "Custom Field Options" functionality + various docs improvements (mongodb#5120)
  MONGOID-5193 rails 7 support  (mongodb#5122)
  Fix default topology name - should be standalone (mongodb#5130)
  Update MRSS (mongodb#5129)
  MONGOID-5207 Test Ruby 3.1 on Evergreen (mongodb#5123)
  MONGOID-5186 .with_scope should restore previous scope (mongodb#5127)
  MONGOID-5161 - [Documentation Only] Clarify the policy for working with . and $ named fields which were introduced in MongoDB 5.0 (mongodb#5051)
p-mongo pushed a commit to johnnyshields/mongoid that referenced this pull request Jan 21, 2022
* master: (48 commits)
  MONGOID-5098 Bug fix: Range.mongoize should mongoize its members (mongodb#5108)
  MONGOID-5212 Support for Decimal128 type (mongodb#5125)
  MONGOID-5213 Document changes to BigDecimal type and addition of global flag (mongodb#5126)
  MONGOID-5209 Document "Custom Field Options" functionality + various docs improvements (mongodb#5120)
  MONGOID-5193 rails 7 support  (mongodb#5122)
  Fix default topology name - should be standalone (mongodb#5130)
  Update MRSS (mongodb#5129)
  MONGOID-5207 Test Ruby 3.1 on Evergreen (mongodb#5123)
  MONGOID-5186 .with_scope should restore previous scope (mongodb#5127)
  MONGOID-5161 - [Documentation Only] Clarify the policy for working with . and $ named fields which were introduced in MongoDB 5.0 (mongodb#5051)
  MONGOID-5173 Specs should use bang (!) methods (without describe/context change) (mongodb#5109)
  Fix doc syntax
  RUBY-2783 Require bson 4-stable for Mongoid as bson master is 5.0 and not compatible with driver (mongodb#5113)
  MONGOID-5208 fix error on reloading nil embedded document (mongodb#5116)
  MONGOID-5206 fix bug where embedded document is not re-embedded (mongodb#5115)
  MONGOID-5207 Add Ruby 3.1 to GH Actions (mongodb#5114)
  MONGOID-5207 Use YAML.safe_load in specs (mongodb#5112)
  MONGOID-5199 Reloadable#reload_embedded_document doesn't respect session (mongodb#5105)
  Fix MONGOID-5198 Calling children_changed? on a deep cyclical data structure will cause semi-infinite looping (mongodb#5104)
  MONGOID-5203 Add all available auth_mech options to Configuration documentation (mongodb#5103)
  ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants