Skip to content

fix(opensearch): honor sort=score in OS non-scroll read path (#36494) - #36643

Merged
fabrizzio-dotCMS merged 2 commits into
mainfrom
issue-36494-os-sort-score
Jul 21, 2026
Merged

fix(opensearch): honor sort=score in OS non-scroll read path (#36494)#36643
fabrizzio-dotCMS merged 2 commits into
mainfrom
issue-36494-os-sort-score

Conversation

@fabrizzio-dotCMS

@fabrizzio-dotCMS fabrizzio-dotCMS commented Jul 20, 2026

Copy link
Copy Markdown
Member

Proposed Changes

Fixes #36494.

During the ES→OpenSearch migration, content queries sorting by relevance (sort=score) silently lost their ordering once reads switched to OpenSearch (Phase 2+): they came back in default moddate desc order instead of _score order. HTTP 200, plausible-looking hit set, wrong order — the kind of regression count-based parity checks can't catch. Root cause: ContentFactoryIndexOperationsOS.addSorting's sortBy.startsWith("score") branch added only the secondary sort and never the primary _score sort.

Note (post-rebase): the core _score-sort fix has since landed on main via #36501/#36557 (fix(os-search): honor track_total_hits and _score sort on the OpenSearch read path), which added the same primary/secondary sort to this branch. After merging main, the net contribution of this PR is now:

  1. unmappedType(Date) hardening on the secondary sort, and
  2. the regression tests for sort=score (which main does not have).

What this PR still changes

Production — ContentFactoryIndexOperationsOS.addSorting: add unmappedType(Date) to the secondary field sort in the score branch, matching the scroll path (OSContentletScrollImpl). Without it, sort=score <field> fails on indices where the secondary field is not mapped, instead of degrading gracefully. Behavior for all other sort inputs is unchanged.

Tests

Added to OpenSearchUpgradeSuite (ContentFactoryIndexOperationsOSIntegrationTest) — new coverage not present on main:

  • test_searchHits_sortByScore_shouldOrderByRelevanceNotModdate — indexes docs so relevance order is the exact reverse of moddate order; asserts scores come back descending and the most-relevant doc is first (guards against the original regression).
  • test_searchHits_sortByScore_secondaryOverride_shouldApplySecondarySort — equal-score docs, sort=score moddate asc; asserts the secondary sort breaks the tie.

Acceptance Criteria

🤖 Generated with Claude Code

This PR fixes: #36494

ContentFactoryIndexOperationsOS.addSorting entered its `score` branch and
added only the secondary sort (default `moddate desc`), never the primary
`_score` sort. Relevance-sorted queries (`sort=score`) therefore returned
default moddate ordering — silently wrong results — once reads switch to
OpenSearch (Phase 2+). The scroll path (OSContentletScrollImpl) already
does this correctly; this aligns the non-scroll path with it and with the
ES read path: primary `_score` desc, then the secondary sort.

Adds OpenSearchUpgradeSuite coverage asserting relevance ordering (not
moddate) and secondary-sort override behavior.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@claude

claude Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Claude finished @fabrizzio-dotCMS's task in 58s —— View job


Code Review: fix(opensearch): honor sort=score in OS non-scroll read path

  • Read production diff (ContentFactoryIndexOperationsOS.addSorting)
  • Compared against scroll path (OSContentletScrollImpl.applySorting) and ES path
  • Reviewed added integration tests
  • Verified imports

Summary

The fix is correct and minimal. The score branch now adds the primary _score (desc) sort before the secondary field sort, and the secondary FieldSort gains unmappedType(FieldType.Date). This makes the non-scroll path (ContentFactoryIndexOperationsOS.java:342-346) an exact match of the already-correct scroll path (OSContentletScrollImpl.java:287-291). The FieldType import is already present (ContentFactoryIndexOperationsOS.java:30), so it compiles cleanly. Behavior for all non-score sort inputs is untouched.

The two added tests are well-designed regression guards:

  • test_searchHits_sortByScore_shouldOrderByRelevanceNotModdate deliberately inverts relevance order against moddate order, so it fails pre-fix (returned C,B,A) and passes post-fix (A,B,C) — a real guard, not a tautology.
  • test_searchHits_sortByScore_secondaryOverride_shouldApplySecondarySort isolates the tie-break by giving all docs identical _score, correctly exercising the sort=score <field> <dir> override path.

New Issues

No issues found.

Notes (non-blocking, pre-existing — not introduced by this PR)

  • A caller override like sort=score title asc would feed a non-date field name into a secondary sort hard-coded with unmappedType(FieldType.Date), and (unlike addBuilderSort) it is used raw without the _dotraw suffix. This is a pre-existing design shared verbatim with the scroll path, so it's out of scope here — but if a non-date secondary override is ever a real use case, both paths would need the field-type inference logic. Not a regression from this change.

The PR does exactly what it claims, aligns the two OS read paths, and adds meaningful coverage. LGTM.

| Branch: issue-36494-os-sort-score

…core

# Conflicts:
#	dotCMS/src/main/java/com/dotcms/content/index/opensearch/ContentFactoryIndexOperationsOS.java
@mergify

mergify Bot commented Jul 21, 2026

Copy link
Copy Markdown

Tick the box to add this pull request to the merge queue (same as @mergifyio queue).

  • Queue this pull request

@fabrizzio-dotCMS
fabrizzio-dotCMS added this pull request to the merge queue Jul 21, 2026
Merged via the queue into main with commit aec4ca2 Jul 21, 2026
63 checks passed
@fabrizzio-dotCMS
fabrizzio-dotCMS deleted the issue-36494-os-sort-score branch July 21, 2026 18:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI: Safe To Rollback Area : Backend PR changes Java/Maven backend code

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

OpenSearch: sort=score ignored by OS read path — relevance-sorted queries return default order in Phases 2/3

2 participants