Skip to content

Commit 6ec3b48

Browse files
committed
Merge branch 'master' into 9.0
2 parents 70ddfd8 + 7f00685 commit 6ec3b48

File tree

8 files changed

+72
-49
lines changed

8 files changed

+72
-49
lines changed

docs/compatibility.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
}
1212
</style>
1313

14-
Mongoose relies on the [MongoDB Node.js Driver](http://mongodb.github.io/node-mongodb-native/) to talk to MongoDB.
14+
Mongoose relies on the [MongoDB Node.js Driver](http://mongodb.github.io/node-mongodb-native/) to talk to MongoDB.
1515
You can refer to [this table](https://www.mongodb.com/docs/drivers/node/current/compatibility/) for up-to-date information as to which version of the MongoDB driver supports which version of MongoDB.
1616

1717
Below are the [semver](http://semver.org/) ranges representing which versions of mongoose are compatible with the listed versions of MongoDB server.
@@ -32,4 +32,6 @@ Below are the [semver](http://semver.org/) ranges representing which versions of
3232
| `2.6.x` | `^3.8.8 \| ^4.0.0 \| ^5.0.0` |
3333
| `2.4.x` | `^3.8.0 \| ^4.0.0` |
3434

35+
Mongoose `^6.5.0` also works with MongoDB server 7.x. But not all new MongoDB server 7.x features are supported by Mongoose 6.x.
36+
3537
Note that Mongoose `5.x` dropped support for all versions of MongoDB before `3.0.0`. If you need to use MongoDB `2.6` or older, use Mongoose `4.x`.

docs/migrating_to_7.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ They always return promises.
106106
* `Connection.prototype.close`
107107
* `Connection.prototype.destroy`
108108
* `Document.prototype.populate`
109+
* `Document.prototype.save`
109110
* `Document.prototype.validate`
110111
* `Mongoose.prototype.connect`
111112
* `Mongoose.prototype.createConnection`

lib/cursor/queryCursor.js

Lines changed: 37 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ function QueryCursor(query) {
8484
// Max out the number of documents we'll populate in parallel at 5000.
8585
this.options._populateBatchSize = Math.min(this.options.batchSize, 5000);
8686
}
87+
if (query._mongooseOptions._asyncIterator) {
88+
this._mongooseOptions._asyncIterator = true;
89+
}
8790

8891
if (model.collection._shouldBufferCommands() && model.collection.buffer) {
8992
model.collection.queue.push([
@@ -382,29 +385,6 @@ QueryCursor.prototype.addCursorFlag = function(flag, value) {
382385
return this;
383386
};
384387

385-
/*!
386-
* ignore
387-
*/
388-
389-
QueryCursor.prototype.transformNull = function(val) {
390-
if (arguments.length === 0) {
391-
val = true;
392-
}
393-
this._mongooseOptions.transformNull = val;
394-
return this;
395-
};
396-
397-
/*!
398-
* ignore
399-
*/
400-
401-
QueryCursor.prototype._transformForAsyncIterator = function() {
402-
if (this._transforms.indexOf(_transformForAsyncIterator) === -1) {
403-
this.map(_transformForAsyncIterator);
404-
}
405-
return this;
406-
};
407-
408388
/**
409389
* Returns an asyncIterator for use with [`for/await/of` loops](https://thecodebarbarian.com/getting-started-with-async-iterators-in-node-js).
410390
* You do not need to call this function explicitly, the JavaScript runtime
@@ -436,19 +416,13 @@ QueryCursor.prototype._transformForAsyncIterator = function() {
436416
*/
437417

438418
if (Symbol.asyncIterator != null) {
439-
QueryCursor.prototype[Symbol.asyncIterator] = function() {
440-
return this.transformNull()._transformForAsyncIterator();
419+
QueryCursor.prototype[Symbol.asyncIterator] = function queryCursorAsyncIterator() {
420+
// Set so QueryCursor knows it should transform results for async iterators into `{ value, done }` syntax
421+
this._mongooseOptions._asyncIterator = true;
422+
return this;
441423
};
442424
}
443425

444-
/*!
445-
* ignore
446-
*/
447-
448-
function _transformForAsyncIterator(doc) {
449-
return doc == null ? { done: true } : { value: doc, done: false };
450-
}
451-
452426
/**
453427
* Get the next doc from the underlying cursor and mongooseify it
454428
* (populate, etc.)
@@ -459,16 +433,38 @@ function _transformForAsyncIterator(doc) {
459433

460434
function _next(ctx, cb) {
461435
let callback = cb;
462-
if (ctx._transforms.length) {
463-
callback = function(err, doc) {
464-
if (err || (doc === null && !ctx._mongooseOptions.transformNull)) {
465-
return cb(err, doc);
436+
437+
// Create a custom callback to handle transforms, async iterator, and transformNull
438+
callback = function(err, doc) {
439+
if (err) {
440+
return cb(err);
441+
}
442+
443+
// Handle null documents - if asyncIterator, we need to return `done: true`, otherwise just
444+
// skip. In either case, avoid transforms.
445+
if (doc === null) {
446+
if (ctx._mongooseOptions._asyncIterator) {
447+
return cb(null, { done: true });
448+
} else {
449+
return cb(null, null);
466450
}
467-
cb(err, ctx._transforms.reduce(function(doc, fn) {
451+
}
452+
453+
// Apply transforms
454+
if (ctx._transforms.length && doc !== null) {
455+
doc = ctx._transforms.reduce(function(doc, fn) {
468456
return fn.call(ctx, doc);
469-
}, doc));
470-
};
471-
}
457+
}, doc);
458+
}
459+
460+
// This option is set in `Symbol.asyncIterator` code paths.
461+
// For async iterator, we need to convert to {value, done} format
462+
if (ctx._mongooseOptions._asyncIterator) {
463+
return cb(null, { value: doc, done: false });
464+
}
465+
466+
return cb(null, doc);
467+
};
472468

473469
if (ctx._error) {
474470
return immediate(function() {

lib/query.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5420,8 +5420,10 @@ Query.prototype.nearSphere = function() {
54205420
*/
54215421

54225422
if (Symbol.asyncIterator != null) {
5423-
Query.prototype[Symbol.asyncIterator] = function() {
5424-
return this.cursor().transformNull()._transformForAsyncIterator();
5423+
Query.prototype[Symbol.asyncIterator] = function queryAsyncIterator() {
5424+
// Set so QueryCursor knows it should transform results for async iterators into `{ value, done }` syntax
5425+
this._mongooseOptions._asyncIterator = true;
5426+
return this.cursor();
54255427
};
54265428
}
54275429

test/query.test.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2354,6 +2354,19 @@ describe('Query', function() {
23542354
assert.strictEqual(called, 1);
23552355
});
23562356

2357+
it('transform with for/await and cursor', async function() {
2358+
const Model = db.model('Test', new Schema({ name: String }));
2359+
2360+
await Model.create({ name: 'test' });
2361+
const cursor = Model.find().transform(doc => doc.name.toUpperCase()).cursor();
2362+
const names = [];
2363+
for await (const name of cursor) {
2364+
names.push(name);
2365+
}
2366+
2367+
assert.deepStrictEqual(names, ['TEST']);
2368+
});
2369+
23572370
describe('orFail (gh-6841)', function() {
23582371
let Model;
23592372

test/types/queries.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,8 @@ expectError(Test.find({}, { 'docs.profiles': { name: 'aa' } })); // should suppo
187187
expectError(Test.find({}, { endDate: { toString: 1 } }));
188188
expectError(Test.find({}, { tags: { trim: 1 } }));
189189
expectError(Test.find({}, { child: { toJSON: 1 } }));
190+
Test.find({}, { age: 1, _id: 0 });
191+
Test.find({}, { name: 0, age: 0, _id: 1 });
190192

191193
// Manual Casting using ProjectionType
192194
Test.find({}, { docs: { unknownParams: 1 } } as ProjectionType<ITest>);

types/index.d.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -685,10 +685,16 @@ declare module 'mongoose' {
685685
}
686686
: Element;
687687
type _IDType = { _id?: boolean | 1 | 0 };
688-
export type InclusionProjection<T> = IsItRecordAndNotAny<T> extends true ? Projector<WithLevel1NestedPaths<T>, true | 1> & _IDType : AnyObject;
689-
export type ExclusionProjection<T> = IsItRecordAndNotAny<T> extends true ? Projector<WithLevel1NestedPaths<T>, false | 0> & _IDType : AnyObject;
690-
691-
export type ProjectionType<T> = (InclusionProjection<T> & AnyObject) | (ExclusionProjection<T> & AnyObject) | string;
688+
export type InclusionProjection<T> = IsItRecordAndNotAny<T> extends true
689+
? Omit<Projector<WithLevel1NestedPaths<T>, true | 1>, '_id'> & _IDType
690+
: AnyObject;
691+
export type ExclusionProjection<T> = IsItRecordAndNotAny<T> extends true
692+
? Omit<Projector<WithLevel1NestedPaths<T>, false | 0>, '_id'> & _IDType
693+
: AnyObject;
694+
695+
export type ProjectionType<T> = (InclusionProjection<T> & AnyObject)
696+
| (ExclusionProjection<T> & AnyObject)
697+
| string;
692698
export type SortValues = SortOrder;
693699

694700
export type SortOrder = -1 | 1 | 'asc' | 'ascending' | 'desc' | 'descending';

types/pipelinestage.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -319,10 +319,11 @@ declare module 'mongoose' {
319319
export interface VectorSearch {
320320
/** [`$vectorSearch` reference](https://www.mongodb.com/docs/atlas/atlas-vector-search/vector-search-stage/) */
321321
$vectorSearch: {
322+
exact?: boolean;
322323
index: string,
323324
path: string,
324325
queryVector: number[],
325-
numCandidates: number,
326+
numCandidates?: number,
326327
limit: number,
327328
filter?: Expression,
328329
}

0 commit comments

Comments
 (0)