From c505a414c9c83b13aec1409a8800bc15c3305910 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Tue, 27 Dec 2022 17:04:52 -0500 Subject: [PATCH] chore: update 5.x docs for search improvements re: #12830 --- docs/5.x/docs/api/arraysubdocument.html | 31 ++ docs/5.x/docs/api/buffer.html | 62 +++ docs/5.x/docs/api/decimal128.html | 25 ++ docs/5.x/docs/api/map.html | 46 +++ docs/5.x/docs/api/mongoosedocumentarray.html | 44 +++ docs/5.x/docs/api/subdocument.html | 25 ++ docs/5.x/docs/api/subdocumentpath.html | 55 +++ docs/5.x/docs/change-streams.html | 90 +++++ docs/5.x/docs/jobs.html | 133 +++++++ docs/5.x/docs/js/search.js | 5 +- docs/5.x/docs/lodash.html | 51 +++ docs/5.x/docs/migrating_to_6.html | 364 +++++++++++------- docs/5.x/docs/sponsors.html | 112 +----- docs/5.x/docs/timestamps.html | 208 ++++++++++ .../docs/typescript/statics-and-methods.html | 142 +++++++ docs/5.x/docs/typescript/subdocuments.html | 96 +++++ docs/5.x/docs/typescript/virtuals.html | 77 ++++ 17 files changed, 1323 insertions(+), 243 deletions(-) create mode 100644 docs/5.x/docs/api/arraysubdocument.html create mode 100644 docs/5.x/docs/api/buffer.html create mode 100644 docs/5.x/docs/api/decimal128.html create mode 100644 docs/5.x/docs/api/map.html create mode 100644 docs/5.x/docs/api/mongoosedocumentarray.html create mode 100644 docs/5.x/docs/api/subdocument.html create mode 100644 docs/5.x/docs/api/subdocumentpath.html create mode 100644 docs/5.x/docs/change-streams.html create mode 100644 docs/5.x/docs/jobs.html create mode 100644 docs/5.x/docs/lodash.html create mode 100644 docs/5.x/docs/timestamps.html create mode 100644 docs/5.x/docs/typescript/statics-and-methods.html create mode 100644 docs/5.x/docs/typescript/subdocuments.html create mode 100644 docs/5.x/docs/typescript/virtuals.html diff --git a/docs/5.x/docs/api/arraysubdocument.html b/docs/5.x/docs/api/arraysubdocument.html new file mode 100644 index 00000000000..55bd7aae1e1 --- /dev/null +++ b/docs/5.x/docs/api/arraysubdocument.html @@ -0,0 +1,31 @@ +Mongoose v6.8.1: ArraySubdocument
+ +

ArraySubdocument


ArraySubdocument.prototype.$parent()

Returns this sub-documents parent document.

+

ArraySubdocument.prototype.parentArray()

Returns this subdocument's parent array.

+

+ + Example: + +

+
const Test = mongoose.model('Test', new Schema({
+  docArr: [{ name: String }]
+}));
+const doc = new Test({ docArr: [{ name: 'test subdoc' }] });
+
+doc.docArr[0].parentArray() === doc.docArr; // true
+
+
\ No newline at end of file diff --git a/docs/5.x/docs/api/buffer.html b/docs/5.x/docs/api/buffer.html new file mode 100644 index 00000000000..938f9b89e1f --- /dev/null +++ b/docs/5.x/docs/api/buffer.html @@ -0,0 +1,62 @@ +Mongoose v6.8.1: Buffer
+ +

Buffer


MongooseBuffer.mixin.copy()

Parameters:
  • target +«Buffer»
Returns:
  • «Number» The number of bytes copied.
Type:
  • «property»

Copies the buffer.

+

+ + Note: + +

+

Buffer#copy does not mark target as modified so you must copy from a MongooseBuffer for it to work as expected. This is a work around since copy modifies the target, not this.

+

MongooseBuffer.mixin.equals()

Parameters:
  • other +«Buffer»
Returns:
  • «Boolean»

Determines if this buffer is equals to other buffer

+

MongooseBuffer.mixin.subtype()

Parameters:
  • subtype +«Hex»
See:

Sets the subtype option and marks the buffer modified.

+

+ + SubTypes: + +

+
const bson = require('bson')
+bson.BSON_BINARY_SUBTYPE_DEFAULT
+bson.BSON_BINARY_SUBTYPE_FUNCTION
+bson.BSON_BINARY_SUBTYPE_BYTE_ARRAY
+bson.BSON_BINARY_SUBTYPE_UUID
+bson.BSON_BINARY_SUBTYPE_MD5
+bson.BSON_BINARY_SUBTYPE_USER_DEFINED
+
+doc.buffer.subtype(bson.BSON_BINARY_SUBTYPE_UUID);
+
+

MongooseBuffer.mixin.toBSON()

Returns:
  • «Binary»

Converts this buffer for storage in MongoDB, including subtype

+

MongooseBuffer.mixin.toObject()

Parameters:
  • [subtype] +«Hex»
Returns:
  • «Binary»
See:

Converts this buffer to its Binary type representation.

+

+ + SubTypes: + +

+
const bson = require('bson')
+bson.BSON_BINARY_SUBTYPE_DEFAULT
+bson.BSON_BINARY_SUBTYPE_FUNCTION
+bson.BSON_BINARY_SUBTYPE_BYTE_ARRAY
+bson.BSON_BINARY_SUBTYPE_UUID
+bson.BSON_BINARY_SUBTYPE_MD5
+bson.BSON_BINARY_SUBTYPE_USER_DEFINED
+doc.buffer.toObject(bson.BSON_BINARY_SUBTYPE_USER_DEFINED);
+
+

MongooseBuffer.mixin.write()

Type:
  • «property»

Writes the buffer.

+
\ No newline at end of file diff --git a/docs/5.x/docs/api/decimal128.html b/docs/5.x/docs/api/decimal128.html new file mode 100644 index 00000000000..99a50cd250f --- /dev/null +++ b/docs/5.x/docs/api/decimal128.html @@ -0,0 +1,25 @@ +Mongoose v6.8.1: Decimal128
+ +

Decimal128


Decimal128()

Decimal128 type constructor

+

+ + Example: + +

+
const id = new mongoose.Types.Decimal128('3.1415');
+
+
\ No newline at end of file diff --git a/docs/5.x/docs/api/map.html b/docs/5.x/docs/api/map.html new file mode 100644 index 00000000000..4910416a6e8 --- /dev/null +++ b/docs/5.x/docs/api/map.html @@ -0,0 +1,46 @@ +Mongoose v6.8.1: Map
+ +

Map


MongooseMap.prototype.$isMongooseMap

Type:
  • «property»

Set to true for all Mongoose map instances

+

MongooseMap.prototype.clear()

Overwrites native Map's clear() function to support change tracking.

+

MongooseMap.prototype.delete()

Overwrites native Map's delete() function to support change tracking.

+

MongooseMap.prototype.get()

Overwrites native Map's get() function to support Mongoose getters.

+

MongooseMap.prototype.set()

Overwrites native Map's set() function to support setters, populate(), +and change tracking. Note that Mongoose maps only support strings and +ObjectIds as keys.

+

+ + Example: + +

+
doc.myMap.set('test', 42); // works
+doc.myMap.set({ obj: 42 }, 42); // Throws "Mongoose maps only support string keys"
+
+

MongooseMap.prototype.toBSON()

Converts this map to a native JavaScript Map so the MongoDB driver can serialize it.

+

MongooseMap.prototype.toJSON()

