Skip to content

fix(NODE-7643): preserve $meta direction in [field, direction] sort pair - #4988

Merged
tadjik1 merged 3 commits into
mongodb:mainfrom
spokodev:w32/mongo-sort-meta-pair
Oct 9, 2026
Merged

tadjik1 merged 3 commits into
mongodb:mainfrom
spokodev:w32/mongo-sort-meta-pair

Conversation

@spokodev

@spokodev spokodev commented Jul 2, 2026 •

Copy link
Copy Markdown
Contributor

Description

Sorting by $meta is rejected in the [field, direction] pair form of sort. The type-legal input

cursor.sort(['score', { $meta: 'textScore' }])

throws:

MongoInvalidArgumentError: Invalid sort direction: [{"$meta":"textScore"}]

The equivalent object form { score: { $meta: 'textScore' } } and the deep form [['score', { $meta: 'textScore' }]] both work, so the three shapes the API accepts behave inconsistently.

Root cause

In src/sort.ts, pairToMap calls prepareDirection([v[1]]), wrapping the direction in an array, unlike every sibling converter which passes the raw value. A scalar direction coincidentally survives the array to string coercion ([-1] -> "-1"), but a $meta object does not (isMeta returns false for an array), so it is rejected.

This is a regression from #2788 (NODE-3174), which replaced pairToObject with pairToMap and wrapped the direction in an array. With src/sort.ts from just before #2788, the pair form returns { score: { $meta: 'textScore' } }.

Fix

-  return new Map([[`${v[0]}`, prepareDirection([v[1]])]]);
+  return new Map([[`${v[0]}`, prepareDirection(v[1])]]);

Test

Added test/unit/sort.test.ts (no test/unit file covered formatSort before). The $meta pair case fails unpatched with the error above and passes after; the scalar-direction pair cases pass throughout, showing the fix is scoped. Full test/unit suite stays green.

Also added an integration test in test/integration/crud/find.test.ts: a text index on title, a $text search with the score projected, sorted with ['score', { $meta: 'textScore' }]. It fails unpatched with the same error and passes after.

Release Highlight

Sorting by $meta with a [field, direction] pair now works

Previously, passing a $meta sort direction in the [field, direction] form, for example cursor.sort(['score', { $meta: 'textScore' }]), threw MongoInvalidArgumentError: Invalid sort direction. The object form ({ score: { $meta: 'textScore' } }) and the nested array form ([['score', { $meta: 'textScore' }]]) already worked, so the same sort was accepted or rejected depending on how it was written. All three forms now behave the same.

Thanks to @spokodev for the fix!

Double check the following

  • Lint is passing (npm run check:lint)
  • Self-review completed using the steps outlined here
  • PR title follows the correct format: type(NODE-xxxx)[!]: description
    • Example: feat(NODE-1234)!: rewriting everything in coffeescript
  • Changes are covered by tests
  • New TODOs have a related JIRA ticket

@spokodev
spokodev requested a review from a team as a code owner July 2, 2026 10:03
@johnmtll

johnmtll commented Jul 7, 2026 •

Copy link
Copy Markdown
Contributor

Thank you for taking the time to fill out this detailed PR! This PR will be tracked by NODE-7643 and will be prioritized in the next triage session.

@johnmtll johnmtll changed the title fix: preserve $meta direction in [field, direction] sort pair fix(NODE-7658): preserve $meta direction in [field, direction] sort pair Jul 7, 2026
@johnmtll johnmtll added External Submission PR submitted from outside the team tracked-in-jira Ticket filed in MongoDB's Jira system labels Jul 7, 2026
@johnmtll johnmtll changed the title fix(NODE-7658): preserve $meta direction in [field, direction] sort pair fix(NODE-7643): preserve $meta direction in [field, direction] sort pair Jul 9, 2026
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.
@spokodev
spokodev force-pushed the w32/mongo-sort-meta-pair branch from 662d2c0 to 4311080 Compare October 5, 2026 19:15
@spokodev

spokodev commented Oct 5, 2026

Copy link
Copy Markdown
Contributor Author

@johnmtll, thank you for landing #4989 on 2 October. This one is now rebased onto main (it was 50 commits behind) and its unit tests pass, 3 of 3. With the src/sort.ts change reverted, the $meta case fails.

The bug is one line in pairToMap: it wraps the direction in an array before calling prepareDirection, so isMeta never sees the { $meta: 'textScore' } object and formatSort(['score', { $meta: 'textScore' }]) throws Invalid sort direction.

Should I add integration coverage here too, as on #4989? Evergreen will need authorizing on the new head.

@tadjik1 tadjik1 self-assigned this Oct 8, 2026
@tadjik1 tadjik1 added the Primary Review In Review with primary reviewer, not yet ready for team's eyes label Oct 8, 2026
@tadjik1

tadjik1 commented Oct 8, 2026

Copy link
Copy Markdown
Member

Hi @spokodev, thanks a lot for the PR! Yes, can you please add integration tests for the fix?
You would need to create a text index, insert few documents with different content relevant to specific queries (I usually use movie names, but it's up to you). And then query by $text with score projection and sorting.

@spokodev

spokodev commented Oct 8, 2026

Copy link
Copy Markdown
Contributor Author

Thanks, @tadjik1. Added an integration test in abf28d6, next to the existing text search sort test in test/integration/crud/find.test.ts. It creates a text index on title, inserts five documents, runs a $text search for godfather harlem with the score projected, sorts with ['score', { $meta: 'textScore' }], and checks the order of the four matches.

Their scores are 1.5, 1, 0.75 and 2/3, so the expected order has no ties. On the old src/sort.ts the test fails with Invalid sort direction: [{"$meta":"textScore"}].

I ran it against a local standalone mongod 8.0.32: all of find.test.ts passes, with 45 passing and 2 pending.

I also corrected the PR description. The array wrapping came from #2788, not from #2573: with src/sort.ts from just before #2788, formatSort(['score', { $meta: 'textScore' }]) returns { score: { $meta: 'textScore' } }, and from #2788 on it throws.

@tadjik1 tadjik1 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great one, thanks @spokodev! 👍

@tadjik1
tadjik1 merged commit 16136bb into mongodb:main Oct 9, 2026
31 of 33 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

External Submission PR submitted from outside the team Primary Review In Review with primary reviewer, not yet ready for team's eyes tracked-in-jira Ticket filed in MongoDB's Jira system

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants