From 11725e086dbaea4933557b1a2e548c52f2633e42 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Sun, 23 Apr 2023 17:12:50 -0400 Subject: [PATCH 1/9] feat(schema): add BigInt support, upgrade mongodb -> 5.3.0 --- lib/cast/bigint.js | 31 +++++++ lib/schema/bigint.js | 205 +++++++++++++++++++++++++++++++++++++++++++ lib/schema/index.js | 27 ++---- package.json | 4 +- test/bigint.test.js | 104 ++++++++++++++++++++++ types/query.d.ts | 1 + 6 files changed, 351 insertions(+), 21 deletions(-) create mode 100644 lib/cast/bigint.js create mode 100644 lib/schema/bigint.js create mode 100644 test/bigint.test.js diff --git a/lib/cast/bigint.js b/lib/cast/bigint.js new file mode 100644 index 00000000000..7191311d647 --- /dev/null +++ b/lib/cast/bigint.js @@ -0,0 +1,31 @@ +'use strict'; + +const assert = require('assert'); + +/** + * Given a value, cast it to a BigInt, 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` is not one of the allowed values + * @api private + */ + +module.exports = function castBigInt(val) { + if (val == null) { + return val; + } + if (val === '') { + return null; + } + if (typeof val === 'bigint') { + return val; + } + + if (typeof val === 'string' || typeof val === 'number') { + return BigInt(val); + } + + assert.ok(false); +}; diff --git a/lib/schema/bigint.js b/lib/schema/bigint.js new file mode 100644 index 00000000000..846fe6e9bf6 --- /dev/null +++ b/lib/schema/bigint.js @@ -0,0 +1,205 @@ +'use strict'; + +/*! + * Module dependencies. + */ + +const CastError = require('../error/cast'); +const SchemaType = require('../schematype'); +const castBigInt = require('../cast/bigint'); +const utils = require('../utils'); + +/** + * BigInt SchemaType constructor. + * + * @param {String} path + * @param {Object} options + * @inherits SchemaType + * @api public + */ + +function SchemaBigInt(path, options) { + SchemaType.call(this, path, options, 'BigInt'); +} + +/** + * This schema type's name, to defend against minifiers that mangle + * function names. + * + * @api public + */ + SchemaBigInt.schemaName = 'BigInt'; + + SchemaBigInt.defaultOptions = {}; + +/*! + * Inherits from SchemaType. + */ +SchemaBigInt.prototype = Object.create(SchemaType.prototype); +SchemaBigInt.prototype.constructor = SchemaBigInt; + +/*! + * ignore + */ + +SchemaBigInt._cast = castBigInt; + +/** + * Sets a default option for all BigInt instances. + * + * #### Example: + * + * // Make all bigints required by default + * mongoose.Schema.BigInt.set('required', true); + * + * @param {String} option The option you'd like to set the value for + * @param {Any} value value for option + * @return {undefined} + * @function set + * @static + * @api public + */ + +SchemaBigInt.set = SchemaType.set; + +/** + * Get/set the function used to cast arbitrary values to booleans. + * + * #### Example: + * + * // Make Mongoose cast empty string '' to false. + * const original = mongoose.Schema.Boolean.cast(); + * mongoose.Schema.Boolean.cast(v => { + * if (v === '') { + * return false; + * } + * return original(v); + * }); + * + * // Or disable casting entirely + * mongoose.Schema.Boolean.cast(false); + * + * @param {Function} caster + * @return {Function} + * @function get + * @static + * @api public + */ + +SchemaBigInt.cast = function cast(caster) { + if (arguments.length === 0) { + return this._cast; + } + if (caster === false) { + caster = this._defaultCaster; + } + this._cast = caster; + + return this._cast; +}; + +/*! + * ignore + */ + +SchemaBigInt._checkRequired = v => v != null; + +/** + * Override the function the required validator uses to check whether a value + * passes the `required` check. + * + * @param {Function} fn + * @return {Function} + * @function checkRequired + * @static + * @api public + */ + +SchemaBigInt.checkRequired = SchemaBigInt.checkRequired; + +/** + * Check if the given value satisfies a required validator. + * + * @param {Any} value + * @return {Boolean} + * @api public + */ + + SchemaBigInt.prototype.checkRequired = function(value) { + return this.constructor._checkRequired(value); +}; + +/** + * Casts to bigint + * + * @param {Object} value + * @param {Object} model this value is optional + * @api private + */ + +SchemaBigInt.prototype.cast = function(value) { + let castBigInt; + if (typeof this._castFunction === 'function') { + castBigInt = this._castFunction; + } else if (typeof this.constructor.cast === 'function') { + castBigInt = this.constructor.cast(); + } else { + castBigInt = SchemaBoolean.cast(); + } + + try { + return castBigInt(value); + } catch (error) { + throw new CastError('BigInt', value, this.path, error, this); + } +}; + +SchemaBigInt.$conditionalHandlers = + utils.options(SchemaType.prototype.$conditionalHandlers, {}); + +/** + * Casts contents for queries. + * + * @param {String} $conditional + * @param {any} val + * @api private + */ + + SchemaBigInt.prototype.castForQuery = function($conditional, val, context) { + let handler; + if ($conditional != null) { + handler = SchemaBigInt.$conditionalHandlers[$conditional]; + + if (handler) { + return handler.call(this, val); + } + + return this.applySetters(null, val, context); + } + + return this.applySetters(val, context); +}; + +/** + * + * @api private + */ + +SchemaBigInt.prototype._castNullish = function _castNullish(v) { + if (typeof v === 'undefined') { + return v; + } + const castBigInt = typeof this.constructor.cast === 'function' ? + this.constructor.cast() : + SchemaBigInt.cast(); + if (castBigInt == null) { + return v; + } + return v; +}; + +/*! + * Module exports. + */ + +module.exports = SchemaBigInt; diff --git a/lib/schema/index.js b/lib/schema/index.js index f3eb9851ea6..4bd4d8d5934 100644 --- a/lib/schema/index.js +++ b/lib/schema/index.js @@ -5,30 +5,19 @@ 'use strict'; -exports.String = require('./string'); - -exports.Number = require('./number'); - -exports.Boolean = require('./boolean'); - -exports.DocumentArray = require('./documentarray'); - -exports.Subdocument = require('./SubdocumentPath'); - exports.Array = require('./array'); - +exports.Boolean = require('./boolean'); +exports.BigInt = require('./bigint'); exports.Buffer = require('./buffer'); - exports.Date = require('./date'); - -exports.ObjectId = require('./objectid'); - -exports.Mixed = require('./mixed'); - exports.Decimal128 = exports.Decimal = require('./decimal128'); - +exports.DocumentArray = require('./documentarray'); exports.Map = require('./map'); - +exports.Mixed = require('./mixed'); +exports.Number = require('./number'); +exports.ObjectId = require('./objectid'); +exports.String = require('./string'); +exports.Subdocument = require('./SubdocumentPath'); exports.UUID = require('./uuid'); // alias diff --git a/package.json b/package.json index 36f12f68544..b47482d756b 100644 --- a/package.json +++ b/package.json @@ -19,9 +19,9 @@ ], "license": "MIT", "dependencies": { - "bson": "^5.0.1", + "bson": "^5.2.0", "kareem": "2.5.1", - "mongodb": "5.1.0", + "mongodb": "5.3.0", "mpath": "0.9.0", "mquery": "5.0.0", "ms": "2.1.3", diff --git a/test/bigint.test.js b/test/bigint.test.js new file mode 100644 index 00000000000..23b5f4f053b --- /dev/null +++ b/test/bigint.test.js @@ -0,0 +1,104 @@ +'use strict'; + +const assert = require('assert'); +const start = require('./common'); + +const mongoose = start.mongoose; +const Schema = mongoose.Schema; + +describe('BigInt', function() { + beforeEach(() => mongoose.deleteModel(/Test/)); + + it('is a valid schema type', function() { + const schema = new Schema({ + myBigInt: BigInt + }); + const Test = mongoose.model('Test', schema); + + const doc = new Test({ + myBigInt: 42n + }); + assert.strictEqual(doc.myBigInt, 42n); + assert.equal(typeof doc.myBigInt, 'bigint'); + }); + + it('casting from strings and numbers', function() { + const schema = new Schema({ + bigint1: { + type: BigInt + }, + bigint2: 'BigInt' + }); + const Test = mongoose.model('Test', schema); + + const doc = new Test({ + bigint1: 42, + bigint2: '997' + }); + assert.strictEqual(doc.bigint1, 42n); + assert.strictEqual(doc.bigint2, 997n); + }); + + it('handles cast errors', async function() { + const schema = new Schema({ + bigint: 'BigInt' + }); + const Test = mongoose.model('Test', schema); + + const doc = new Test({ + bigint: 'foo bar' + }); + assert.strictEqual(doc.bigint, undefined); + + const err = await doc.validate().then(() => null, err => err); + assert.ok(err); + assert.ok(err.errors['bigint']); + assert.equal(err.errors['bigint'].name, 'CastError'); + assert.equal( + err.errors['bigint'].message, + 'Cast to BigInt failed for value "foo bar" (type string) at path "bigint" because of "SyntaxError"' + ); + }); + + describe('MongoDB integration', function() { + let db; + let Test; + + before(async function() { + db = await start(); + + const schema = new Schema({ + myBigInt: BigInt + }); + db.deleteModel(/Test/); + Test = db.model('Test', schema); + }); + + after(async function() { + await db.close(); + }); + + beforeEach(async () => { + await Test.deleteMany({}); + }); + + it('is stored as a long in MongoDB', async function() { + await Test.create({ myBigInt: 42n }); + + const doc = await Test.findOne({ myBigInt: { $type: 'long' } }); + assert.ok(doc); + assert.strictEqual(doc.myBigInt, 42n); + }); + + it('becomes a bigint with lean using useBigInt64', async function() { + await Test.create({ myBigInt: 7n }); + + const doc = await Test. + findOne({ myBigInt: 7n }). + setOptions({ useBigInt64: true }). + lean(); + assert.ok(doc); + assert.strictEqual(doc.myBigInt, 7n); + }); + }); +}); \ No newline at end of file diff --git a/types/query.d.ts b/types/query.d.ts index 15b973bbb4f..74e353b8f50 100644 --- a/types/query.d.ts +++ b/types/query.d.ts @@ -162,6 +162,7 @@ declare module 'mongoose' { */ timestamps?: boolean | QueryTimestampsConfig; upsert?: boolean; + useBigInt64?: boolean; writeConcern?: mongodb.WriteConcern; [other: string]: any; From 8b019826672087cfdeda0081ef4f4f65b47156fb Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Sun, 23 Apr 2023 17:22:34 -0400 Subject: [PATCH 2/9] chore: add ES2020 globals to eslint --- .eslintrc.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.eslintrc.js b/.eslintrc.js index 0b98da74134..1f021f897bf 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -116,7 +116,8 @@ module.exports = { }, env: { node: true, - es6: true + es6: true, + es2020: true }, rules: { 'comma-style': 'error', From 939b12019967423bbc80ccc1d0f90d11d388abde Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Sun, 23 Apr 2023 17:22:49 -0400 Subject: [PATCH 3/9] feat(schema): query casting for BigInt --- lib/schema/bigint.js | 32 ++++++++++++++++++++++++-------- test/bigint.test.js | 29 +++++++++++++++++++++++------ 2 files changed, 47 insertions(+), 14 deletions(-) diff --git a/lib/schema/bigint.js b/lib/schema/bigint.js index 846fe6e9bf6..218b113690e 100644 --- a/lib/schema/bigint.js +++ b/lib/schema/bigint.js @@ -28,9 +28,9 @@ function SchemaBigInt(path, options) { * * @api public */ - SchemaBigInt.schemaName = 'BigInt'; +SchemaBigInt.schemaName = 'BigInt'; - SchemaBigInt.defaultOptions = {}; +SchemaBigInt.defaultOptions = {}; /*! * Inherits from SchemaType. @@ -115,7 +115,7 @@ SchemaBigInt._checkRequired = v => v != null; * @api public */ -SchemaBigInt.checkRequired = SchemaBigInt.checkRequired; +SchemaBigInt.checkRequired = SchemaType.checkRequired; /** * Check if the given value satisfies a required validator. @@ -125,7 +125,7 @@ SchemaBigInt.checkRequired = SchemaBigInt.checkRequired; * @api public */ - SchemaBigInt.prototype.checkRequired = function(value) { +SchemaBigInt.prototype.checkRequired = function(value) { return this.constructor._checkRequired(value); }; @@ -144,7 +144,7 @@ SchemaBigInt.prototype.cast = function(value) { } else if (typeof this.constructor.cast === 'function') { castBigInt = this.constructor.cast(); } else { - castBigInt = SchemaBoolean.cast(); + castBigInt = SchemaBigInt.cast(); } try { @@ -154,8 +154,24 @@ SchemaBigInt.prototype.cast = function(value) { } }; -SchemaBigInt.$conditionalHandlers = - utils.options(SchemaType.prototype.$conditionalHandlers, {}); +/*! + * ignore + */ + +SchemaBigInt.$conditionalHandlers = utils.options(SchemaType.prototype.$conditionalHandlers, { + $gt: handleSingle, + $gte: handleSingle, + $lt: handleSingle, + $lte: handleSingle +}); + +/*! + * ignore + */ + +function handleSingle(val, context) { + return this.castForQuery(null, val, context); +} /** * Casts contents for queries. @@ -165,7 +181,7 @@ SchemaBigInt.$conditionalHandlers = * @api private */ - SchemaBigInt.prototype.castForQuery = function($conditional, val, context) { +SchemaBigInt.prototype.castForQuery = function($conditional, val, context) { let handler; if ($conditional != null) { handler = SchemaBigInt.$conditionalHandlers[$conditional]; diff --git a/test/bigint.test.js b/test/bigint.test.js index 23b5f4f053b..16a800566f7 100644 --- a/test/bigint.test.js +++ b/test/bigint.test.js @@ -49,7 +49,7 @@ describe('BigInt', function() { bigint: 'foo bar' }); assert.strictEqual(doc.bigint, undefined); - + const err = await doc.validate().then(() => null, err => err); assert.ok(err); assert.ok(err.errors['bigint']); @@ -78,21 +78,21 @@ describe('BigInt', function() { await db.close(); }); - beforeEach(async () => { + beforeEach(async() => { await Test.deleteMany({}); }); it('is stored as a long in MongoDB', async function() { await Test.create({ myBigInt: 42n }); - + const doc = await Test.findOne({ myBigInt: { $type: 'long' } }); assert.ok(doc); assert.strictEqual(doc.myBigInt, 42n); }); - it('becomes a bigint with lean using useBigInt64', async function() { + it('becomes a bigint with lean using useBigInt64', async function() { await Test.create({ myBigInt: 7n }); - + const doc = await Test. findOne({ myBigInt: 7n }). setOptions({ useBigInt64: true }). @@ -100,5 +100,22 @@ describe('BigInt', function() { assert.ok(doc); assert.strictEqual(doc.myBigInt, 7n); }); + + it('can query with comparison operators', async function() { + await Test.create([ + { myBigInt: 1n }, + { myBigInt: 2n }, + { myBigInt: 3n }, + { myBigInt: 4n } + ]); + + let docs = await Test.find({ myBigInt: { $gte: 3n } }).sort({ myBigInt: 1 }); + assert.equal(docs.length, 2); + assert.deepStrictEqual(docs.map(doc => doc.myBigInt), [3n, 4n]); + + docs = await Test.find({ myBigInt: { $lt: 3n } }).sort({ myBigInt: -1 }); + assert.equal(docs.length, 2); + assert.deepStrictEqual(docs.map(doc => doc.myBigInt), [2n, 1n]); + }); }); -}); \ No newline at end of file +}); From c6cc7421fea05c38bd088b2e5b0d0b901dc3168a Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Sun, 23 Apr 2023 21:29:33 -0400 Subject: [PATCH 4/9] test: add test coverage for populate() and required with bigint --- test/bigint.test.js | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/test/bigint.test.js b/test/bigint.test.js index 16a800566f7..7f192223c72 100644 --- a/test/bigint.test.js +++ b/test/bigint.test.js @@ -60,6 +60,29 @@ describe('BigInt', function() { ); }); + it('supports required', async function() { + const schema = new Schema({ + bigint: { + type: BigInt, + required: true + } + }); + const Test = mongoose.model('Test', schema); + + const doc = new Test({ + bigint: null + }); + + const err = await doc.validate().then(() => null, err => err); + assert.ok(err); + assert.ok(err.errors['bigint']); + assert.equal(err.errors['bigint'].name, 'ValidatorError'); + assert.equal( + err.errors['bigint'].message, + 'Path `bigint` is required.' + ); + }); + describe('MongoDB integration', function() { let db; let Test; @@ -117,5 +140,27 @@ describe('BigInt', function() { assert.equal(docs.length, 2); assert.deepStrictEqual(docs.map(doc => doc.myBigInt), [2n, 1n]); }); + + it('supports populate()', async function() { + const parentSchema = new Schema({ + child: { + type: BigInt, + ref: 'Child' + } + }); + const childSchema = new Schema({ + _id: BigInt, + name: String + }); + const Parent = db.model('Parent', parentSchema); + const Child = db.model('Child', childSchema); + + const { _id } = await Parent.create({ child: 42n }); + await Child.create({ _id: 42n, name: 'test-bigint-populate' }); + + const doc = await Parent.findById(_id).populate('child'); + assert.ok(doc); + assert.equal(doc.child.name, 'test-bigint-populate'); + }); }); }); From bead47b07a55fb8925ed109580f160685e0b0ea0 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Tue, 25 Apr 2023 12:32:40 -0400 Subject: [PATCH 5/9] Update lib/schema/bigint.js Co-authored-by: hasezoey --- lib/schema/bigint.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/schema/bigint.js b/lib/schema/bigint.js index 218b113690e..24cff4ea9ed 100644 --- a/lib/schema/bigint.js +++ b/lib/schema/bigint.js @@ -68,8 +68,8 @@ SchemaBigInt.set = SchemaType.set; * #### Example: * * // Make Mongoose cast empty string '' to false. - * const original = mongoose.Schema.Boolean.cast(); - * mongoose.Schema.Boolean.cast(v => { + * const original = mongoose.Schema.BigInt.cast(); + * mongoose.Schema.BigInt.cast(v => { * if (v === '') { * return false; * } @@ -77,7 +77,7 @@ SchemaBigInt.set = SchemaType.set; * }); * * // Or disable casting entirely - * mongoose.Schema.Boolean.cast(false); + * mongoose.Schema.BigInt.cast(false); * * @param {Function} caster * @return {Function} From ff3dcdfb26d54802712e3702829b7e8da871f1eb Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Tue, 25 Apr 2023 12:34:17 -0400 Subject: [PATCH 6/9] test: add additional assertion re: code review comments --- test/bigint.test.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/bigint.test.js b/test/bigint.test.js index 7f192223c72..e3d00418e2c 100644 --- a/test/bigint.test.js +++ b/test/bigint.test.js @@ -161,6 +161,7 @@ describe('BigInt', function() { const doc = await Parent.findById(_id).populate('child'); assert.ok(doc); assert.equal(doc.child.name, 'test-bigint-populate'); + assert.equal(doc.child._id, 42n); }); }); }); From ed05be586b25f442e83296dcf31308db53547c47 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Tue, 25 Apr 2023 12:45:55 -0400 Subject: [PATCH 7/9] docs: add useBigInt64 option to docs --- docs/tutorials/lean.md | 10 ++++++++++ lib/query.js | 3 ++- test/docs/lean.test.js | 30 ++++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/docs/tutorials/lean.md b/docs/tutorials/lean.md index 8edd3438ba7..baa830f8fe5 100644 --- a/docs/tutorials/lean.md +++ b/docs/tutorials/lean.md @@ -10,6 +10,7 @@ In this tutorial, you'll learn more about the tradeoffs of using `lean()`. * [Lean and Populate](#lean-and-populate) * [When to Use Lean](#when-to-use-lean) * [Plugins](#plugins) +* [BigInts](#bigints)