Parameters:
  • [options] +«Object»
    • [options.flattenMaps=false] +«Boolean» set to true to convert the map to a POJO rather than a native JavaScript map

      +

Converts this map to a native JavaScript Map for JSON.stringify(). Set +the flattenMaps option to convert this map to a POJO instead.

+

+ + Example: + +

+
doc.myMap.toJSON() instanceof Map; // true
+doc.myMap.toJSON({ flattenMaps: true }) instanceof Map; // false
+
+
\ No newline at end of file diff --git a/docs/5.x/docs/api/mongoosedocumentarray.html b/docs/5.x/docs/api/mongoosedocumentarray.html new file mode 100644 index 00000000000..e4dbfd9f078 --- /dev/null +++ b/docs/5.x/docs/api/mongoosedocumentarray.html @@ -0,0 +1,44 @@ +Mongoose v6.8.1: MongooseDocumentArray
+ +

MongooseDocumentArray


MongooseDocumentArray.prototype.create()

Parameters:
  • obj +«Object» the value to cast to this arrays SubDocument schema

    +

Creates a subdocument casted to this schema.

+

This is the same subdocument constructor used for casting.

+

MongooseDocumentArray.prototype.id()

Parameters:
  • id +«ObjectId|String|Number|Buffer»
Returns:
  • «EmbeddedDocument,null» the subdocument or null if not found.

Searches array items for the first document with a matching _id.

+

+ + Example: + +

+
const embeddedDoc = m.array.id(some_id);
+
+

MongooseDocumentArray.prototype.inspect()

Helper for console.log

+

MongooseDocumentArray.prototype.pull()

Parameters:
  • [...args] +«Object»

Pulls items from the array atomically.

+

MongooseDocumentArray.prototype.push()

Parameters:
  • [...args] +«Object»

Wraps Array#push with proper change tracking.

+

MongooseDocumentArray.prototype.toObject()

Parameters:
  • [options] +«Object» optional options to pass to each documents toObject method call during conversion

    +
Returns:
  • «Array»

Returns a native js Array of plain js objects

+

+ + Note: + +

+

Each sub-document is converted to a plain object by calling its #toObject method.

+
\ No newline at end of file diff --git a/docs/5.x/docs/api/subdocument.html b/docs/5.x/docs/api/subdocument.html new file mode 100644 index 00000000000..6b4e9e32807 --- /dev/null +++ b/docs/5.x/docs/api/subdocument.html @@ -0,0 +1,25 @@ +Mongoose v6.8.1: Subdocument
+ +

Subdocument


Subdocument.prototype.$parent()

Returns this sub-documents parent document.

+

Subdocument.prototype.inspect()

Helper for console.log

+

Subdocument.prototype.ownerDocument()

Returns:
  • «Document»

Returns the top level document of this sub-document.

+

Subdocument.prototype.parent()

Returns this sub-documents parent document.

+

Subdocument.prototype.remove()

Parameters:
  • [options] +«Object»
  • [callback] +«Function» optional callback for compatibility with Document.prototype.remove

    +

Null-out this subdoc

+
\ No newline at end of file diff --git a/docs/5.x/docs/api/subdocumentpath.html b/docs/5.x/docs/api/subdocumentpath.html new file mode 100644 index 00000000000..be90e3f0054 --- /dev/null +++ b/docs/5.x/docs/api/subdocumentpath.html @@ -0,0 +1,55 @@ +Mongoose v6.8.1: SubdocumentPath
+ +

SubdocumentPath


SubdocumentPath()

Parameters:
  • schema +«Schema»
  • path +«String»
  • options +«Object»
Inherits:

Single nested subdocument SchemaType constructor.

+

SubdocumentPath.prototype.discriminator()

Parameters:
  • name +«String»
  • schema +«Schema» fields to add to the schema for instances of this sub-class

    +
  • [options] +«Object|string» If string, same as options.value.

    +
    • [options.value] +«String» the string stored in the discriminatorKey property. If not specified, Mongoose uses the name parameter.

      +
    • [options.clone=true] +«Boolean» By default, discriminator() clones the given schema. Set to false to skip cloning.

      +
Returns:
  • «Function» the constructor Mongoose will use for creating instances of this discriminator model
See:

Adds a discriminator to this single nested subdocument.

+

+ + Example: + +

+
const shapeSchema = Schema({ name: String }, { discriminatorKey: 'kind' });
+const schema = Schema({ shape: shapeSchema });
+
+const singleNestedPath = parentSchema.path('shape');
+singleNestedPath.discriminator('Circle', Schema({ radius: Number }));
+
+

SubdocumentPath.set()

Parameters:
  • option +«String» The option you'd like to set the value for

    +
  • value +«Any» value for option

    +
Returns:
  • «void,void»
Type:
  • «property»

Sets a default option for all SubdocumentPath instances.

+

+ + Example: + +

+
// Make all numbers have option `min` equal to 0.
+mongoose.Schema.SubdocumentPath.set('required', true);
+
+
\ No newline at end of file diff --git a/docs/5.x/docs/change-streams.html b/docs/5.x/docs/change-streams.html new file mode 100644 index 00000000000..0523042210c --- /dev/null +++ b/docs/5.x/docs/change-streams.html @@ -0,0 +1,90 @@ +Mongoose v6.8.1: MongoDB Change Streams in NodeJS with Mongoose
+ +

+ + Change Streams + +

+ + + + +

Change streams let you listen for updates to documents in a given model's collection, or even documents in an entire database. +Unlike middleware, change streams are a MongoDB server construct, which means they pick up changes from anywhere. +Even if you update a document from a MongoDB GUI, your Mongoose change stream will be notified.

+

The watch() function creates a change stream. +Change streams emit a 'data' event when a document is updated.

+
const Person = mongoose.model('Person', new mongoose.Schema({ name: String }));
+
+// Create a change stream. The 'change' event gets emitted when there's a
+// change in the database. Print what the change stream emits.
+Person.watch().
+  on('change', data => console.log(data));
+
+// Insert a doc, will trigger the change stream handler above
+await Person.create({ name: 'Axl Rose' });
+
+

The above script will print output that looks like:

+
{
+  _id: {
+    _data: '8262408DAC000000012B022C0100296E5A10042890851837DB4792BE6B235E8B85489F46645F6964006462408DAC6F5C42FF5EE087A20004'
+  },
+  operationType: 'insert',
+  clusterTime: new Timestamp({ t: 1648397740, i: 1 }),
+  fullDocument: {
+    _id: new ObjectId("62408dac6f5c42ff5ee087a2"),
+    name: 'Axl Rose',
+    __v: 0
+  },
+  ns: { db: 'test', coll: 'people' },
+  documentKey: { _id: new ObjectId("62408dac6f5c42ff5ee087a2") }
+}
+
+

Note that you must be connected to a MongoDB replica set or sharded cluster to use change streams. +If you try to call watch() when connected to a standalone MongoDB server, you'll get the below error.

+
MongoServerError: The $changeStream stage is only supported on replica sets
+
+

If you're using watch() in production, we recommend using MongoDB Atlas. +For local development, we recommend mongodb-memory-server or run-rs to start a replica set locally.

+

+ + Iterating using next() + +

+

If you want to iterate through a change stream in a AWS Lambda function, do not use event emitters to listen to the change stream. +You need to make sure you close your change stream when your Lambda function is done executing, because your change stream may end up in an inconsistent state if Lambda stops your container while the change stream is pulling data from MongoDB.

+

Change streams also have a next() function that lets you explicitly wait for the next change to come in. +Use resumeAfter to track where the last change stream left off, and add a timeout to make sure your handler doesn't wait forever if no changes come in.

