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/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 = 0; k < val.length; ++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] != null && typeof projection[key] === 'object') ?
26
- isExclusive(projection[key]) :
26
+ exclude = isPOJO(projection[key]) ?
27
+ (isExclusive(projection[key]) ?? exclude) :
27
28
  !projection[key];
28
- break;
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] != null && typeof projection[prop] === 'object') {
30
+ if (isPOJO(projection[prop])) {
30
31
  return isInclusive(projection[prop]);
31
32
  } else {
32
33
  return !!projection[prop];
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mongoose",
3
3
  "description": "Mongoose MongoDB ODM",
4
- "version": "7.8.1",
4
+ "version": "7.8.2",
5
5
  "author": "Guillermo Rauch <guillermo@learnboost.com>",
6
6
  "keywords": [
7
7
  "mongodb",
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'));