Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: Automattic/mongoose
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: df01ba6bdff9cae17697b72b0178492237a776bc
Choose a base ref
...
head repository: Automattic/mongoose
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 8846d2918184c1598118a2681375af8cee61604c
Choose a head ref
  • 20 commits
  • 13 files changed
  • 3 contributors

Commits on Oct 4, 2022

  1. chore(mocha-fixtures): fix spelling and remove "example" comments (#1…

    …2511)
    
    * chore(mocha-fixtures): fix spelling and remove "example" comments
    
    also use "MONGOOSE_REPLSET_URI" in the false case
    
    * Update test/mocha-fixtures.js
    
    * chore(mocha-fixtures): update comment to be more descriptive
    
    Co-authored-by: Hafez <a.hafez852@gmail.com>
    hasezoey and AbdelrahmanHafez authored Oct 4, 2022

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    94b308e View commit details
  2. fix(types): add "estimatedDocumentCount" and "countDocuments" as poss…

    …ible hooks
    
    plus tests
    
    fixes #12516
    hasezoey committed Oct 4, 2022

    Verified

    This commit was signed with the committer’s verified signature.
    hasezoey hasezoey
    Copy the full SHA
    ed3a7df View commit details
  3. docs(change-streams): remove unnecessary obsolete comment about needi…

    …ng to use mongodb driver change streams
    
    Fix #12444
    vkarpov15 committed Oct 4, 2022
    Copy the full SHA
    b4aaf62 View commit details
  4. Copy the full SHA
    e800193 View commit details
  5. Merge pull request #12519 from hasezoey/addQueryMiddleware

    fix(types): add "estimatedDocumentCount" and "countDocuments" as possible hooks
    vkarpov15 authored Oct 4, 2022

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    140524c View commit details
  6. Copy the full SHA
    a15242d View commit details
  7. Copy the full SHA
    167f682 View commit details

Commits on Oct 5, 2022

  1. Copy the full SHA
    b0edca9 View commit details
  2. Copy the full SHA
    9093f7c View commit details
  3. Copy the full SHA
    630bd37 View commit details
  4. Merge pull request #12523 from Automattic/vkarpov15/gh-12515

    fix(document): set defaults on subdocuments underneath init-ed single nested subdocument
    vkarpov15 authored Oct 5, 2022

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    542ce79 View commit details
  5. Merge pull request #12522 from Automattic/vkarpov15/gh-12457

    fix(types): indicate that Schema.prototype.discriminator() returns `this`
    vkarpov15 authored Oct 5, 2022

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    2b5c79e View commit details
  6. Merge pull request #12521 from Automattic/vkarpov15/gh-12398

    docs(subdocs): clarify that populated docs are not subdocs
    vkarpov15 authored Oct 5, 2022

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    eb292d2 View commit details
  7. Copy the full SHA
    2f22919 View commit details
  8. Update docs/models.md

    Co-authored-by: hasezoey <hasezoey@gmail.com>
    vkarpov15 and hasezoey authored Oct 5, 2022

    Partially verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    We cannot verify signatures from co-authors, and some of the co-authors attributed to this commit require their commits to be signed.
    Copy the full SHA
    2691f74 View commit details
  9. Merge pull request #12526 from Automattic/vkarpov15/gh-5694

    docs(models): add section on MongoDB Views
    vkarpov15 authored Oct 5, 2022

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    7573398 View commit details
  10. Verified

    This commit was signed with the committer’s verified signature.
    hasezoey hasezoey
    Copy the full SHA
    c10174c View commit details
  11. Merge pull request #12528 from hasezoey/fixClosingTag

    docs(models): fix non-matching closing tag
    vkarpov15 authored Oct 5, 2022

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    81ac6b7 View commit details
  12. Merge pull request #12527 from Automattic/vkarpov15/gh-12514

    fix: make Jest fake timers check more robust to other libs that overwrite time functions
    vkarpov15 authored Oct 5, 2022

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    573e5bc View commit details
  13. chore: release 6.6.5

    vkarpov15 committed Oct 5, 2022
    Copy the full SHA
    8846d29 View commit details
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
6.6.5 / 2022-10-05
==================
* fix(document): set defaults on subdocuments underneath init-ed single nested subdocument #12523 #12515
* fix: make Jest fake timers check more robust to other libs that overwrite time functions #12527 #12514
* fix(types): indicate that Schema.prototype.discriminator() returns this #12522 #12457
* fix(types): add "estimatedDocumentCount" and "countDocuments" as possible hooks #12519 #12516
* docs(models): add section on MongoDB Views #12526 #5694
* docs(subdocs): clarify that populated docs are not subdocs #12521 #12398
* docs(change-streams): remove unnecessary obsolete comment about needing to use mongodb driver change streams #12444