+
let resumeAfter = undefined;
+
+exports.handler = async (event, context) => {
+  // add this so that we can re-use any static/global variables between function calls if Lambda
+  // happens to re-use existing containers for the invocation.
+  context.callbackWaitsForEmptyEventLoop = false;
+
+  await connectToDatabase();
+
+  const changeStream = await Country.watch([], { resumeAfter });
+
+  // Change stream `next()` will wait forever if there are no changes. So make sure to
+  // stop listening to the change stream after a fixed period of time.
+  let timeoutPromise = new Promise(resolve => setTimeout(() => resolve(false), 1000));
+  let doc = null;
+  while (doc = await Promise.race([changeStream.next(), timeoutPromise])) {
+    console.log('Got', doc);
+  }
+
+  // `resumeToken` tells you where the change stream left off, so next function instance
+  // can pick up any changes that happened in the meantime.
+  resumeAfter = changeStream.resumeToken;
+  await changeStream.close();
+};
+
+
\ No newline at end of file diff --git a/docs/5.x/docs/jobs.html b/docs/5.x/docs/jobs.html new file mode 100644 index 00000000000..856e78b2485 --- /dev/null +++ b/docs/5.x/docs/jobs.html @@ -0,0 +1,133 @@ +Mongoose v6.8.1: Mongoose MongoDB Jobs

Mongoose Jobs

Add Your Own