Using Lean

@@ -140,3 +141,12 @@ schema.virtual('lowercase', function() { this.get('name'); // Crashes because `this` is not a Mongoose document. }); ``` + +## BigInts + +By default, the MongoDB Node driver converts longs stored in MongoDB into JavaScript numbers, **not** [BigInts](https://thecodebarbarian.com/an-overview-of-bigint-in-node-js.html). +Set the `useBigInt64` option on your `lean()` queries to inflate longs into BigInts. + +```acquit +[require:Lean Tutorial.*bigint] +``` \ No newline at end of file diff --git a/lib/query.js b/lib/query.js index b860eda0f8c..90df5af9ea9 100644 --- a/lib/query.js +++ b/lib/query.js @@ -1525,12 +1525,13 @@ Query.prototype.getOptions = function() { * - [timestamps](https://mongoosejs.com/docs/guide.html#timestamps): If `timestamps` is set in the schema, set this option to `false` to skip timestamps for that particular update. Has no effect if `timestamps` is not enabled in the schema options. * - overwriteDiscriminatorKey: allow setting the discriminator key in the update. Will use the correct discriminator schema if the update changes the discriminator key. * - * The following options are only for `find()`, `findOne()`, `findById()`, `findOneAndUpdate()`, and `findByIdAndUpdate()`: + * The following options are only for `find()`, `findOne()`, `findById()`, `findOneAndUpdate()`, `findOneAndReplace()`, `findOneAndDelete()`, and `findByIdAndUpdate()`: * * - [lean](#query_Query-lean) * - [populate](/docs/populate.html) * - [projection](#query_Query-projection) * - sanitizeProjection + * - useBigInt64 * * The following options are only for all operations **except** `updateOne()`, `updateMany()`, `deleteOne()`, and `deleteMany()`: * diff --git a/test/docs/lean.test.js b/test/docs/lean.test.js index 51ca5458cff..372a703179f 100644 --- a/test/docs/lean.test.js +++ b/test/docs/lean.test.js @@ -203,4 +203,34 @@ describe('Lean Tutorial', function() { assert.equal(group.members[1].name, 'Kira Nerys'); // acquit:ignore:end }); + + it('bigint', async function() { + const Person = mongoose.model('Person', new mongoose.Schema({ + name: String, + age: BigInt + })); + // acquit:ignore:start + await Person.deleteMany({}); + // acquit:ignore:end + await Person.create({ name: 'Benjamin Sisko', age: 37n }); + + // By default, if you store a document with a BigInt property in MongoDB and you + // load the document with `lean()`, the BigInt property will be a number + let person = await Person.findOne({ name: 'Benjamin Sisko' }).lean(); + typeof person.age; // 'number' + // acquit:ignore:start + assert.equal(typeof person.age, 'number'); + assert.equal(person.age, 37); + // acquit:ignore:end + + // Set the `useBigInt64` option to opt in to converting MongoDB longs to BigInts. + person = await Person.findOne({ name: 'Benjamin Sisko' }). + setOptions({ useBigInt64: true }). + lean(); + typeof person.age; // 'bigint' + // acquit:ignore:start + assert.equal(typeof person.age, 'bigint'); + assert.equal(person.age, 37n); + // acquit:ignore:end + }); }); From 01554e5c034d7a97732fa728d6397fd9479230e3 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Tue, 25 Apr 2023 14:37:26 -0400 Subject: [PATCH 8/9] docs(schema): add bigint to schema docs --- docs/schematypes.md | 16 ++++++++++++++++ lib/schema.js | 2 ++ 2 files changed, 18 insertions(+) diff --git a/docs/schematypes.md b/docs/schematypes.md index 1c3fb9a34f7..37525c969b6 100644 --- a/docs/schematypes.md +++ b/docs/schematypes.md @@ -54,6 +54,7 @@ Check out [Mongoose's plugins search](http://plugins.mongoosejs.io) to find plug - [Map](#maps) - [Schema](#schemas) - [UUID](#uuid) +- [BigInt](#bigint)

Example

@@ -632,6 +633,21 @@ const schema = new mongoose.Schema({ }); ``` +

BigInt

+ +Mongoose supports [JavaScript BigInts](https://thecodebarbarian.com/an-overview-of-bigint-in-node-js.html) as a SchemaType. +BigInts are stored as [64-bit integers in MongoDB (BSON type "long")](https://www.mongodb.com/docs/manual/reference/bson-types/). + +```javascript +const questionSchema = new Schema({ + answer: BigInt +}); +const Question = mongoose.model('Question', questionSchema); + +const question = new Question({ answer: 42n }); +typeof question.answer; // 'bigint' +``` +

Getters

Getters are like virtuals for paths defined in your schema. For example, diff --git a/lib/schema.js b/lib/schema.js index 8a7a7150e67..0f2a33d54b0 100644 --- a/lib/schema.js +++ b/lib/schema.js @@ -2665,6 +2665,8 @@ module.exports = exports = Schema; * - [Date](/docs/schematypes.html#dates) * - [ObjectId](/docs/schematypes.html#objectids) | Oid * - [Mixed](/docs/schematypes.html#mixed) + * - [UUID](/docs/schematypes.html#uuid) + * - [BigInt](/docs/schematypes.html#bigint) * * Using this exposed access to the `Mixed` SchemaType, we can use them in our schema. * From 4255f5651746678161e40018fac4576d178a625b Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Tue, 25 Apr 2023 14:41:09 -0400 Subject: [PATCH 9/9] chore: fix docs build --- test/docs/lean.test.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/docs/lean.test.js b/test/docs/lean.test.js index 372a703179f..4512c7ced7f 100644 --- a/test/docs/lean.test.js +++ b/test/docs/lean.test.js @@ -212,7 +212,9 @@ describe('Lean Tutorial', function() { // acquit:ignore:start await Person.deleteMany({}); // acquit:ignore:end - await Person.create({ name: 'Benjamin Sisko', age: 37n }); + // Mongoose will convert `age` to a BigInt + const { age } = await Person.create({ name: 'Benjamin Sisko', age: 37 }); + typeof age; // 'bigint' // By default, if you store a document with a BigInt property in MongoDB and you // load the document with `lean()`, the BigInt property will be a number @@ -230,7 +232,6 @@ describe('Lean Tutorial', function() { typeof person.age; // 'bigint' // acquit:ignore:start assert.equal(typeof person.age, 'bigint'); - assert.equal(person.age, 37n); // acquit:ignore:end }); });