6.6.4 / 2022-10-03
==================
* fix(model): avoid saving applied defaults if path is deselected #12506 #12414
4 changes: 1 addition & 3 deletions docs/change-streams.md
Original file line number Diff line number Diff line change
@@ -65,9 +65,7 @@ exports.handler = async (event, context) => {
context.callbackWaitsForEmptyEventLoop = false;

await connectToDatabase();

// Use MongoDB Node driver's `watch()` function, because Mongoose change streams
// don't support `next()` yet. See https://github.com/Automattic/mongoose/issues/11527

const changeStream = await Country.watch([], { resumeAfter });

// Change stream `next()` will wait forever if there are no changes. So make sure to
69 changes: 58 additions & 11 deletions docs/models.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
## Models
# Models

[Models](./api.html#model-js) are fancy constructors compiled from
`Schema` definitions. An instance of a model is called a
@@ -11,8 +11,9 @@ reading documents from the underlying MongoDB database.
* [Deleting](#deleting)
* [Updating](#updating)
* [Change Streams](#change-streams)
* [Views](#views)

<h3 id="compiling"><a href="#compiling">Compiling your first model</a></h3>
<h2 id="compiling"><a href="#compiling">Compiling your first model</a></h2>

When you call `mongoose.model()` on a schema, Mongoose _compiles_ a model
for you.
@@ -31,7 +32,7 @@ in the database.
you've added everything you want to `schema`, including hooks,
before calling `.model()`!

### Constructing Documents
## Constructing Documents

An instance of a model is called a [document](./documents.html). Creating
them and saving to the database is easy.
@@ -73,7 +74,7 @@ const connection = mongoose.createConnection('mongodb://localhost:27017/test');
const Tank = connection.model('Tank', yourSchema);
```

### Querying
## Querying

Finding documents is easy with Mongoose, which supports the [rich](http://www.mongodb.org/display/DOCS/Advanced+Queries) query syntax of MongoDB. Documents can be retrieved using a `model`'s [find](./api.html#model_Model-find), [findById](./api.html#model_Model-findById), [findOne](./api.html#model_Model-findOne), or [where](./api.html#model_Model-where) static methods.

@@ -83,7 +84,7 @@ Tank.find({ size: 'small' }).where('createdDate').gt(oneYearAgo).exec(callback);

See the chapter on [queries](./queries.html) for more details on how to use the [Query](./api.html#query-js) api.

### Deleting
## Deleting

Models have static `deleteOne()` and `deleteMany()` functions
for removing all documents matching the given `filter`.
@@ -95,7 +96,7 @@ Tank.deleteOne({ size: 'large' }, function (err) {
});
```

### Updating
## Updating

Each `model` has its own `update` method for modifying documents in the
database without returning them to your application. See the
@@ -112,9 +113,7 @@ _If you want to update a single document in the db and return it to your
application, use [findOneAndUpdate](./api.html#model_Model-findOneAndUpdate)
instead._

### Change Streams

_New in MongoDB 3.6.0 and Mongoose 5.0.0_
## Change Streams

[Change streams](https://docs.mongodb.com/manual/changeStreams/) provide
a way for you to listen to all inserts and updates going through your
@@ -154,10 +153,58 @@ The output from the above [async function](http://thecodebarbarian.com/80-20-gui

You can read more about [change streams in mongoose in this blog post](http://thecodebarbarian.com/a-nodejs-perspective-on-mongodb-36-change-streams.html#change-streams-in-mongoose).

### Yet more
## Views

[MongoDB Views](https://www.mongodb.com/docs/manual/core/views) are essentially read-only collections that contain data computed from other collections using [aggregations](./api/aggregate.html).
In Mongoose, you should define a separate Model for each of your Views.
You can also create a View using [`createCollection()`](./api/model.html#model_Model-createCollection).

The following example shows how you can create a new `RedactedUser` View on a `User` Model that hides potentially sensitive information, like name and email.

```javascript
// Make sure to disable `autoCreate` and `autoIndex` for Views,
// because you want to create the collection manually.
const userSchema = new Schema({
name: String,
email: String,
roles: [String]
}, { autoCreate: false, autoIndex: false });
const User = mongoose.model('User', userSchema);

const RedactedUser = mongoose.model('RedactedUser', userSchema);

// First, create the User model's underlying collection...
await User.createCollection();
// Then create the `RedactedUser` model's underlying collection
// as a View.
await RedactedUser.createCollection({
viewOn: 'users', // Set `viewOn` to the collection name, **not** model name.
pipeline: [
{
$set: {
name: { $concat: [{ $substr: ['$name', 0, 3] }, '...'] },
email: { $concat: [{ $substr: ['$email', 0, 3] }, '...'] }
}
}
]
});

await User.create([
{ name: 'John Smith', email: 'john.smith@gmail.com', roles: ['user'] },
{ name: 'Bill James', email: 'bill@acme.co', roles: ['user', 'admin'] }
]);

// [{ _id: ..., name: 'Bil...', email: 'bil...', roles: ['user', 'admin'] }]
console.log(await RedactedUser.find({ roles: 'admin' }));
```

Note that Mongoose does **not** currently enforce that Views are read-only.
If you attempt to `save()` a document from a View, you will get an error from the MongoDB server.

## Yet more

The [API docs](./api.html#model_Model) cover many additional methods available like [count](./api.html#model_Model-count), [mapReduce](./api.html#model_Model-mapReduce), [aggregate](./api.html#model_Model-aggregate), and [more](./api.html#model_Model-findOneAndRemove).

### Next Up
## Next Up

Now that we've covered `Models`, let's take a look at [Documents](/docs/documents.html).
26 changes: 21 additions & 5 deletions docs/subdocs.md
Original file line number Diff line number Diff line change
@@ -11,15 +11,31 @@ const childSchema = new Schema({ name: 'string' });
const parentSchema = new Schema({
// Array of subdocuments
children: [childSchema],
// Single nested subdocuments. Caveat: single nested subdocs only work
// in mongoose >= 4.2.0
// Single nested subdocuments
child: childSchema
});
```

Aside from code reuse, one important reason to use subdocuments is to create
a path where there would otherwise not be one to allow for validation over
a group of fields (e.g. dateRange.fromDate <= dateRange.toDate).
Note that populated documents are **not** subdocuments in Mongoose.
Subdocument data is embedded in the top-level document.
Referenced documents are separate top-level documents.

```javascript
const childSchema = new Schema({ name: 'string' });
const Child = mongoose.model('Child', childSchema);

const parentSchema = new Schema({
child: {
type: mongoose.ObjectId,
ref: 'Child'
}
});
const Parent = mongoose.model('Parent', parentSchema);

const doc = await Parent.findOne().populate('child');
// NOT a subdocument. `doc.child` is a separate top-level document.
doc.child;
```

<ul class="toc">
<li><a href="#what-is-a-subdocument-">What is a Subdocument?</a></li>
2 changes: 1 addition & 1 deletion lib/helpers/printJestWarning.js
Original file line number Diff line number Diff line change
@@ -9,7 +9,7 @@ if (typeof jest !== 'undefined' && typeof window !== 'undefined') {
'https://mongoosejs.com/docs/jest.html');
}

if (typeof jest !== 'undefined' && process.nextTick.toString().indexOf('nextTick') === -1) {
if (typeof jest !== 'undefined' && setTimeout.clock != null && typeof setTimeout.clock.Date === 'function') {
utils.warn('Mongoose: looks like you\'re trying to test a Mongoose app ' +
'with Jest\'s mock timers enabled. Please make sure you read ' +
'Mongoose\'s docs on configuring Jest to test Node.js apps: ' +
1 change: 1 addition & 0 deletions lib/schema/SubdocumentPath.js
Original file line number Diff line number Diff line change
@@ -172,6 +172,7 @@ SubdocumentPath.prototype.cast = function(val, doc, init, priorVal, options) {
options = Object.assign({}, options, { priorDoc: priorVal });
if (init) {
subdoc = new Constructor(void 0, selected, doc, false, { defaults: false });
delete subdoc.$__.defaults;
subdoc.$init(val);
applyDefaults(subdoc, selected);
} else {
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "mongoose",
"description": "Mongoose MongoDB ODM",
"version": "6.6.4",
"version": "6.6.5",
"author": "Guillermo Rauch <guillermo@learnboost.com>",
"keywords": [
"mongodb",
29 changes: 29 additions & 0 deletions test/document.test.js
Original file line number Diff line number Diff line change
@@ -11865,6 +11865,35 @@ describe('document', function() {
assert.strictEqual(called, 1);
});

it('applies defaults to pushed subdocs after initing document (gh-12515)', async function() {
const animalSchema = new Schema({ title: String });
const animalsSchema = new Schema({
species: [animalSchema],
totalAnimals: Number
});
const Userschema = new Schema({
animals: animalsSchema
});
const UserModel = db.model('User', Userschema);

const doc = new UserModel();
doc.animals = { totalAnimals: 1 };
doc.animals.species = [{ title: 'Lion' }];
await doc.save();
// once created we fetch it again
let user = await UserModel.findById(doc._id);

// add new animal
user.animals.species.push({ title: 'Elephant' });
await user.save();
assert.ok(user.animals.species[0]._id);
assert.ok(user.animals.species[1]._id);
user = await UserModel.collection.findOne({ _id: user._id });

assert.ok(user.animals.species[0]._id);
assert.ok(user.animals.species[1]._id);
});

it('If the field does not exist, $inc should create it and set is value to the specified one (gh-12435)', async function() {
const schema = new mongoose.Schema({
name: String,
10 changes: 5 additions & 5 deletions test/mocha-fixtures.js
Original file line number Diff line number Diff line change
@@ -12,7 +12,7 @@ let mongoinstance;
// a replset-instance for running repl-set tests
let mongorreplset;

// decide wheter to start a in-memory or not
// decide whether to start MMS instances or use the existing URI's
const startMemoryInstance = !process.env.MONGOOSE_TEST_URI;
const startMemoryReplset = !process.env.MONGOOSE_REPLSET_URI;

@@ -24,11 +24,11 @@ module.exports.mochaGlobalSetup = async function mochaGlobalSetup() {

// set some options when running in a CI
if (process.env.CI) {
// this option is also set in the github-ci tests.yml, but this is just to ensure it is in any CI
process.env.MONGOMS_PREFER_GLOBAL_PATH = '1'; // set MMS to use "~/.cache/mongodb-binaries" even when the path does not yet exist
}

if (startMemoryInstance) { // Config to decided if an mongodb-memory-server instance should be used
// it's needed in global space, because we don't want to create a new instance every test-suite
if (startMemoryInstance) {
mongoinstance = await mms.MongoMemoryServer.create({ instance: { args: ['--setParameter', 'ttlMonitorSleepSecs=1'], storageEngine: 'wiredTiger' } });
instanceuri = mongoinstance.getUri();
} else {
@@ -39,15 +39,15 @@ module.exports.mochaGlobalSetup = async function mochaGlobalSetup() {
mongorreplset = await mms.MongoMemoryReplSet.create({ replSet: { count: 3, args: ['--setParameter', 'ttlMonitorSleepSecs=1'], storageEngine: 'wiredTiger' } }); // using 3 because even numbers can lead to vote problems
replseturi = mongorreplset.getUri();
} else {
replseturi = '';
replseturi = process.env.MONGOOSE_REPLSET_URI;
}

process.env.MONGOOSE_TEST_URI = instanceuri;
process.env.MONGOOSE_REPLSET_URI = replseturi;
};

module.exports.mochaGlobalTeardown = async function mochaGlobalTeardown() {
if (mongoinstance) { // Config to decided if an mongodb-memory-server instance should be used
if (mongoinstance) {
await mongoinstance.stop();
}
if (mongorreplset) {
18 changes: 18 additions & 0 deletions test/types/middleware.test.ts
Original file line number Diff line number Diff line change
@@ -92,6 +92,24 @@ schema.pre<Model<ITest>>('insertMany', function(next, docs: Array<ITest>) {
next();
});

schema.pre<Query<number, any>>('count', function(next) {});
schema.post<Query<number, any>>('count', function(count, next) {
expectType<number>(count);
next();
});

schema.pre<Query<number, any>>('estimatedDocumentCount', function(next) {});
schema.post<Query<number, any>>('estimatedDocumentCount', function(count, next) {
expectType<number>(count);
next();
});

schema.pre<Query<number, any>>('countDocuments', function(next) {});
schema.post<Query<number, any>>('countDocuments', function(count, next) {
expectType<number>(count);
next();
});

schema.post<Query<ITest, ITest>>('findOneAndDelete', function(res, next) {
expectType<ITest>(res);
next();
8 changes: 2 additions & 6 deletions test/types/schema.test.ts
Original file line number Diff line number Diff line change
@@ -570,9 +570,7 @@ export type AutoTypedSchemaType = {
// discriminator
const eventSchema = new Schema<{ message: string }>({ message: String }, { discriminatorKey: 'kind' });
const batchSchema = new Schema<{ name: string }>({ name: String }, { discriminatorKey: 'kind' });
const discriminatedSchema = batchSchema.discriminator('event', eventSchema);

expectType<Schema<Omit<{ name: string }, 'message'> & { message: string }>>(discriminatedSchema);
batchSchema.discriminator('event', eventSchema);

// discriminator statics
const eventSchema2 = new Schema({ message: String }, { discriminatorKey: 'kind', statics: { static1: function() {
@@ -581,9 +579,7 @@ const eventSchema2 = new Schema({ message: String }, { discriminatorKey: 'kind',
const batchSchema2 = new Schema({ name: String }, { discriminatorKey: 'kind', statics: { static2: function() {
return 1;
} } });
const discriminatedSchema2 = batchSchema2.discriminator('event', eventSchema2);

expectAssignable<Schema<Omit<{ name: string }, 'message'> & { message: string }, Model<any>, {}, {}, {}, { static1(): number; static2(): number; }>>(discriminatedSchema2);
batchSchema2.discriminator('event', eventSchema2);

function gh11828() {
interface IUser {
2 changes: 1 addition & 1 deletion types/index.d.ts
Original file line number Diff line number Diff line change
@@ -208,7 +208,7 @@ declare module 'mongoose' {
/** Returns a copy of this schema */
clone<T = this>(): T;

discriminator<DisSchema = Schema>(name: string, schema: DisSchema): DiscriminatorSchema<DocType, M, TInstanceMethods, TQueryHelpers, TVirtuals, TStaticMethods, DisSchema>;
discriminator<DisSchema = Schema>(name: string, schema: DisSchema): this;

/** Returns a new schema that has the picked `paths` from this schema. */
pick<T = this>(paths: string[], options?: SchemaOptions): T;
2 changes: 1 addition & 1 deletion types/middlewares.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
declare module 'mongoose' {

type MongooseDocumentMiddleware = 'validate' | 'save' | 'remove' | 'updateOne' | 'deleteOne' | 'init';
type MongooseQueryMiddleware = 'count' | 'deleteMany' | 'deleteOne' | 'distinct' | 'find' | 'findOne' | 'findOneAndDelete' | 'findOneAndRemove' | 'findOneAndReplace' | 'findOneAndUpdate' | 'remove' | 'replaceOne' | 'update' | 'updateOne' | 'updateMany';
type MongooseQueryMiddleware = 'count' | 'estimatedDocumentCount' | 'countDocuments' | 'deleteMany' | 'deleteOne' | 'distinct' | 'find' | 'findOne' | 'findOneAndDelete' | 'findOneAndRemove' | 'findOneAndReplace' | 'findOneAndUpdate' | 'remove' | 'replaceOne' | 'update' | 'updateOne' | 'updateMany';

type MiddlewareOptions = { document?: boolean, query?: boolean };
type SchemaPreOptions = MiddlewareOptions;