From 431108075a2d6da4765358d733341b23eeaddeee Mon Sep 17 00:00:00 2001 From: Yarchik Date: Thu, 2 Jul 2026 10:57:56 +0100 Subject: [PATCH 1/2] fix: preserve $meta direction in [field, direction] sort pair formatSort's pairToMap wrapped the direction in an array (prepareDirection([v[1]])) unlike every sibling converter. Scalar directions survive the array-to-string coercion, but a $meta object does not (isMeta is false for an array), so the documented cursor.sort(['score', { $meta: 'textScore' }]) throws Invalid sort direction while the object and deep-array forms work. Pass the raw value through, as the other converters do. --- src/sort.ts | 2 +- test/unit/sort.test.ts | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 test/unit/sort.test.ts diff --git a/src/sort.ts b/src/sort.ts index 0b239ddd494..7625937ca56 100644 --- a/src/sort.ts +++ b/src/sort.ts @@ -81,7 +81,7 @@ function isReadonlyArray(value: any): value is readonly T[] { /** @internal */ function pairToMap(v: readonly [string, SortDirection]): SortForCmd { - return new Map([[`${v[0]}`, prepareDirection([v[1]])]]); + return new Map([[`${v[0]}`, prepareDirection(v[1])]]); } /** @internal */ diff --git a/test/unit/sort.test.ts b/test/unit/sort.test.ts new file mode 100644 index 00000000000..19669feff36 --- /dev/null +++ b/test/unit/sort.test.ts @@ -0,0 +1,21 @@ +import { expect } from 'chai'; + +import { formatSort } from '../mongodb'; + +describe('formatSort', function () { + context('when the sort is a [field, direction] pair', function () { + it('formats a numeric direction', function () { + expect(formatSort(['alpha', -1])).to.deep.equal(new Map([['alpha', -1]])); + }); + + it('formats a string direction', function () { + expect(formatSort(['alpha', 'asc'])).to.deep.equal(new Map([['alpha', 1]])); + }); + + it('preserves a $meta direction', function () { + expect(formatSort(['alpha', { $meta: 'textScore' }])).to.deep.equal( + new Map([['alpha', { $meta: 'textScore' }]]) + ); + }); + }); +}); From abf28d6289ab261b222261705e2a381ca83f9563 Mon Sep 17 00:00:00 2001 From: Yarchik Date: Thu, 8 Oct 2026 17:37:30 +0100 Subject: [PATCH 2/2] test: cover sorting text search results by a [field, { $meta }] pair --- test/integration/crud/find.test.ts | 31 ++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/test/integration/crud/find.test.ts b/test/integration/crud/find.test.ts index 2b24254e2db..337b3d86f56 100644 --- a/test/integration/crud/find.test.ts +++ b/test/integration/crud/find.test.ts @@ -884,6 +884,37 @@ describe('Find', function () { test.equal('spam eggs and spam', items[0].s); }); + it('should correctly sort by text score given as a [field, direction] pair', async function () { + const configuration = this.configuration; + const db = client.db(configuration.db); + + const collection = db.collection('textSearchWithPairSort'); + await collection.createIndex({ title: 'text' }); + + await collection.insertMany([ + { title: 'The Godfather Part II' }, + { title: 'Casablanca' }, + { title: 'Harlem Nights' }, + { title: 'The Godfather' }, + { title: 'Godfather of Harlem' } + ]); + + const items = await collection + .find( + { $text: { $search: 'godfather harlem' } }, + { projection: { _id: false, title: true, score: { $meta: 'textScore' } } } + ) + .sort(['score', { $meta: 'textScore' }]) + .toArray(); + + expect(items.map(({ title }) => title)).to.deep.equal([ + 'Godfather of Harlem', + 'The Godfather', + 'Harlem Nights', + 'The Godfather Part II' + ]); + }); + it('should not mutate user-provided options', async function () { const configuration = this.configuration; const db = client.db(configuration.db);