Skip to content

Commit

Permalink
Merge pull request #9321 from Automattic/5.10
Browse files Browse the repository at this point in the history
5.10
  • Loading branch information
vkarpov15 committed Aug 14, 2020
2 parents 4f833ff + 7646d9e commit 8b8fea6
Show file tree
Hide file tree
Showing 36 changed files with 1,199 additions and 188 deletions.
72 changes: 69 additions & 3 deletions docs/guide.pug
Expand Up @@ -434,6 +434,7 @@ block content
- [useNestedStrict](#useNestedStrict)
- [validateBeforeSave](#validateBeforeSave)
- [versionKey](#versionKey)
- [optimisticConcurrency](#optimisticConcurrency)
- [collation](#collation)
- [selectPopulatedPaths](#selectPopulatedPaths)
- [skipVersioning](#skipVersioning)
Expand Down Expand Up @@ -891,9 +892,8 @@ block content
thing.save(); // { _somethingElse: 0, name: 'mongoose v3' }
```

Note that Mongoose versioning is **not** a full [optimistic concurrency](https://en.wikipedia.org/wiki/Optimistic_concurrency_control)
solution. Use [mongoose-update-if-current](https://github.com/eoin-obrien/mongoose-update-if-current)
for OCC support. Mongoose versioning only operates on arrays:
Note that Mongoose's default versioning is **not** a full [optimistic concurrency](https://en.wikipedia.org/wiki/Optimistic_concurrency_control)
solution. Mongoose's default versioning only operates on arrays as shown below.

```javascript
// 2 copies of the same document
Expand All @@ -911,6 +911,8 @@ block content
await doc2.save();
```

If you need optimistic concurrency support for `save()`, you can set the [`optimisticConcurrency` option](#optimisticConcurrency)

Document versioning can also be disabled by setting the `versionKey` to
`false`.
_DO NOT disable versioning unless you [know what you are doing](http://aaronheckmann.blogspot.com/2012/06/mongoose-v3-part-1-versioning.html)._
Expand Down Expand Up @@ -946,6 +948,70 @@ block content
});
```

<h3 id="optimisticConcurrency"><a href="#optimisticConcurrency">option: optimisticConcurrency</a></h3>

[Optimistic concurrency](https://en.wikipedia.org/wiki/Optimistic_concurrency_control) is a strategy to ensure
the document you're updating didn't change between when you loaded it using `find()` or `findOne()`, and when
you update it using `save()`.

For example, suppose you have a `House` model that contains a list of `photos`, and a `status` that represents
whether this house shows up in searches. Suppose that a house that has status `'APPROVED'` must have at least
two `photos`. You might implement the logic of approving a house document as shown below:

```javascript
async function markApproved(id) {
const house = await House.findOne({ _id });
if (house.photos.length < 2) {
throw new Error('House must have at least two photos!');
}

house.status = 'APPROVED';
await house.save();
}
```

The `markApproved()` function looks right in isolation, but there might be a potential issue: what if another
function removes the house's photos between the `findOne()` call and the `save()` call? For example, the below
code will succeed:

```javascript
const house = await House.findOne({ _id });
if (house.photos.length < 2) {
throw new Error('House must have at least two photos!');
}

const house2 = await House.findOne({ _id });
house2.photos = [];
await house2.save();

// Marks the house as 'APPROVED' even though it has 0 photos!
house.status = 'APPROVED';
await house.save();
```

If you set the `optimisticConcurrency` option on the `House` model's schema, the above script will throw an
error.

```javascript
const House = mongoose.model('House', Schema({
status: String,
photos: [String]
}, { optimisticConcurrency: true }));

const house = await House.findOne({ _id });
if (house.photos.length < 2) {
throw new Error('House must have at least two photos!');
}

const house2 = await House.findOne({ _id });
house2.photos = [];
await house2.save();

// Throws 'VersionError: No matching document found for id "..." version 0'
house.status = 'APPROVED';
await house.save();
```

<h3 id="collation"><a href="#collation">option: collation</a></h3>

Sets a default [collation](https://docs.mongodb.com/manual/reference/collation/)
Expand Down
26 changes: 26 additions & 0 deletions lib/aggregate.js
Expand Up @@ -909,6 +909,32 @@ Aggregate.prototype.facet = function(options) {
return this.append({ $facet: options });
};

/**
* Helper for [Atlas Text Search](https://docs.atlas.mongodb.com/reference/atlas-search/tutorial/)'s
* `$search` stage.
*
* ####Example:
*
* Model.aggregate().
* search({
* text: {
* query: 'baseball',
* path: 'plot'
* }
* });
*
* // Output: [{ plot: '...', title: '...' }]
*
* @param {Object} $search options
* @return {Aggregate} this
* @see $search https://docs.atlas.mongodb.com/reference/atlas-search/tutorial/
* @api public
*/

Aggregate.prototype.search = function(options) {
return this.append({ $search: options });
};

/**
* Returns the current pipeline
*
Expand Down
5 changes: 5 additions & 0 deletions lib/cast.js
Expand Up @@ -9,6 +9,7 @@ const StrictModeError = require('./error/strict');
const Types = require('./schema/index');
const castTextSearch = require('./schema/operators/text');
const get = require('./helpers/get');
const getSchemaDiscriminatorByValue = require('./helpers/discriminator/getSchemaDiscriminatorByValue');
const isOperator = require('./helpers/query/isOperator');
const util = require('util');
const isObject = require('./helpers/isObject');
Expand Down Expand Up @@ -42,6 +43,10 @@ module.exports = function cast(schema, obj, options, context) {
delete obj._bsontype;
}

if (schema != null && schema.discriminators != null && obj[schema.options.discriminatorKey] != null) {
schema = getSchemaDiscriminatorByValue(schema, obj[schema.options.discriminatorKey]) || schema;
}

const paths = Object.keys(obj);
let i = paths.length;
let _keys;
Expand Down

0 comments on commit 8b8fea6

Please sign in to comment.