Skip to content

Commit 03bb0be

Browse files
authored
Merge pull request Automattic#15250 from Automattic/vkarpov15/Automatticgh-14906
perf: some more micro optimizations for find() and findOne() re: Automattic#14906
2 parents 8257637 + a41b821 commit 03bb0be

File tree

2 files changed

+34
-29
lines changed

2 files changed

+34
-29
lines changed

lib/document.js

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -741,15 +741,10 @@ function init(self, obj, doc, opts, prefix) {
741741
let schemaType;
742742
let path;
743743
let i;
744-
let index = 0;
745744
const strict = self.$__.strictMode;
746745
const docSchema = self.$__schema;
747746

748-
while (index < len) {
749-
_init(index++);
750-
}
751-
752-
function _init(index) {
747+
for (let index = 0; index < len; ++index) {
753748
i = keys[index];
754749
// avoid prototype pollution
755750
if (i === '__proto__' || i === 'constructor') {

lib/query.js

Lines changed: 33 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,25 @@ const queryOptionMethods = new Set([
6565
'wtimeout'
6666
]);
6767

68+
// Map from operation name to the name of the function that executes the actual operation against MongoDB.
69+
// Called a thunk for legacy reasons, "thunk" means function that takes exactly 1 param, a callback.
70+
// Currently `_countDocuments()`, etc. are async functions that take no params.
71+
const opToThunk = new Map([
72+
['countDocuments', '_countDocuments'],
73+
['distinct', '__distinct'],
74+
['estimatedDocumentCount', '_estimatedDocumentCount'],
75+
['find', '_find'],
76+
['findOne', '_findOne'],
77+
['findOneAndReplace', '_findOneAndReplace'],
78+
['findOneAndUpdate', '_findOneAndUpdate'],
79+
['replaceOne', '_replaceOne'],
80+
['updateMany', '_updateMany'],
81+
['updateOne', '_updateOne'],
82+
['deleteMany', '_deleteMany'],
83+
['deleteOne', '_deleteOne'],
84+
['findOneAndDelete', '_findOneAndDelete']
85+
]);
86+
6887
/**
6988
* Query constructor used for building queries. You do not need
7089
* to instantiate a `Query` directly. Instead use Model functions like
@@ -2337,18 +2356,17 @@ Query.prototype._find = async function _find() {
23372356
}
23382357

23392358
const mongooseOptions = this._mongooseOptions;
2340-
const _this = this;
2341-
const userProvidedFields = _this._userProvidedFields || {};
2359+
const userProvidedFields = this._userProvidedFields || {};
23422360

23432361
applyGlobalMaxTimeMS(this.options, this.model.db.options, this.model.base.options);
23442362
applyGlobalDiskUse(this.options, this.model.db.options, this.model.base.options);
23452363

23462364
// Separate options to pass down to `completeMany()` in case we need to
23472365
// set a session on the document
2348-
const completeManyOptions = Object.assign({}, {
2366+
const completeManyOptions = {
23492367
session: this && this.options && this.options.session || null,
23502368
lean: mongooseOptions.lean || null
2351-
});
2369+
};
23522370

23532371
const options = this._optionsForExec();
23542372

@@ -2366,7 +2384,7 @@ Query.prototype._find = async function _find() {
23662384
}
23672385

23682386
if (!mongooseOptions.populate) {
2369-
const versionKey = _this.schema.options.versionKey;
2387+
const versionKey = this.schema.options.versionKey;
23702388
if (mongooseOptions.lean && mongooseOptions.lean.versionKey === false && versionKey) {
23712389
docs.forEach((doc) => {
23722390
if (versionKey in doc) {
@@ -2375,17 +2393,17 @@ Query.prototype._find = async function _find() {
23752393
});
23762394
}
23772395
return mongooseOptions.lean ?
2378-
_completeManyLean(_this.model.schema, docs, null, completeManyOptions) :
2379-
_this._completeMany(docs, fields, userProvidedFields, completeManyOptions);
2396+
_completeManyLean(this.model.schema, docs, null, completeManyOptions) :
2397+
this._completeMany(docs, fields, userProvidedFields, completeManyOptions);
23802398
}
23812399

2382-
const pop = helpers.preparePopulationOptionsMQ(_this, mongooseOptions);
2400+
const pop = helpers.preparePopulationOptionsMQ(this, mongooseOptions);
23832401

23842402
if (mongooseOptions.lean) {
2385-
return _this.model.populate(docs, pop);
2403+
return this.model.populate(docs, pop);
23862404
}
23872405

2388-
docs = await _this._completeMany(docs, fields, userProvidedFields, completeManyOptions);
2406+
docs = await this._completeMany(docs, fields, userProvidedFields, completeManyOptions);
23892407
await this.model.populate(docs, pop);
23902408

23912409
return docs;
@@ -4397,22 +4415,14 @@ Query.prototype.exec = async function exec(op) {
43974415
if (this.model == null) {
43984416
throw new MongooseError('Query must have an associated model before executing');
43994417
}
4400-
this._validateOp();
4401-
4402-
if (!this.op) {
4403-
return;
4404-
}
44054418

4406-
if (this.options && this.options.sort) {
4407-
const keys = Object.keys(this.options.sort);
4408-
if (keys.includes('')) {
4409-
throw new Error('Invalid field "" passed to sort()');
4410-
}
4419+
const thunk = opToThunk.get(this.op);
4420+
if (!thunk) {
4421+
throw new MongooseError('Query has invalid `op`: "' + this.op + '"');
44114422
}
44124423

4413-
let thunk = '_' + this.op;
4414-
if (this.op === 'distinct') {
4415-
thunk = '__distinct';
4424+
if (this.options && this.options.sort && typeof this.options.sort === 'object' && this.options.sort.hasOwnProperty('')) {
4425+
throw new Error('Invalid field "" passed to sort()');
44164426
}
44174427

44184428
if (this._executionStack != null) {

0 commit comments

Comments
 (0)