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

feat(NODE-6503): Add Int32 as a SchemaType #1

Closed
wants to merge 17 commits into from

Conversation

baileympearson
Copy link

@baileympearson baileympearson commented Nov 7, 2024

Note: @aditi-khare-mongoDB is the implementer, just a clarification in case it seems confusing why @baileympearson is leaving review comments.

Summary

Motivation
Support a 32-bit integer type in mongoose for compatibility with CSFLE/QE. The current Number schema type can result in either Int32, Int64, or Doubles that are inserted into the database using type inference but CSFLE and QE require exact BSON types in schemas.

Summary of Changes

  • Add SchemaType Int32, which has the following behavior:
    • its default cast method throws when provided a
      • a non-numeric string
      • a non-integer value
      • a NaN
      • a number beyond the bounds of INT32_MAX or INT32_MIN
    • it is queryable as $type: int or $type: number, but NOT $type: double
  • Add API Docs for new query option and new schema type

Example

const vehicleSchema = new Schema({ numberOfTires: mongoose.Int32 });

@aditi-khare-mongoDB aditi-khare-mongoDB changed the title initial commit feat(NODE-6503): Add Int32 to SchemaType Nov 7, 2024
@aditi-khare-mongoDB aditi-khare-mongoDB changed the title feat(NODE-6503): Add Int32 to SchemaType feat(NODE-6503): Add Int32 as a SchemaType Nov 8, 2024
Copy link
Author

@baileympearson baileympearson left a comment

Choose a reason for hiding this comment

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

besides removing the code from lib/types, only minor comments. Lint is failing too, btw

(also can't request changes)

const INT32_MIN = -0x80000000;

if (v != null) {
if (typeof v !== 'number') {
Copy link
Author

Choose a reason for hiding this comment

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

do you think we should also confirm that it is an integer here?

Choose a reason for hiding this comment

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

I'm not sure - I wonder if that would make it too similar to the other cast function

Choose a reason for hiding this comment

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

We can leave a comment open for when we open this for Val

Copy link
Member

Choose a reason for hiding this comment

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

Yes, this function should check for integer.

The idea behind _defaultCaster() is that _defaultCaster() is the function Mongoose will use if casting is disabled using cast: false, so it should skip any type coercions (like converting strings or objects to int32).

@@ -14,6 +14,7 @@ exports.Embedded = require('./arraySubdocument');
exports.DocumentArray = require('./documentArray');
exports.Decimal128 = require('./decimal128');
exports.ObjectId = require('./objectid');
exports.Int32 = require('./int32');
Copy link
Author

Choose a reason for hiding this comment

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

No - but mongoose has specific wrappers for them in lib/types. see Automattic#1671

Copy link
Author

@baileympearson baileympearson left a comment

Choose a reason for hiding this comment

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

just one minor comment

lib/mongoose.js Outdated
*
* @property Int32
* @api public
*/
Copy link
Author

Choose a reason for hiding this comment

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

Do we need to store a reference to our schema on the Mongoose object? My thought is no becase the majority of schema types do not. Can we remove this? (and the TS changes you made as well)?

Choose a reason for hiding this comment

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

Removed them - will add back in depending on discussion with Val

index.js Outdated
@@ -46,6 +46,7 @@ module.exports.Decimal128 = mongoose.Decimal128;
module.exports.Mixed = mongoose.Mixed;
module.exports.Date = mongoose.Date;
module.exports.Number = mongoose.Number;
module.exports.Int32 = mongoose.Int32;
Copy link
Author

Choose a reason for hiding this comment

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

Along with my comment in mongoose.js, i think this should be removed

Copy link
Member

Choose a reason for hiding this comment

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

Why do you think this should be removed?

Copy link
Author

Choose a reason for hiding this comment

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

Not all schema types are exported as top-level exports, so I assumed that the exported schema types are exported for a reason. Is that incorrect?

Choose a reason for hiding this comment

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

I've removed the schema type top level export for Int32 but i can add it back in case this is incorrect behavior

}

let coercedVal;
if (val instanceof BSON.Int32 || val instanceof BSON.Double) {

Choose a reason for hiding this comment

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

we check for _bsontype because that lines up with how the serializer interprets our BSON types. instanceof is a different contract than our code expects so I think it shouldn't be introduced here so we don't create two versions of what is a true "type check"

Copy link
Member

Choose a reason for hiding this comment

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

I agree with using _bsontype rather than instanceof. Mongoose has an isBsonType() helper for this purpose.

The reason why we use _bsontype over instanceof is that it is common for Node.js apps to have multiple versions of bson floating around, and relying on instanceof can lead to hard-to-debug behavior when we get an Int32 instance from a different version of bson module

Choose a reason for hiding this comment

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

made the change!

});

describe('supports the required property', function() {
it('when vaglue is null', async function() {
Copy link
Member

Choose a reason for hiding this comment

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

Minor typo value -> vaglue

Choose a reason for hiding this comment

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

Made the change, another good catch!

const Test = mongoose.model('Test', schema);

const doc = new Test({
myInt: -42
Copy link
Member

Choose a reason for hiding this comment

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

If I'm reading this test correctly, myInt should be a string here

Choose a reason for hiding this comment

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

Good catch!

Copy link
Member

@vkarpov15 vkarpov15 left a comment

Choose a reason for hiding this comment

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

I had a couple of minor comments, but overall this looks good, no major issues

return null;
}

const coercedVal = val instanceof BSON.Long ? val.toNumber() : Number(val);

Choose a reason for hiding this comment

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

#1 (comment)

Shouldn't be using instanceof here per convo ^

Choose a reason for hiding this comment

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

My bad! Fixed now!

Choose a reason for hiding this comment

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

Per Val's comment, I think it is recommended to use the isBsonType() helper in mongoose

Choose a reason for hiding this comment

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

Oh I see! I'll change it again.

return null;
}

const coercedVal = val instanceof BSON.Long ? val.toNumber() : Number(val);

Choose a reason for hiding this comment

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

Per Val's comment, I think it is recommended to use the isBsonType() helper in mongoose

Copy link
Author

@baileympearson baileympearson left a comment

Choose a reason for hiding this comment

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

I can't approve because I opened the PR but LGTM

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.

4 participants