Skip to content
Closed
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ The permitted SchemaTypes are:
* [Decimal128](api/mongoose.html#mongoose_Mongoose-Decimal128)
* [Map](schematypes.html#maps)
* [UUID](schematypes.html#uuid)
* [Int32](schematypes.html#int32)

Read more about [SchemaTypes here](schematypes.html).

Expand Down
40 changes: 40 additions & 0 deletions docs/schematypes.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ Check out [Mongoose's plugins search](http://plugins.mongoosejs.io) to find plug
* [Schema](#schemas)
* [UUID](#uuid)
* [BigInt](#bigint)
* [Int32](#int32)

### Example

Expand All @@ -68,6 +69,7 @@ const schema = new Schema({
mixed: Schema.Types.Mixed,
_someId: Schema.Types.ObjectId,
decimal: Schema.Types.Decimal128,
int32bit: Schema.Types.Int32,
array: [],
ofString: [String],
ofNumber: [Number],
Expand Down Expand Up @@ -647,6 +649,44 @@ const question = new Question({ answer: 42n });
typeof question.answer; // 'bigint'
```

### Int32 {#int32}

Mongoose supports 32-bit integers as a SchemaType.
Int32s are stored as [32-bit integers in MongoDB (BSON type "int")](https://www.mongodb.com/docs/manual/reference/bson-types/).

```javascript
const studentsSchema = new Schema({
id: Int32
});
const Student = mongoose.model('Student', schema);

const student = new Student({ id: 1339 });
typeof student.id; // 'number'
```

There are several types of values that will be successfully cast to a Number.

```javascript
new Student({ id: '15' }).id; // 15 as a Int32
new Student({ id: true }).id; // 1 as a Int32
new Student({ id: false }).id; // 0 as a Int32
new Student({ id: { valueOf: () => 83 } }).id; // 83 as a Int32
new Student({ id: '' }).id; // null as a Int32
```

If you pass an object with a `valueOf()` function that returns a Number, Mongoose will
call it and assign the returned value to the path.

The values `null` and `undefined` are not cast.

The following inputs will result will all result in a [CastError](validation.html#cast-errors) once validated, meaning that it will not throw on initialization, only when validated:

* NaN
* strings that cast to NaN
* objects that don't have a `valueOf()` function
* a decimal that must be rounded to be an integer
* an input that represents a value outside the bounds of an 32-bit integer

## Getters {#getters}

Getters are like virtuals for paths defined in your schema. For example,
Expand Down
1 change: 1 addition & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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

module.exports.Error = mongoose.Error;
module.exports.MongooseError = mongoose.MongooseError;
module.exports.now = mongoose.now;
Expand Down
43 changes: 43 additions & 0 deletions lib/cast/int32.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
'use strict';

const assert = require('assert');
const BSON = require('bson');

/**
* Given a value, cast it to a Int32, or throw an `Error` if the value
* cannot be casted. `null` and `undefined` are considered valid.
*
* @param {Any} value
* @return {Number}
* @throws {Error} if `value` does not represent an integer, or is outside the bounds of an 32-bit integer.
* @api private
*/

module.exports = function castInt32(val) {
if (val == null) {
return val;
}
if (val === '') {
return null;
}

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!

coercedVal = val.value;
} else if (val instanceof BSON.Long) {
coercedVal = val.toNumber();
} else {
coercedVal = Number(val);
}

const INT32_MAX = 0x7FFFFFFF;
const INT32_MIN = -0x80000000;

if (coercedVal === (coercedVal | 0) &&
coercedVal >= INT32_MIN &&
coercedVal <= INT32_MAX
) {
return coercedVal;
}
assert.ok(false);
};
17 changes: 17 additions & 0 deletions lib/mongoose.js
Original file line number Diff line number Diff line change
Expand Up @@ -987,6 +987,7 @@ Mongoose.prototype.VirtualType = VirtualType;
* - [ObjectId](https://mongoosejs.com/docs/schematypes.html#objectids)
* - [Map](https://mongoosejs.com/docs/schematypes.html#maps)
* - [Subdocument](https://mongoosejs.com/docs/schematypes.html#schemas)
* - [Int32](https://mongoosejs.com/docs/schematypes.html#int32)
*
* Using this exposed access to the `ObjectId` type, we can construct ids on demand.
*
Expand Down Expand Up @@ -1138,6 +1139,22 @@ Mongoose.prototype.syncIndexes = function(options) {

Mongoose.prototype.Decimal128 = SchemaTypes.Decimal128;


/**
* The Mongoose Int32 [SchemaType](https://mongoosejs.com/docs/schematypes.html). Used for
* declaring paths in your schema that should be 32-bit integers.
* Do not use this to create a new Int32 instance, use `mongoose.Types.Int32`
* instead.
*
* #### Example:
*
* const vehicleSchema = new Schema({ numTires: mongoose.Int32 });
*
* @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

Mongoose.prototype.Int32 = SchemaTypes.Int32;

/**
* The Mongoose Mixed [SchemaType](https://mongoosejs.com/docs/schematypes.html). Used for
* declaring paths in your schema that Mongoose's change tracking, casting,
Expand Down
1 change: 1 addition & 0 deletions lib/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -2848,6 +2848,7 @@ module.exports = exports = Schema;
* - [Mixed](https://mongoosejs.com/docs/schematypes.html#mixed)
* - [UUID](https://mongoosejs.com/docs/schematypes.html#uuid)
* - [BigInt](https://mongoosejs.com/docs/schematypes.html#bigint)
* - [Int32](https://mongoosejs.com/docs/schematypes.html#int32)
*
* Using this exposed access to the `Mixed` SchemaType, we can use them in our schema.
*
Expand Down
4 changes: 2 additions & 2 deletions lib/schema/bigint.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,12 @@ SchemaBigInt.setters = [];
SchemaBigInt.get = SchemaType.get;

/**
* Get/set the function used to cast arbitrary values to booleans.
* Get/set the function used to cast arbitrary values to bigints.
*
* #### Example:
*
* // Make Mongoose cast empty string '' to false.
* const original = mongoose.Schema.BigInt.cast();
* const original = mongoose.Schema.Types.BigInt.cast();
* mongoose.Schema.BigInt.cast(v => {
* if (v === '') {
* return false;
Expand Down
1 change: 1 addition & 0 deletions lib/schema/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ exports.ObjectId = require('./objectId');
exports.String = require('./string');
exports.Subdocument = require('./subdocument');
exports.UUID = require('./uuid');
exports.Int32 = require('./int32');

// alias

Expand Down
Loading
Loading