diff --git a/lib/query.js b/lib/query.js index 8731f003204..7c8cb0f2d8c 100644 --- a/lib/query.js +++ b/lib/query.js @@ -2861,19 +2861,27 @@ Query.prototype.distinct = function(field, conditions) { * Cannot be used with `distinct()` * * @param {Object|String|Array>} arg + * @param {Object} [options] + * @param {Boolean} [options.override=false] If true, replace existing sort options with `arg` * @return {Query} this * @see cursor.sort https://www.mongodb.com/docs/manual/reference/method/cursor.sort/ * @api public */ -Query.prototype.sort = function(arg) { - if (arguments.length > 1) { - throw new Error('sort() only takes 1 Argument'); +Query.prototype.sort = function(arg, options) { + if (arguments.length > 2) { + throw new Error('sort() takes at most 2 arguments'); + } + if (options != null && typeof options !== 'object') { + throw new Error('sort() options argument must be an object or nullish'); } if (this.options.sort == null) { this.options.sort = {}; } + if (options && options.override) { + this.options.sort = {}; + } const sort = this.options.sort; if (typeof arg === 'string') { const properties = arg.indexOf(' ') === -1 ? [arg] : arg.split(' '); diff --git a/test/query.test.js b/test/query.test.js index 9d1cd201515..94f6911903a 100644 --- a/test/query.test.js +++ b/test/query.test.js @@ -795,7 +795,7 @@ describe('Query', function() { e = err; } assert.ok(e, 'uh oh. no error was thrown'); - assert.equal(e.message, 'sort() only takes 1 Argument'); + assert.equal(e.message, 'sort() takes at most 2 arguments'); }); }); @@ -4191,4 +4191,27 @@ describe('Query', function() { assert.strictEqual(doc.account.owner, undefined); assert.strictEqual(doc.account.taxIds, undefined); }); + + it('allows overriding sort (gh-14365)', function() { + const testSchema = new mongoose.Schema({ + name: String + }); + + const Test = db.model('Test', testSchema); + + const q = Test.find().select('name').sort({ name: -1, _id: -1 }); + assert.deepStrictEqual(q.getOptions().sort, { name: -1, _id: -1 }); + + q.sort({ name: 1 }, { override: true }); + assert.deepStrictEqual(q.getOptions().sort, { name: 1 }); + + q.sort(null, { override: true }); + assert.deepStrictEqual(q.getOptions().sort, {}); + + q.sort({ _id: 1 }, { override: true }); + assert.deepStrictEqual(q.getOptions().sort, { _id: 1 }); + + q.sort({}, { override: true }); + assert.deepStrictEqual(q.getOptions().sort, {}); + }); }); diff --git a/types/query.d.ts b/types/query.d.ts index 0bdda904350..db9275a12bd 100644 --- a/types/query.d.ts +++ b/types/query.d.ts @@ -715,7 +715,10 @@ declare module 'mongoose' { slice(val: number | Array): this; /** Sets the sort order. If an object is passed, values allowed are `asc`, `desc`, `ascending`, `descending`, `1`, and `-1`. */ - sort(arg?: string | { [key: string]: SortOrder | { $meta: any } } | [string, SortOrder][] | undefined | null): this; + sort( + arg?: string | { [key: string]: SortOrder | { $meta: any } } | [string, SortOrder][] | undefined | null, + options?: { override?: boolean } + ): this; /** Sets the tailable option (for use with capped collections). */ tailable(bool?: boolean, opts?: {