88import static org .opensearch .core .xcontent .DeprecationHandler .IGNORE_DEPRECATIONS ;
99import static org .opensearch .search .sort .FieldSortBuilder .DOC_FIELD_NAME ;
1010import static org .opensearch .search .sort .SortOrder .ASC ;
11- import static org .opensearch .sql .opensearch .storage .OpenSearchIndex .METADATA_FIELD_ID ;
1211
1312import java .io .IOException ;
1413import java .util .Collections ;
3130import org .opensearch .search .SearchModule ;
3231import org .opensearch .search .builder .PointInTimeBuilder ;
3332import org .opensearch .search .builder .SearchSourceBuilder ;
33+ import org .opensearch .search .sort .FieldSortBuilder ;
34+ import org .opensearch .search .sort .ShardDocSortBuilder ;
35+ import org .opensearch .search .sort .SortBuilders ;
3436import org .opensearch .sql .opensearch .data .value .OpenSearchExprValueFactory ;
3537import org .opensearch .sql .opensearch .response .OpenSearchResponse ;
3638import org .opensearch .sql .opensearch .storage .OpenSearchIndex ;
@@ -51,7 +53,7 @@ public class OpenSearchQueryRequest implements OpenSearchRequest {
5153 private final IndexName indexName ;
5254
5355 /** Search request source builder. */
54- private SearchSourceBuilder sourceBuilder ;
56+ private final SearchSourceBuilder sourceBuilder ;
5557
5658 /** OpenSearchExprValueFactory. */
5759 @ EqualsAndHashCode .Exclude @ ToString .Exclude
@@ -203,12 +205,23 @@ public OpenSearchResponse searchWithPIT(Function<SearchRequest, SearchResponse>
203205 if (searchAfter != null ) {
204206 this .sourceBuilder .searchAfter (searchAfter );
205207 }
206- // Set sort field for search_after
207- if (this .sourceBuilder .sorts () == null ) {
208+ // Add sort tiebreaker for PIT search.
209+ // We cannot remove it since `_shard_doc` is not added implicitly in PIT now.
210+ // Ref https://github.com/opensearch-project/OpenSearch/pull/18924#issuecomment-3342365950
211+ if (this .sourceBuilder .sorts () == null || this .sourceBuilder .sorts ().isEmpty ()) {
212+ // If no sort field specified, sort by `_doc` + `_shard_doc`to get better performance
208213 this .sourceBuilder .sort (DOC_FIELD_NAME , ASC );
209- // Workaround to preserve sort location more exactly,
210- // see https://github.com/opensearch-project/sql/pull/3061
211- this .sourceBuilder .sort (METADATA_FIELD_ID , ASC );
214+ this .sourceBuilder .sort (SortBuilders .shardDocSort ());
215+ } else {
216+ // If sort fields specified, sort by `fields` + `_doc` + `_shard_doc`.
217+ if (this .sourceBuilder .sorts ().stream ()
218+ .noneMatch (
219+ b -> b instanceof FieldSortBuilder f && f .fieldName ().equals (DOC_FIELD_NAME ))) {
220+ this .sourceBuilder .sort (DOC_FIELD_NAME , ASC );
221+ }
222+ if (this .sourceBuilder .sorts ().stream ().noneMatch (ShardDocSortBuilder .class ::isInstance )) {
223+ this .sourceBuilder .sort (SortBuilders .shardDocSort ());
224+ }
212225 }
213226 SearchRequest searchRequest =
214227 new SearchRequest ().indices (indexName .getIndexNames ()).source (this .sourceBuilder );
0 commit comments