OpenSearch ranks search results using two cooperating mechanisms: a sort clause that defines the final field-and-score ordering, and sort policies that define how relevance scores are calculated through a two-phase pipeline called rough sort and fine sort.
How scoring works
OpenSearch scoring runs in two stages:
Rough sort (FirstRank): After documents are retrieved by the query clause and filtered by the filter clause, rough sort scores all matching documents using the rough sort expression.
Fine sort (SecondRank): The top N documents from rough sort are re-scored using the fine sort expression. The final sort policy score is based on the fine sort result.
This two-phase design exists so that expensive fine sort expressions run only on a small candidate set, not on every matched document. The 10,000-point base added at each stage transition separates documents that reached fine sort from those that did not — which is why debug output shows scores like 10000.2259 rather than the raw expression value.
Score calculation rules:
| Sort policy configuration | Score formula | Maximum score |
|---|---|---|
| Rough sort only | 10,000 + rough sort expression result | 20,000 (capped) |
| Fine sort only | 10,000 + fine sort expression result | No limit |
| Both rough and fine sort | Documents reaching fine sort: 10,000 + fine sort result; documents that only pass rough sort: 10,000 + rough sort result | 20,000 (capped) |
Sort clause and sort policies
The sort clause controls the final ordering of documents — similar to the ORDER BY clause in SQL. List sort fields and RANK in priority order, separated by semicolons. Prefix each entry with - for descending order or + for ascending order. RANK represents the final score from the sort policy.
sort=-create_time;-RANKThis clause sorts by create_time descending first, then by the sort policy score descending for documents with the same create_time.
By default, if no sort clause is configured, the system applies -RANK automatically. If you configure a sort clause, -RANK is not added automatically — include it explicitly whenever you want sort policy scores to influence ranking.
| Scenario | Behavior |
|---|---|
| No sort clause | System applies -RANK |
Sort clause without RANK | Sort policy score has no effect |
Sort clause with -RANK | Sort policy score applies as a tiebreaker or primary criterion |
To configure the expressions used in rough sort and fine sort, see Configure sort policies.
Example: sort clause with a two-field sort
Consider an application with the following schema:
| Field | Type | Index |
|---|---|---|
| id | int | Keyword |
| name | text | General index for Chinese |
| age | int | Keyword |
The rough sort policy uses static_bm25(). The fine sort policy uses text_relevance(name).
Configure the sort clause:
sort=age;-RANKThis sorts documents first by age ascending. For documents with the same age, it sorts by the sort policy score descending.
Turn on Show Sort Details to inspect the score breakdown. The output looks similar to:

The score 13,10000.2259030193 breaks down as:
13— the value of theagefield10000.2259030193— the final sort policy score
The sort formula behind that score:
FirstRank:
expression[static_bm25()], result[0.496452].
SecondRank:
expression[text_relevance(name)], result[0.225903].The rough sort policy for the name field is configured as shown:

The fine sort policy for the name field is configured as shown:

How the final score 10000.2259030193 is calculated: the document ranked in the top 200 based on rough sort (score: 0.496452), so it entered fine sort. At that transition, OpenSearch discards the rough sort score and adds 10,000 as a base. The fine sort expression text_relevance(name) produced 0.225903, giving a final score of 10000 + 0.225903 = 10000.2259030193.