Hiring Node.js and MongoDB developers? +Sponsor Mongoose on GitHub to post your job here!
\ No newline at end of file diff --git a/docs/5.x/docs/js/search.js b/docs/5.x/docs/js/search.js index 71c623502e2..2d62ed66901 100644 --- a/docs/5.x/docs/js/search.js +++ b/docs/5.x/docs/js/search.js @@ -9,9 +9,12 @@ for (var i = 0; i < pairs.length; ++i) { } } +var versionFromUrl = window.location.pathname.match(/^\/docs\/(\d+\.x)/); +var version = versionFromUrl ? versionFromUrl[1] : '6.x'; + if (q != null) { document.getElementById('search-input').value = decodeURIComponent(q); - fetch(root + '/search?search=' + q). + fetch(root + '/search?search=' + q + '&version=' + version). then(function(res) { return res.json(); }). then( function(result) { diff --git a/docs/5.x/docs/lodash.html b/docs/5.x/docs/lodash.html new file mode 100644 index 00000000000..2d6cb3c27e9 --- /dev/null +++ b/docs/5.x/docs/lodash.html @@ -0,0 +1,51 @@ +Mongoose v6.8.1: Using Mongoose with Lodash
+ +

+ + Using Mongoose with Lodash + +

+ + + + +

For the most part, Mongoose works well with Lodash. +However, there are a few caveats that you should know about.

+ +

+ + cloneDeep() + +

+

You should not use Lodash's cloneDeep() function on any Mongoose objects. +This includes connections, model classes, and queries, but is especially important for documents. +For example, you may be tempted to do the following:

+
const _ = require('lodash');
+
+const doc = await MyModel.findOne();
+
+const newDoc = _.cloneDeep(doc);
+newDoc.myProperty = 'test';
+await newDoc.save();
+
+

However, the above code will throw the following error if MyModel has any array properties.

+
TypeError: this.__parentArray.$path is not a function
+
+

This is because Lodash's cloneDeep() function doesn't handle proxies, and Mongoose arrays are proxies as of Mongoose 6. +You typically don't have to deep clone Mongoose documents, but, if you have to, use the following alternative to cloneDeep():

+
const doc = await MyModel.findOne();
+
+const newDoc = new MyModel().init(doc.toObject());
+newDoc.myProperty = 'test';
+await newDoc.save();
+
+
\ No newline at end of file diff --git a/docs/5.x/docs/migrating_to_6.html b/docs/5.x/docs/migrating_to_6.html index 9e9d4fcd95f..16c3b7d5d16 100644 --- a/docs/5.x/docs/migrating_to_6.html +++ b/docs/5.x/docs/migrating_to_6.html @@ -1,5 +1,5 @@ -Mongoose v6.0.8: Migrating to Mongoose 6
+Mongoose v6.8.1: Migrating to Mongoose 6
+ +

There are several backwards-breaking changes you should be aware of when migrating from Mongoose 5.x to Mongoose 6.x.

-

If you're still on Mongoose 4.x, please read the Mongoose 4.x to 5.x migration guide and upgrade to Mongoose 5.x first.

+

If you're still on Mongoose 4.x, please read the Mongoose 4.x to 5.x migration guide and upgrade to Mongoose 5.x first.

Version Requirements

@@ -62,64 +74,86 @@

MongoDB Driver 4.0
  • MongoDB Driver 4.x is written in TypeScript and has its own TypeScript type definitions. These may conflict with @types/mongodb, so if you have TypeScript compiler errors please make sure you upgrade to the latest version of @types/mongodb, which is an empty stub.
  • +
  • The poolSize option for connections has been replaced with minPoolSize and maxPoolSize. The Mongoose 5.x poolSize option is equivalent to the Mongoose 6 maxPoolSize option. The default value of maxPoolSize has been increased to 100.
  • The result of updateOne() and updateMany() is now different.
  • The result of deleteOne() and deleteMany() no longer has an n property.
-
let res = await TestModel.updateMany({}, { someProperty: 'someValue' });
+
let res = await TestModel.updateMany({}, { someProperty: 'someValue' });
 
-res.matchedCount; // Number of documents that were found that match the filter. Replaces `res.n`
-res.modifiedCount; // Number of documents modified. Replaces `res.nModified`
-res.upsertedCount; // Number of documents upserted. Replaces `res.upserted`
-
let res = await TestModel.deleteMany({});
+res.matchedCount; // Number of documents that were found that match the filter. Replaces `res.n`
+res.modifiedCount; // Number of documents modified. Replaces `res.nModified`
+res.upsertedCount; // Number of documents upserted. Replaces `res.upserted`
+
+
let res = await TestModel.deleteMany({});
 
 // In Mongoose 6: `{ acknowledged: true, deletedCount: 2 }`
 // In Mongoose 5: `{ n: 2, ok: 1, deletedCount: 2 }`
 res;
 
-res.deletedCount; // Number of documents that were deleted. Replaces `res.n`
+res.deletedCount; // Number of documents that were deleted. Replaces `res.n` +

No More Deprecation Warning Options

useNewUrlParser, useUnifiedTopology, useFindAndModify, and useCreateIndex are no longer supported options. Mongoose 6 always behaves as if useNewUrlParser, useUnifiedTopology, and useCreateIndex are true, and useFindAndModify is false. Please remove these options from your code.

// No longer necessary:
-mongoose.set('useFindAndModify', false);
+mongoose.set('useFindAndModify', false);
 
-await mongoose.connect('mongodb://localhost:27017/test', {
+await mongoose.connect('mongodb://127.0.0.1:27017/test', {
   useNewUrlParser: true, // <-- no longer necessary
   useUnifiedTopology: true // <-- no longer necessary
-});
-

The `asPromise()` Method for Connections

+}); + +

The asPromise() Method for Connections

Mongoose connections are no longer thenable. This means that await mongoose.createConnection(uri) no longer waits for Mongoose to connect. Use mongoose.createConnection(uri).asPromise() instead. See #8810.

// The below no longer works in Mongoose 6
-await mongoose.createConnection(uri);
+await mongoose.createConnection(uri);
 
 // Do this instead
-await mongoose.createConnection(uri).asPromise();
-

`mongoose.connect()` Returns a Promise

+await mongoose.createConnection(uri).asPromise(); + +

mongoose.connect() Returns a Promise

The mongoose.connect() function now always returns a promise, not a Mongoose instance.

Duplicate Query Execution

Mongoose no longer allows executing the same query object twice. If you do, you'll get a Query was already executed error. Executing the same query instance twice is typically indicative of mixing callbacks and promises, but if you need to execute the same query twice, you can call Query#clone() to clone the query and re-execute it. See gh-7398

-
// Results in 'Query was already executed' error, because technically this `find()` query executes twice.
-await Model.find({}, function(err, result) {});
+
// Results in 'Query was already executed' error, because technically this `find()` query executes twice.
+await Model.find({}, function(err, result) {});
 
-const q = Model.find();
+const q = Model.find();
 await q;
-await q.clone(); // Can `clone()` the query to allow executing the query again
-

`strictQuery` is removed and replaced by `strict`

- -

Mongoose no longer supports a strictQuery option. You must now use strict. This means that, by default, Mongoose will filter out filter properties that are not in the schema.

-
const userSchema = new Schema({ name: String });
-const User = mongoose.model('User', userSchema);
+await q.clone(); // Can `clone()` the query to allow executing the query again
+
+

Model.exists(...) now returns a lean document instead of boolean

+ +
// in Mongoose 5.x, `existingUser` used to be a boolean
+// now `existingUser` will be either `{ _id: ObjectId(...) }` or `null`.
+const existingUser = await User.exists({ name: 'John' });
+if (existingUser) {
+  console.log(existingUser._id); 
+}
+
+

strictQuery is now equal to strict by default

+ +

Mongoose no longer supports a strictQuery option. You must now use strict. +As of Mongoose 6.0.10, we brought back the strictQuery option. +However, strictQuery is tied to strict by default. +This means that, by default, Mongoose will filter out query filter properties that are not in the schema.

+
const userSchema = new Schema({ name: String });
+const User = mongoose.model('User', userSchema);
 
 // By default, this is equivalent to `User.find()` because Mongoose filters out `notInSchema`
-await User.find({ notInSchema: 1 });
+await User.find({ notInSchema: 1 });
 
-// Set `strict: false` to opt in to filtering by properties that aren't in the schema
-await User.find({ notInSchema: 1 }, null, { strict: false });
+// Set `strictQuery: false` to opt in to filtering by properties that aren't in the schema
+await User.find({ notInSchema: 1 }, null, { strictQuery: false });
 // equivalent:
-await User.find({ notInSchema: 1 }).setOptions({ strict: false });
+await User.find({ notInSchema: 1 }).setOptions({ strictQuery: false }); +
+

You can also disable strictQuery globally to override:

+
mongoose.set('strictQuery', false);
+

MongoError is now MongoServerError

In MongoDB Node.js Driver v4.x, 'MongoError' is now 'MongoServerError'. Please change any code that depends on the hardcoded string 'MongoError'.

@@ -127,86 +161,135 @@

// In Mongoose 6, these two are equivalent: -User.discriminator('author', authorSchema); -User.discriminator('author', authorSchema.clone()); +User.discriminator('author', authorSchema); +User.discriminator('author', authorSchema.clone()); // To opt out if `clone()` is causing issues, pass `clone: false` -User.discriminator('author', authorSchema, { clone: false }); +User.discriminator('author', authorSchema, { clone: false }); + +

Simplified isValidObjectId() and separate isObjectIdOrHexString()

+ +

In Mongoose 5, mongoose.isValidObjectId() returned false for values like numbers, which was inconsistent with the MongoDB driver's ObjectId.isValid() function. +Technically, any JavaScript number can be converted to a MongoDB ObjectId.

+

In Mongoose 6, mongoose.isValidObjectId() is just a wrapper for mongoose.Types.ObjectId.isValid() for consistency.

+

Mongoose 6.2.5 now includes a mongoose.isObjectIdOrHexString() function, which does a better job of capturing the more common use case for isValidObjectId(): is the given value an ObjectId instance or a 24 character hex string representing an ObjectId?

+
// `isValidObjectId()` returns `true` for some surprising values, because these
+// values are _technically_ ObjectId representations
+mongoose.isValidObjectId(new mongoose.Types.ObjectId()); // true
+mongoose.isValidObjectId('0123456789ab'); // true
+mongoose.isValidObjectId(6); // true
+mongoose.isValidObjectId(new User({ name: 'test' })); // true
+
+// `isObjectIdOrHexString()` instead only returns `true` for ObjectIds and 24
+// character hex strings.
+mongoose.isObjectIdOrHexString(new mongoose.Types.ObjectId()); // true
+mongoose.isObjectIdOrHexString('62261a65d66c6be0a63c051f'); // true
+mongoose.isObjectIdOrHexString('0123456789ab'); // false
+mongoose.isObjectIdOrHexString(6); // false
+

Schema Defined Document Key Order

Mongoose now saves objects with keys in the order the keys are specified in the schema, not in the user-defined object. So whether Object.keys(new User({ name: String, email: String }).toObject() is ['name', 'email'] or ['email', 'name'] depends on the order name and email are defined in your schema.

-
const schema = new Schema({
+
const schema = new Schema({
   profile: {
     name: {
-      first: String,
-      last: String
+      first: String,
+      last: String
     }
   }
 });
-const Test = db.model('Test', schema);
+const Test = db.model('Test', schema);
 
-const doc = new Test({
-  profile: { name: { last: 'Musashi', first: 'Miyamoto' } }
+const doc = new Test({
+  profile: { name: { last: 'Musashi', first: 'Miyamoto' } }
 });
 
-// Note that 'first' comes before 'last', even though the argument to `new Test()` flips the key order.
-// Mongoose uses the schema's key order, not the provided objects' key order.
-assert.deepEqual(Object.keys(doc.toObject().profile.name), ['first', 'last']);
-

`sanitizeFilter` and `trusted()`

+// Note that 'first' comes before 'last', even though the argument to `new Test()` flips the key order. +// Mongoose uses the schema's key order, not the provided objects' key order. +assert.deepEqual(Object.keys(doc.toObject().profile.name), ['first', 'last']); +
+

sanitizeFilter and trusted()

Mongoose 6 introduces a new sanitizeFilter option to globals and queries that defends against query selector injection attacks. If you enable sanitizeFilter, Mongoose will wrap any object in the query filter in a $eq:

-
// Mongoose will convert this filter into `{ username: 'val', pwd: { $eq: { $ne: null } } }`, preventing
+
// Mongoose will convert this filter into `{ username: 'val', pwd: { $eq: { $ne: null } } }`, preventing
 // a query selector injection.
-await Test.find({ username: 'val', pwd: { $ne: null } }).setOptions({ sanitizeFilter: true });
+await Test.find({ username: 'val', pwd: { $ne: null } }).setOptions({ sanitizeFilter: true }); +

To explicitly allow a query selector, use mongoose.trusted():

// `mongoose.trusted()` allows query selectors through
-await Test.find({ username: 'val', pwd: mongoose.trusted({ $ne: null }) }).setOptions({ sanitizeFilter: true });
+await Test.find({ username: 'val', pwd: mongoose.trusted({ $ne: null }) }).setOptions({ sanitizeFilter: true }); + +

Removed omitUndefined: Mongoose now removes undefined keys in updates instead of setting them to null

+ +

In Mongoose 5.x, setting a key to undefined in an update operation was equivalent to setting it to null.

+
let res = await Test.findOneAndUpdate({}, { $set: { name: undefined } }, { new: true });
+
+res.name; // `null` in Mongoose 5.x
+
+// Equivalent to `findOneAndUpdate({}, {}, { new: true })` because `omitUndefined` will
+// remove `name: undefined`
+res = await Test.findOneAndUpdate({}, { $set: { name: undefined } }, { new: true, omitUndefined: true });
+
+

Mongoose 5.x supported an omitUndefined option to strip out undefined keys. +In Mongoose 6.x, the omitUndefined option has been removed, and Mongoose will always strip out undefined keys.

+
// In Mongoose 6, equivalent to `findOneAndUpdate({}, {}, { new: true })` because Mongoose will
+// remove `name: undefined`
+const res = await Test.findOneAndUpdate({}, { $set: { name: undefined } }, { new: true });
+
+

The only workaround is to explicitly set properties to null in your updates:

+
const res = await Test.findOneAndUpdate({}, { $set: { name: null } }, { new: true });
+

Document Parameter to Default Functions

-

Mongoose now passes the document as the first parameter to default functions, which is helpful for using arrow functions with defaults. This may affect you if you pass a function that expects different parameters to default, like default: mongoose.Types.ObjectId. See gh-9633

-
const schema = new Schema({
-  name: String,
-  age: Number,
+

Mongoose now passes the document as the first parameter to default functions, which is helpful for using arrow functions with defaults.

+

This may affect you if you pass a function that expects different parameters to default, like default: mongoose.Types.ObjectId. See gh-9633. If you're passing a default function that does not utilize the document, change default: myFunction to default: () => myFunction() to avoid accidentally passing parameters that potentially change the behavior.

+
const schema = new Schema({
+  name: String,
+  age: Number,
   canVote: {
-    type: Boolean,
+    type: Boolean,
     // Default functions now receive a `doc` parameter, helpful for arrow functions
-    default: doc => doc.age >= 18
+    default: doc => doc.age >= 18
   }
-});
+}); +

Arrays are Proxies

Mongoose arrays are now ES6 proxies. You no longer need to markModified() after setting an array index directly.

-
const post = await BlogPost.findOne();
+
const post = await BlogPost.findOne();
 
-post.tags[0] = 'javascript';
-await post.save(); // Works, no need for `markModified()`!
-

`typePojoToMixed`

+post.tags[0] = 'javascript'; +await post.save(); // Works, no need for `markModified()`! +
+

typePojoToMixed

Schema paths declared with type: { name: String } become single nested subdocs in Mongoose 6, as opposed to Mixed in Mongoose 5. This removes the need for the typePojoToMixed option. See gh-7181.

// In Mongoose 6, the below makes `foo` into a subdocument with a `name` property.
 // In Mongoose 5, the below would make `foo` a `Mixed` type, _unless_ you set `typePojoToMixed: true`.
-const schema = new Schema({
-  foo: { type: { name: String } }
-});
-

`strictPopulate()`

+const schema = new Schema({ + foo: { type: { name: String } } +}); + +

strictPopulate()

Mongoose now throws an error if you populate() a path that isn't defined in your schema. This is only for cases when we can infer the local schema, like when you use Query#populate(), not when you call Model.populate() on a POJO. See gh-5124.

-

Subdocument `ref` Function Context

+

Subdocument ref Function Context

When populating a subdocument with a function ref or refPath, this is now the subdocument being populated, not the top-level document. See #8469.

-
const schema = new Schema({
+
const schema = new Schema({
   works: [{
-    modelId: String,
+    modelId: String,
     data: {
-      type: mongoose.ObjectId,
-      ref: function(doc) {
+      type: mongoose.ObjectId,
+      ref: function(doc) {
         // In Mongoose 6, `doc` is the array element, so you can access `modelId`.
         // In Mongoose 5, `doc` was the top-level document.
-        return doc.modelId;
+        return doc.modelId;
       }
     }
   }]
-});
+}); +

Schema Reserved Names Warning

Using save, isNew, and other Mongoose reserved names as schema path names now triggers a warning, not an error. You can suppress the warning by setting the supressReservedKeysWarning in your schema options: new Schema({ save: String }, { supressReservedKeysWarning: true }). Keep in mind that this may break plugins that rely on these reserved names.

@@ -216,10 +299,10 @@

Subdocument PathsCreating Aggregation Cursors

Aggregate#cursor() now returns an AggregationCursor instance to be consistent with Query#cursor(). You no longer need to do Model.aggregate(pipeline).cursor().exec() to get an aggregation cursor, just Model.aggregate(pipeline).cursor().

-

`autoCreate` Defaults to `true`

+

autoCreate Defaults to true

autoCreate is true by default unless readPreference is secondary or secondaryPreferred, which means Mongoose will attempt to create every model's underlying collection before creating indexes. If readPreference is secondary or secondaryPreferred, Mongoose will default to false for both autoCreate and autoIndex because both createCollection() and createIndex() will fail when connected to a secondary.

-

No More `context: 'query'`

+

No More context: 'query'

The context option for queries has been removed. Now Mongoose always uses context = 'query'.

Custom Validators with Populated Paths

@@ -229,41 +312,110 @@

Removed `execPopulate()`

+

Removed execPopulate()

Document#populate() now returns a promise and is now no longer chainable.

  • Replace await doc.populate('path1').populate('path2').execPopulate(); with await doc.populate(['path1', 'path2']);
  • -
  • Replace await doc.populate('path1', 'select1').populate('path2', 'select2').execPopulate(); with
    await doc.populate([{path: 'path1', select: 'select1'}, {path: 'path2', select: 'select2'}]);
    +
  • Replace await doc.populate('path1', 'select1').populate('path2', 'select2').execPopulate(); with
    await doc.populate([{path: 'path1', select: 'select1'}, {path: 'path2', select: 'select2'}]);
    +
-

`create()` with Empty Array

+

create() with Empty Array

await Model.create([]) in v6.0 returns an empty array when provided an empty array, in v5.0 it used to return undefined. If any of your code is checking whether the output is undefined or not, you need to modify it with the assumption that await Model.create(...) will always return an array if provided an array.

Removed Nested Path Merging

doc.set({ child: { age: 21 } }) now works the same whether child is a nested path or a subdocument: Mongoose will overwrite the value of child. In Mongoose 5, this operation would merge child if child was a nested path.

-

ObjectId `valueOf()`

+

ObjectId valueOf()

Mongoose now adds a valueOf() function to ObjectIds. This means you can now use == to compare an ObjectId against a string.

-
const a = ObjectId('6143b55ac9a762738b15d4f0');
+
const a = ObjectId('6143b55ac9a762738b15d4f0');
 
-a == '6143b55ac9a762738b15d4f0'; // true
-

Immutable `createdAt`

+a == '6143b55ac9a762738b15d4f0'; // true +
+

Immutable createdAt

If you set timestamps: true, Mongoose will now make the createdAt property immutable. See gh-10139

-

Removed Validator `isAsync`

+

Removed Validator isAsync

isAsync is no longer an option for validate. Use an async function instead.

-

Removed `safe`

+

Removed safe

safe is no longer an option for schemas, queries, or save(). Use writeConcern instead.

+

SchemaType set parameters

+ +

Mongoose now calls setter functions with priorValue as the 2nd parameter, rather than schemaType in Mongoose 5.

+
const userSchema = new Schema({
+  name: {
+    type: String,
+    trimStart: true,
+    set: trimStartSetter
+  }
+});
+
+// in v5.x the parameters were (value, schemaType), in v6.x the parameters are (value, priorValue, schemaType).
+function trimStartSetter(val, priorValue, schemaType) {
+  if (schemaType.options.trimStart && typeof val === 'string') {
+    return val.trimStart();
+  }
+  return val;
+}
+
+const User = mongoose.model('User', userSchema);
+
+const user = new User({ name: 'Robert Martin' });
+console.log(user.name); // 'robert martin'
+
+

toObject() and toJSON() Use Nested Schema minimize

+ +

This change was technically released with 5.10.5, but caused issues for users migrating from 5.9.x to 6.x. +In Mongoose < 5.10.5, toObject() and toJSON() would use the top-level schema's minimize option by default.

+
const child = new Schema({ thing: Schema.Types.Mixed });
+const parent = new Schema({ child }, { minimize: false });
+const Parent = model('Parent', parent);
+const p = new Parent({ child: { thing: {} } });
+
+// In v5.10.4, would contain `child.thing` because `toObject()` uses `parent` schema's `minimize` option
+// In `>= 5.10.5`, `child.thing` is omitted because `child` schema has `minimize: true`
+console.log(p.toObject());
+
+

As a workaround, you can either explicitly pass minimize to toObject() or toJSON():

+
console.log(p.toObject({ minimize: false }));
+
+

Or define the child schema inline (Mongoose 6 only) to inherit the parent's minimize option.

+
const parent = new Schema({
+  // Implicitly creates a new schema with the top-level schema's `minimize` option.
+  child: { type: { thing: Schema.Types.Mixed } }
+}, { minimize: false });
+
+

No default model for Query.prototype.populate()

+ +

In Mongoose 5, calling populate() on a mixed type or other path with no ref would fall back to using the query's model.

+
const testSchema = new mongoose.Schema({
+  data: String,
+  parents: Array // Array of mixed
+});
+
+const Test = mongoose.model('Test', testSchema);
+
+// The below `populate()`...
+await Test.findOne().populate('parents');
+// Is a shorthand for the following populate in Mongoose 5
+await Test.findOne().populate({ path: 'parents', model: Test });
+
+

In Mongoose 6, populating a path with no ref, refPath, or model is a no-op.

+
// The below `populate()` does nothing.
+await Test.findOne().populate('parents');
+

TypeScript changes

The Schema class now takes 3 generic params instead of 4. The 3rd generic param, SchemaDefinitionType, is now the same as the 1st generic param DocType. Replace new Schema<UserDocument, UserModel, User>(schemaDefinition) with new Schema<UserDocument, UserModel>(schemaDefinition)

+

Types.ObjectId is now a class, which means you can no longer omit new when creating a new ObjectId using new mongoose.Types.ObjectId(). +Currently, you can still omit new in JavaScript, but you must put new in TypeScript.

The following legacy types have been removed:

  • ModelUpdateOptions
  • @@ -276,56 +428,4 @@

  • SchemaTypeOpts
  • ConnectionOptions
-
\ No newline at end of file +
\ No newline at end of file diff --git a/docs/5.x/docs/sponsors.html b/docs/5.x/docs/sponsors.html index 32d8f67cc02..9937b237bf9 100644 --- a/docs/5.x/docs/sponsors.html +++ b/docs/5.x/docs/sponsors.html @@ -1,110 +1,2 @@ -Mongoose v6.0.8: Mongoose Sponsors
- -

- - Mongoose Sponsors - -

- - - - -
-
- - - -
-
-

- - Localize - -

- The Localize platform helps businesses of all sizes easily translate websites, applications, and documents into foreign languages, opening up access to new markets quickly and efficiently. And developers of all stripes will appreciate the simplicity of installing a single code snippet that unlocks an industry-leading and secure translation experience. Learn more at localizejs.com. -
-
- -
-
- - - -
-
-

- - Birb - -

- Birb is an e-commerce referral marketing platform to reward customers for referring their business to friends and followers. -
-
- -

- - Add Your Own - -

-

Want to feature your app on this page? -Sponsor Mongoose on GitHub!

-
\ No newline at end of file +Mongoose v6.8.1: Mongoose Sponsors

Mongoose Sponsors

The Localize platform helps businesses of all sizes easily translate websites, applications, and documents into foreign languages, opening up access to new markets quickly and efficiently. And developers of all stripes will appreciate the simplicity of installing a single code snippet that unlocks an industry-leading and secure translation experience. Learn more at localizejs.com.

Hello Club is a cloud-based club and membership management solution which offers a range of features for tracking members, finances, bookings, events, resources and access control. Hello Club makes member management easy and efficient, and caters to a large variety of organisations, including clubs, associations, non-profits, sport centres, gyms and co-working spaces. Consistent 5 star reviews, monthly new features, and a team that is dedicated to helping you succeed make Hello Club the best choice for your organisation. Try it now with a 30 day free trial!

Birb is an e-commerce referral marketing platform to reward customers for referring their business to friends and followers.

Fully Transparent Developer job board with salary ranges.

Add Your Own

Want to feature your app on this page? +Sponsor Mongoose on GitHub!
\ No newline at end of file diff --git a/docs/5.x/docs/timestamps.html b/docs/5.x/docs/timestamps.html new file mode 100644 index 00000000000..dc6ad0f61fe --- /dev/null +++ b/docs/5.x/docs/timestamps.html @@ -0,0 +1,208 @@ +Mongoose v6.8.1: Mongoose Timestamps
+ +

+ + Timestamps + +

+ + + + +

Mongoose schemas support a timestamps option. +If you set timestamps: true, Mongoose will add two properties of type Date to your schema:

+
    +
  1. createdAt: a date representing when this document was created
  2. +
  3. updatedAt: a date representing when this document was last updated
  4. +
+

Mongoose will then set createdAt when the document is first inserted, and update updatedAt whenever you update the document using save(), updateOne(), updateMany(), findOneAndUpdate(), update(), replaceOne(), or bulkWrite().

+
const userSchema = new Schema({ name: String }, { timestamps: true });
+const User = mongoose.model('User', userSchema);
+
+let doc = await User.create({ name: 'test' });
+
+console.log(doc.createdAt); // 2022-02-26T16:37:48.244Z
+console.log(doc.updatedAt); // 2022-02-26T16:37:48.244Z
+
+doc.name = 'test2';
+await doc.save();
+console.log(doc.createdAt); // 2022-02-26T16:37:48.244Z
+console.log(doc.updatedAt); // 2022-02-26T16:37:48.307Z
+
+doc = await User.findOneAndUpdate({ _id: doc._id }, { name: 'test3' }, { new: true });
+console.log(doc.createdAt); // 2022-02-26T16:37:48.244Z
+console.log(doc.updatedAt); // 2022-02-26T16:37:48.366Z
+
+

The createdAt property is immutable, and Mongoose overwrites any user-specified updates to updatedAt by default.

+
let doc = await User.create({ name: 'test' });
+
+console.log(doc.createdAt); // 2022-02-26T17:08:13.930Z
+console.log(doc.updatedAt); // 2022-02-26T17:08:13.930Z
+
+doc.name = 'test2';
+doc.createdAt = new Date(0);
+doc.updatedAt = new Date(0);
+await doc.save();
+
+// Mongoose blocked changing `createdAt` and set its own `updatedAt`, ignoring
+// the attempt to manually set them.
+console.log(doc.createdAt); // 2022-02-26T17:08:13.930Z
+console.log(doc.updatedAt); // 2022-02-26T17:08:13.991Z
+
+// Mongoose also blocks changing `createdAt` and sets its own `updatedAt`
+// on `findOneAndUpdate()`, `updateMany()`, and other query operations
+doc = await User.findOneAndUpdate(
+  { _id: doc._id },
+  { name: 'test3', createdAt: new Date(0), updatedAt: new Date(0) },
+  { new: true }
+);
+console.log(doc.createdAt); // 2022-02-26T17:08:13.930Z
+console.log(doc.updatedAt); // 2022-02-26T17:08:14.008Z
+
+

+ + Alternate Property Names + +

+

For the purposes of these docs, we'll always refer to createdAt and updatedAt. +But you can overwrite these property names as shown below.

+
const userSchema = new Schema({ name: String }, {
+  timestamps: {
+    createdAt: 'created_at', // Use `created_at` to store the created date
+    updatedAt: 'updated_at' // and `updated_at` to store the last updated date
+  }
+});
+
+

+ + Disabling Timestamps + +

+

save(), updateOne(), updateMany(), findOneAndUpdate(), update(), replaceOne(), and bulkWrite() all support a timestamps option. +Set timestamps: false to skip setting timestamps for that particular operation.

+
let doc = await User.create({ name: 'test' });
+
+console.log(doc.createdAt); // 2022-02-26T23:28:54.264Z
+console.log(doc.updatedAt); // 2022-02-26T23:28:54.264Z
+
+doc.name = 'test2';
+
+// Setting `timestamps: false` tells Mongoose to skip updating `updatedAt` on this `save()`
+await doc.save({ timestamps: false });
+console.log(doc.updatedAt); // 2022-02-26T23:28:54.264Z
+
+// Similarly, setting `timestamps: false` on a query tells Mongoose to skip updating
+// `updatedAt`.
+doc = await User.findOneAndUpdate({ _id: doc._id }, { name: 'test3' }, {
+  new: true,
+  timestamps: false
+});
+console.log(doc.updatedAt); // 2022-02-26T23:28:54.264Z
+
+// Below is how you can disable timestamps on a `bulkWrite()`
+await User.bulkWrite([{
+  updateOne: {
+    filter: { _id: doc._id },
+    update: { name: 'test4' },
+    timestamps: false
+  }
+}]);
+doc = await User.findOne({ _id: doc._id });
+console.log(doc.updatedAt); // 2022-02-26T23:28:54.264Z
+
+

You can also set the timestamps option to an object to configure createdAt and updatedAt separately. +For example, in the below code, Mongoose sets createdAt on save() but skips updatedAt.

+
let doc = new User({ name: 'test' });
+
+// Tell Mongoose to set `createdAt`, but skip `updatedAt`.
+await doc.save({ timestamps: { createdAt: true, updatedAt: false } });
+console.log(doc.createdAt); // 2022-02-26T23:32:12.478Z
+console.log(doc.updatedAt); // undefined
+
+

Disabling timestamps also lets you set timestamps yourself. +For example, suppose you need to correct a document's createdAt or updatedAt property. +You can do that by setting timestamps: false and setting createdAt yourself as shown below.

+
let doc = await User.create({ name: 'test' });
+
+// To update `updatedAt`, do a `findOneAndUpdate()` with `timestamps: false` and
+// `updatedAt` set to the value you want
+doc = await User.findOneAndUpdate({ _id: doc._id }, { updatedAt: new Date(0) }, {
+  new: true,
+  timestamps: false
+});
+console.log(doc.updatedAt); // 1970-01-01T00:00:00.000Z
+
+// To update `createdAt`, you also need to set `strict: false` because `createdAt`
+// is immutable
+doc = await User.findOneAndUpdate({ _id: doc._id }, { createdAt: new Date(0) }, {
+  new: true,
+  timestamps: false,
+  strict: false
+});
+console.log(doc.createdAt); // 1970-01-01T00:00:00.000Z
+
+

+ + Timestamps on Subdocuments + +

+

Mongoose also supports setting timestamps on subdocuments. +Keep in mind that createdAt and updatedAt for subdocuments represent when the subdocument was created or updated, not the top level document. +Overwriting a subdocument will also overwrite createdAt.

+
const roleSchema = new Schema({ value: String }, { timestamps: true });
+const userSchema = new Schema({ name: String, roles: [roleSchema] });
+
+let doc = await User.create({ name: 'test', roles: [{ value: 'admin' }] });
+console.log(doc.roles[0].createdAt); // 2022-02-27T00:22:53.836Z
+console.log(doc.roles[0].updatedAt); // 2022-02-27T00:22:53.836Z
+
+// Overwriting the subdocument also overwrites `createdAt` and `updatedAt`
+doc.roles[0] = { value: 'root' };
+await doc.save();
+console.log(doc.roles[0].createdAt); // 2022-02-27T00:22:53.902Z
+console.log(doc.roles[0].updatedAt); // 2022-02-27T00:22:53.902Z
+
+// But updating the subdocument preserves `createdAt` and updates `updatedAt`
+doc.roles[0].value = 'admin';
+await doc.save();
+console.log(doc.roles[0].createdAt); // 2022-02-27T00:22:53.902Z
+console.log(doc.roles[0].updatedAt); // 2022-02-27T00:22:53.909Z
+
+

+ + Under the Hood + +

+

For queries with timestamps, Mongoose adds 2 properties to each update query:

+
    +
  1. Add updatedAt to $set
  2. +
  3. Add createdAt to $setOnInsert
  4. +
+

For example, if you run the below code:

+
mongoose.set('debug', true);
+
+const userSchema = new Schema({
+  name: String,
+}, { timestamps: true });
+const User = mongoose.model('User', userSchema);
+
+await User.findOneAndUpdate({}, { name: 'test' });
+
+

You'll see the below output from Mongoose debug mode:

+
Mongoose: users.findOneAndUpdate({}, { '$setOnInsert': { createdAt: new Date("Sun, 27 Feb 2022 00:26:27 GMT") }, '$set': { updatedAt: new Date("Sun, 27 Feb 2022 00:26:27 GMT"), name: 'test' }}, {...})
+
+

Notice the $setOnInsert for createdAt and $set for updatedAt. +MongoDB's $setOnInsert operator applies the update only if a new document is upserted. +So, for example, if you want to only set updatedAt if the document if a new document is created, you can disable the updatedAt timestamp and set it yourself as shown below:

+
await User.findOneAndUpdate({}, { $setOnInsert: { updatedAt: new Date() } }, {
+  timestamps: { createdAt: true, updatedAt: false }
+});
+
+
\ No newline at end of file diff --git a/docs/5.x/docs/typescript/statics-and-methods.html b/docs/5.x/docs/typescript/statics-and-methods.html new file mode 100644 index 00000000000..2fc8bb2cfda --- /dev/null +++ b/docs/5.x/docs/typescript/statics-and-methods.html @@ -0,0 +1,142 @@ +Mongoose v6.8.1: Mongoose: Statics and Methods in TypeScript
+ +

+ + Statics and Methods in TypeScript + +

+ + + + +

You can define instance methods and static functions on Mongoose models. +With a little extra configuration, you can also register methods and statics in TypeScript.

+

+ + Methods + +

+

To define an instance method in TypeScript, create a new interface representing your instance methods. +You need to pass that interface as the 3rd generic parameter to the Schema constructor and as the 3rd generic parameter to Model as shown below.

+
import { Model, Schema, model } from 'mongoose';
+
+interface IUser {
+  firstName: string;
+  lastName: string;
+}
+
+// Put all user instance methods in this interface:
+interface IUserMethods {
+  fullName(): string;
+}
+
+// Create a new Model type that knows about IUserMethods...
+type UserModel = Model<IUser, {}, IUserMethods>;
+
+// And a schema that knows about IUserMethods
+const schema = new Schema<IUser, UserModel, IUserMethods>({
+  firstName: { type: String, required: true },
+  lastName: { type: String, required: true }
+});
+schema.method('fullName', function fullName() {
+  return this.firstName + ' ' + this.lastName;
+});
+
+const User = model<IUser, UserModel>('User', schema);
+
+const user = new User({ firstName: 'Jean-Luc', lastName: 'Picard' });
+const fullName: string = user.fullName(); // 'Jean-Luc Picard'
+
+

+ + Statics + +

+

Mongoose models do not have an explicit generic parameter for statics. +If your model has statics, we recommend creating an interface that extends Mongoose's Model interface as shown below.

+
import { Model, Schema, model } from 'mongoose';
+
+interface IUser {
+  name: string;
+}
+
+interface UserModel extends Model<IUser> {
+  myStaticMethod(): number;
+}
+
+const schema = new Schema<IUser, UserModel>({ name: String });
+schema.static('myStaticMethod', function myStaticMethod() {
+  return 42;
+});
+
+const User = model<IUser, UserModel>('User', schema);
+
+const answer: number = User.myStaticMethod(); // 42
+
+

Mongoose does support auto typed static functions now that it is supplied in schema options. +Statics functions can be defined as following:

+
import { Schema, model } from 'mongoose';
+
+const schema = new Schema(
+  { name: String },
+  {
+    statics: {
+      myStaticMethod() {
+        return 42;
+      },
+    },
+  }
+);
+
+const User = model('User', schema);
+
+const answer = User.myStaticMethod(); // 42
+
+

+ + Both Methods and Statics + +

+

Below is how you can define a model that has both methods and statics.

+
import { Model, Schema, HydratedDocument, model } from 'mongoose';
+
+interface IUser {
+  firstName: string;
+  lastName: string;
+}
+
+interface IUserMethods {
+  fullName(): string;
+}
+
+interface UserModel extends Model<IUser, {}, IUserMethods> {
+  createWithFullName(name: string): Promise<HydratedDocument<IUser, IUserMethods>>;
+}
+
+const schema = new Schema<IUser, UserModel, IUserMethods>({
+  firstName: { type: String, required: true },
+  lastName: { type: String, required: true }
+});
+schema.static('createWithFullName', function createWithFullName(name: string) {
+  const [firstName, lastName] = name.split(' ');
+  return this.create({ firstName, lastName });
+});
+schema.method('fullName', function fullName(): string {
+  return this.firstName + ' ' + this.lastName;
+});
+
+const User = model<IUser, UserModel>('User', schema);
+
+User.createWithFullName('Jean-Luc Picard').then(doc => {
+  console.log(doc.firstName); // 'Jean-Luc'
+  doc.fullName(); // 'Jean-Luc Picard'
+});
+
+
\ No newline at end of file diff --git a/docs/5.x/docs/typescript/subdocuments.html b/docs/5.x/docs/typescript/subdocuments.html new file mode 100644 index 00000000000..e2a148fe3e7 --- /dev/null +++ b/docs/5.x/docs/typescript/subdocuments.html @@ -0,0 +1,96 @@ +Mongoose v6.8.1: Mongoose: Handling Subdocuments in TypeScript
+ +

+ + Handling Subdocuments in TypeScript + +

+ + + + +

Subdocuments are tricky in TypeScript. +By default, Mongoose treats object properties in document interfaces as nested properties rather than subdocuments.

+
// Setup
+import { Schema, Types, model, Model } from 'mongoose';
+
+// Subdocument definition
+interface Names {
+  _id: Types.ObjectId;
+  firstName: string;
+}
+
+// Document definition
+interface User {
+  names: Names;
+}
+
+// Models and schemas
+type UserModelType = Model<User>;
+const userSchema = new Schema<User, UserModelType>({
+  names: new Schema<Names>({ firstName: String })
+});
+const UserModel = model<User, UserModelType>('User', userSchema);
+
+// Create a new document:
+const doc = new UserModel({ names: { _id: '0'.repeat(24), firstName: 'foo' } });
+
+// "Property 'ownerDocument' does not exist on type 'Names'."
+// Means that `doc.names` is not a subdocument!
+doc.names.ownerDocument();
+
+

Mongoose provides a mechanism to override types in the hydrated document. +The 3rd generic param to the Model<> is called TMethodsAndOverrides: originally it was just used to define methods, but you can also use it to override types as shown below.

+
// Define property overrides for hydrated documents
+type UserDocumentOverrides = {
+  names: Types.Subdocument<Types.ObjectId> & Names;
+};
+type UserModelType = Model<User, {}, UserDocumentOverrides>;
+
+const userSchema = new Schema<User, UserModelType>({
+  names: new Schema<Names>({ firstName: String })
+});
+const UserModel = model<User, UserModelType>('User', userSchema);
+
+
+const doc = new UserModel({ names: { _id: '0'.repeat(24), firstName: 'foo' } });
+doc.names.ownerDocument(); // Works, `names` is a subdocument!
+
+

+ + Subdocument Arrays + +

+

You can also override arrays to properly type subdocument arrays using TMethodsAndOverrides:

+
// Subdocument definition
+interface Names {
+  _id: Types.ObjectId;
+  firstName: string;
+}
+// Document definition
+interface User {
+  names: Names[];
+}
+
+// TMethodsAndOverrides
+type UserDocumentProps = {
+  names: Types.DocumentArray<Names>;
+};
+type UserModelType = Model<User, {}, UserDocumentProps>;
+
+// Create model
+const UserModel = model<User, UserModelType>('User', new Schema<User, UserModelType>({
+  names: [new Schema<Names>({ firstName: String })]
+}));
+
+const doc = new UserModel({});
+doc.names[0].ownerDocument(); // Works!
+
+
\ No newline at end of file diff --git a/docs/5.x/docs/typescript/virtuals.html b/docs/5.x/docs/typescript/virtuals.html new file mode 100644 index 00000000000..1f72283fae7 --- /dev/null +++ b/docs/5.x/docs/typescript/virtuals.html @@ -0,0 +1,77 @@ +Mongoose v6.8.1: Mongoose: Virtuals in TypeScript
+ +

+ + Virtuals in TypeScript + +

+ + + + +

Virtuals are computed properties: you can access virtuals on hydrated Mongoose documents, but virtuals are not stored in MongoDB. +Mongoose supports auto typed virtuals so you don't need to define additional typescript interface anymore but you are still able to do so.

+

+ + Automatically Inferred Types: + +

+

To make mongoose able to infer virtuals type, You have to define them in schema constructor as following:

+
import { Schema, Model, model } from 'mongoose';
+
+const schema = new Schema(
+  {
+    firstName: String,
+    lastName: String,
+  },
+  {
+    virtuals:{
+      fullName:{
+        get(){
+          return `${this.firstName} ${this.lastName}`;
+        }
+        // virtual setter and options can be defined here as well.
+      }
+    }
+  }
+);
+
+

+ + Set virtuals type manually: + +

+

You shouldn't define virtuals in your TypeScript document interface. +Instead, you should define a separate interface for your virtuals, and pass this interface to Model and Schema.

+

For example, suppose you have a UserDoc interface, and you want to define a fullName virtual. +Below is how you can define a separate UserVirtuals interface for fullName.

+
import { Schema, Model, model } from 'mongoose';
+
+interface UserDoc {
+  firstName: string;
+  lastName: string;
+}
+
+interface UserVirtuals {
+  fullName: string;
+}
+
+type UserModel = Model<UserDoc, {}, UserVirtuals>; // <-- add virtuals here...
+
+const schema = new Schema<UserDoc, UserModel, UserVirtuals>({ // <-- and here
+  firstName: String,
+  lastName: String
+});
+
+schema.virtual('fullName').get(function() {
+  return `${this.firstName} ${this.lastName}`;
+});
+
+
\ No newline at end of file