mongoose 7.8.1 → 7.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser.umd.js +1 -1
- package/lib/cast.js +11 -1
- package/lib/document.js +1 -1
- package/lib/helpers/projection/isExclusive.js +6 -3
- package/lib/helpers/projection/isInclusive.js +2 -1
- package/package.json +1 -1
- package/sample.md +0 -133
- package/test.js +0 -43
package/lib/cast.js
CHANGED
|
@@ -65,10 +65,11 @@ module.exports = function cast(schema, obj, options, context) {
|
|
|
65
65
|
if (!Array.isArray(val)) {
|
|
66
66
|
throw new CastError('Array', val, path);
|
|
67
67
|
}
|
|
68
|
-
for (let k =
|
|
68
|
+
for (let k = val.length - 1; k >= 0; k--) {
|
|
69
69
|
if (val[k] == null || typeof val[k] !== 'object') {
|
|
70
70
|
throw new CastError('Object', val[k], path + '.' + k);
|
|
71
71
|
}
|
|
72
|
+
const beforeCastKeysLength = Object.keys(val[k]).length;
|
|
72
73
|
const discriminatorValue = val[k][schema.options.discriminatorKey];
|
|
73
74
|
if (discriminatorValue == null) {
|
|
74
75
|
val[k] = cast(schema, val[k], options, context);
|
|
@@ -76,6 +77,15 @@ module.exports = function cast(schema, obj, options, context) {
|
|
|
76
77
|
const discriminatorSchema = getSchemaDiscriminatorByValue(context.schema, discriminatorValue);
|
|
77
78
|
val[k] = cast(discriminatorSchema ? discriminatorSchema : schema, val[k], options, context);
|
|
78
79
|
}
|
|
80
|
+
|
|
81
|
+
if (Object.keys(val[k]).length === 0 && beforeCastKeysLength !== 0) {
|
|
82
|
+
val.splice(k, 1);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// delete empty: {$or: []} -> {}
|
|
87
|
+
if (val.length === 0) {
|
|
88
|
+
delete obj[path];
|
|
79
89
|
}
|
|
80
90
|
} else if (path === '$where') {
|
|
81
91
|
type = typeof val;
|
package/lib/document.js
CHANGED
|
@@ -1204,7 +1204,7 @@ Document.prototype.$set = function $set(path, val, type, options) {
|
|
|
1204
1204
|
this.$__setValue(path, null);
|
|
1205
1205
|
cleanModifiedSubpaths(this, path);
|
|
1206
1206
|
} else {
|
|
1207
|
-
return this.$set(val, path, constructing);
|
|
1207
|
+
return this.$set(val, path, constructing, options);
|
|
1208
1208
|
}
|
|
1209
1209
|
|
|
1210
1210
|
const keys = getKeysInSchemaOrder(this.$__schema, val, path);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const isDefiningProjection = require('./isDefiningProjection');
|
|
4
|
+
const isPOJO = require('../isPOJO');
|
|
4
5
|
|
|
5
6
|
/*!
|
|
6
7
|
* ignore
|
|
@@ -22,10 +23,12 @@ module.exports = function isExclusive(projection) {
|
|
|
22
23
|
// Explicitly avoid `$meta` and `$slice`
|
|
23
24
|
const key = keys[ki];
|
|
24
25
|
if (key !== '_id' && isDefiningProjection(projection[key])) {
|
|
25
|
-
exclude = (projection[key]
|
|
26
|
-
isExclusive(projection[key]) :
|
|
26
|
+
exclude = isPOJO(projection[key]) ?
|
|
27
|
+
(isExclusive(projection[key]) ?? exclude) :
|
|
27
28
|
!projection[key];
|
|
28
|
-
|
|
29
|
+
if (exclude != null) {
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
29
32
|
}
|
|
30
33
|
}
|
|
31
34
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const isDefiningProjection = require('./isDefiningProjection');
|
|
4
|
+
const isPOJO = require('../isPOJO');
|
|
4
5
|
|
|
5
6
|
/*!
|
|
6
7
|
* ignore
|
|
@@ -26,7 +27,7 @@ module.exports = function isInclusive(projection) {
|
|
|
26
27
|
// If field is truthy (1, true, etc.) and not an object, then this
|
|
27
28
|
// projection must be inclusive. If object, assume its $meta, $slice, etc.
|
|
28
29
|
if (isDefiningProjection(projection[prop]) && !!projection[prop]) {
|
|
29
|
-
if (projection[prop]
|
|
30
|
+
if (isPOJO(projection[prop])) {
|
|
30
31
|
return isInclusive(projection[prop]);
|
|
31
32
|
} else {
|
|
32
33
|
return !!projection[prop];
|
package/package.json
CHANGED
package/sample.md
DELETED
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
# Transactions in Mongoose
|
|
2
|
-
|
|
3
|
-
[Transactions](https://www.mongodb.com/transactions) let you execute multiple operations in isolation and potentially undo all the operations if one of them fails.
|
|
4
|
-
This guide will get you started using transactions with Mongoose.
|
|
5
|
-
|
|
6
|
-
## Getting Started with Transactions {#getting-started-with-transactions}
|
|
7
|
-
|
|
8
|
-
If you haven't already, import mongoose:
|
|
9
|
-
|
|
10
|
-
```javascript
|
|
11
|
-
import mongoose from 'mongoose';
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
To create a transaction, you first need to create a session using [`Mongoose#startSession`](api/mongoose.html#mongoose_Mongoose-startSession)
|
|
15
|
-
or [`Connection#startSession()`](api/connection.html#connection_Connection-startSession).
|
|
16
|
-
|
|
17
|
-
```javascript
|
|
18
|
-
// Using Mongoose's default connection
|
|
19
|
-
const session = await mongoose.startSession();
|
|
20
|
-
|
|
21
|
-
// Using custom connection
|
|
22
|
-
const db = await mongoose.createConnection(mongodbUri).asPromise();
|
|
23
|
-
const session = await db.startSession();
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
In practice, you should use either the [`session.withTransaction()` helper](https://mongodb.github.io/node-mongodb-native/3.2/api/ClientSession.html#withTransaction)
|
|
27
|
-
or Mongoose's `Connection#transaction()` function to run a transaction. The `session.withTransaction()` helper handles:
|
|
28
|
-
|
|
29
|
-
* Creating a transaction
|
|
30
|
-
* Committing the transaction if it succeeds
|
|
31
|
-
* Aborting the transaction if your operation throws
|
|
32
|
-
* Retrying in the event of a [transient transaction error](https://stackoverflow.com/questions/52153538/what-is-a-transienttransactionerror-in-mongoose-or-mongodb).
|
|
33
|
-
|
|
34
|
-
```acquit
|
|
35
|
-
[require:transactions.*withTransaction]
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
For more information on the `ClientSession#withTransaction()` function, please see
|
|
39
|
-
[the MongoDB Node.js driver docs](https://mongodb.github.io/node-mongodb-native/3.2/api/ClientSession.html#withTransaction).
|
|
40
|
-
|
|
41
|
-
Mongoose's `Connection#transaction()` function is a wrapper around `withTransaction()` that
|
|
42
|
-
integrates Mongoose change tracking with transactions.
|
|
43
|
-
For example, suppose you `save()` a document in a transaction that later fails.
|
|
44
|
-
The changes in that document are not persisted to MongoDB.
|
|
45
|
-
The `Connection#transaction()` function informs Mongoose change tracking that the `save()` was rolled back, and marks all fields that were changed in the transaction as modified.
|
|
46
|
-
|
|
47
|
-
```javascript
|
|
48
|
-
const doc = new Person({ name: 'Will Riker' });
|
|
49
|
-
|
|
50
|
-
await db.transaction(async function setRank(session) {
|
|
51
|
-
doc.name = 'Captain';
|
|
52
|
-
await doc.save({ session });
|
|
53
|
-
doc.isNew; // false
|
|
54
|
-
|
|
55
|
-
// Throw an error to abort the transaction
|
|
56
|
-
throw new Error('Oops!');
|
|
57
|
-
}, { readPreference: 'primary' }).catch(() => {});
|
|
58
|
-
|
|
59
|
-
// true, `transaction()` reset the document's state because the
|
|
60
|
-
// transaction was aborted.
|
|
61
|
-
doc.isNew;
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
## Note About Parallelism in Transactions {#note-about-parallelism-in-transactions}
|
|
65
|
-
|
|
66
|
-
Running operations in parallel is **not supported** during a transaction. The use of `Promise.all`, `Promise.allSettled`, `Promise.race`, etc. to parallelize operations inside a transaction is
|
|
67
|
-
undefined behaviour and should be avoided.
|
|
68
|
-
|
|
69
|
-
## With Mongoose Documents and `save()` {#with-mongoose-documents-and-save}
|
|
70
|
-
|
|
71
|
-
If you get a [Mongoose document](documents.html) from [`findOne()`](api/model.html#model_Model-findOne)
|
|
72
|
-
or [`find()`](api/model.html#model_Model-find) using a session, the document will
|
|
73
|
-
keep a reference to the session and use that session for [`save()`](api/document.html#document_Document-save).
|
|
74
|
-
|
|
75
|
-
To get/set the session associated with a given document, use [`doc.$session()`](api/document.html#document_Document-$session).
|
|
76
|
-
|
|
77
|
-
```acquit
|
|
78
|
-
[require:transactions.*save]
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
## With the Aggregation Framework {#with-the-aggregation-framework}
|
|
82
|
-
|
|
83
|
-
The `Model.aggregate()` function also supports transactions. Mongoose
|
|
84
|
-
aggregations have a [`session()` helper](api/aggregate.html#aggregate_Aggregate-session)
|
|
85
|
-
that sets the [`session` option](api/aggregate.html#aggregate_Aggregate-option).
|
|
86
|
-
Below is an example of executing an aggregation within a transaction.
|
|
87
|
-
|
|
88
|
-
```acquit
|
|
89
|
-
[require:transactions.*aggregate]
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
## Using AsyncLocalStorage {#asynclocalstorage}
|
|
93
|
-
|
|
94
|
-
One major pain point with transactions in Mongoose is that you need to remember to set the `session` option on every operation.
|
|
95
|
-
If you don't, your operation will execute outside of the transaction.
|
|
96
|
-
Mongoose 8.4 is able to set the `session` operation on all operations within a `Connection.prototype.transaction()` executor function using Node's [AsyncLocalStorage API](https://nodejs.org/api/async_context.html#class-asynclocalstorage).
|
|
97
|
-
Set the `transactionAsyncLocalStorage` option using `mongoose.set('transactionAsyncLocalStorage', true)` to enable this feature.
|
|
98
|
-
|
|
99
|
-
```javascript
|
|
100
|
-
mongoose.set('transactionAsyncLocalStorage', true);
|
|
101
|
-
|
|
102
|
-
const Test = mongoose.model('Test', mongoose.Schema({ name: String }));
|
|
103
|
-
|
|
104
|
-
const doc = new Test({ name: 'test' });
|
|
105
|
-
|
|
106
|
-
// Save a new doc in a transaction that aborts
|
|
107
|
-
await connection.transaction(async() => {
|
|
108
|
-
await doc.save(); // Notice no session here
|
|
109
|
-
throw new Error('Oops');
|
|
110
|
-
}).catch(() => {});
|
|
111
|
-
|
|
112
|
-
// false, `save()` was rolled back
|
|
113
|
-
await Test.exists({ _id: doc._id });
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
With `transactionAsyncLocalStorage`, you no longer need to pass sessions to every operation.
|
|
117
|
-
Mongoose will add the session by default under the hood.
|
|
118
|
-
|
|
119
|
-
## Advanced Usage {#advanced-usage}
|
|
120
|
-
|
|
121
|
-
Advanced users who want more fine-grained control over when they commit or abort transactions
|
|
122
|
-
can use `session.startTransaction()` to start a transaction:
|
|
123
|
-
|
|
124
|
-
```acquit
|
|
125
|
-
[require:transactions.*basic example]
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
You can also use `session.abortTransaction()` to abort a transaction:
|
|
129
|
-
|
|
130
|
-
```acquit
|
|
131
|
-
[require:transactions.*abort]
|
|
132
|
-
```
|
|
133
|
-
|
package/test.js
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
const cheerio = require('cheerio');
|
|
2
|
-
|
|
3
|
-
const handle = (line) => {
|
|
4
|
-
if (line.startsWith('<h2') && line.includes('</h2')) {
|
|
5
|
-
const $ = cheerio.load(line);
|
|
6
|
-
const h2 = $('h2');
|
|
7
|
-
|
|
8
|
-
let textContainer = h2;
|
|
9
|
-
const first = h2.children().first()[0];
|
|
10
|
-
if (first?.type === 'tag' && first?.name === 'a') {
|
|
11
|
-
textContainer = $(first);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
// contents() not children(), because contents() also returns text nodes
|
|
15
|
-
const children = [...textContainer.contents()];
|
|
16
|
-
const text = children.map(el => el.type === 'tag' && el.name === 'code' ? '`' + $(el).text() + '`' : $(el).text()).join('');
|
|
17
|
-
return `## ${text}` + (h2.attr('id') ? ` {#${h2.attr('id')}}` : '');
|
|
18
|
-
}
|
|
19
|
-
if (line.startsWith('<h3') && line.includes('</h3')) {
|
|
20
|
-
const $ = cheerio.load(line);
|
|
21
|
-
const h3 = $('h3');
|
|
22
|
-
|
|
23
|
-
let textContainer = h3;
|
|
24
|
-
const first = h3.children().first()[0];
|
|
25
|
-
if (first?.type === 'tag' && first?.name === 'a') {
|
|
26
|
-
textContainer = $(first);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// contents() not children(), because contents() also returns text nodes
|
|
30
|
-
const children = [...textContainer.contents()];
|
|
31
|
-
const text = children.map(el => el.type === 'tag' && el.name === 'code' ? '`' + $(el).text() + '`' : $(el).text()).join('');
|
|
32
|
-
|
|
33
|
-
return `### ${text}` + (h3.attr('id') ? ` {#${h3.attr('id')}}` : '');
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
return line;
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
const str = require('fs').readFileSync(process.argv[2]).toString('utf8');
|
|
40
|
-
|
|
41
|
-
const lines = str.split('\n');
|
|
42
|
-
|
|
43
|
-
console.log(lines.map(line => handle(line)).join('\n'));
|