diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/search/elasticsearch/ElasticSearchSearchManager.java b/openmetadata-service/src/main/java/org/openmetadata/service/search/elasticsearch/ElasticSearchSearchManager.java index 28d5dba43434..abd860f2062d 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/search/elasticsearch/ElasticSearchSearchManager.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/search/elasticsearch/ElasticSearchSearchManager.java @@ -94,6 +94,8 @@ public class ElasticSearchSearchManager implements SearchManagementClient { private final String clusterAlias; private final RBACConditionEvaluator rbacConditionEvaluator; private final NLQService nlqService; + private static final String SORT_FIELD_SCORE = "_score"; + private static final String SORT_TYPE_KEYWORD = "keyword"; private static final Set FIELDS_TO_REMOVE = Set.of( "suggest", @@ -975,7 +977,7 @@ public Response doSearch( String clusterAlias) throws IOException { ElasticSearchRequestBuilder requestBuilder = - buildSearchRequestBuilder(request, subjectContext, searchSettings, clusterAlias); + buildSearchRequestBuilder(request, subjectContext, searchSettings, clusterAlias, false); LOG.debug("Executing search on index: {}, query: {}", request.getIndex(), request.getQuery()); @@ -1017,7 +1019,7 @@ public SearchResultListMapper searchForExport( SearchSettings searchSettings = SettingsCache.getSetting(SettingsType.SEARCH_SETTINGS, SearchSettings.class); ElasticSearchRequestBuilder requestBuilder = - buildSearchRequestBuilder(request, subjectContext, searchSettings, clusterAlias); + buildSearchRequestBuilder(request, subjectContext, searchSettings, clusterAlias, true); try { SearchRequest searchRequest = requestBuilder.build(request.getIndex()); @@ -1060,7 +1062,8 @@ private ElasticSearchRequestBuilder buildSearchRequestBuilder( org.openmetadata.schema.search.SearchRequest request, SubjectContext subjectContext, SearchSettings searchSettings, - String clusterAlias) + String clusterAlias, + boolean isExport) throws IOException { if (!isClientAvailable) { throw new IOException("Elasticsearch client is not available"); @@ -1191,18 +1194,17 @@ private ElasticSearchRequestBuilder buildSearchRequestBuilder( + request.getSortOrder().substring(1).toLowerCase(); SortOrder sortOrder = SortOrder.valueOf(sortTypeCapitalized); - if (!sortField.equalsIgnoreCase("_score")) { + if (!sortField.equalsIgnoreCase(SORT_FIELD_SCORE)) { boolean isKeywordField = sortField.endsWith(".keyword") || SearchSourceBuilderFactory.KEYWORD_SORT_FIELDS.contains(sortField); - requestBuilder.sort(sortField, sortOrder, isKeywordField ? "keyword" : "integer"); + requestBuilder.sort(sortField, sortOrder, isKeywordField ? SORT_TYPE_KEYWORD : "integer"); } else { requestBuilder.sort(sortField, sortOrder, null); } - // Add tiebreaker sort for stable pagination when sorting by score - if (sortField.equalsIgnoreCase("_score")) { - requestBuilder.sort("name.keyword", SortOrder.Asc, "keyword"); + if (sortField.equalsIgnoreCase(SORT_FIELD_SCORE) || isExport) { + requestBuilder.sort("name.keyword", SortOrder.Asc, SORT_TYPE_KEYWORD); } } @@ -1370,10 +1372,8 @@ private ElasticSearchRequestBuilder buildHierarchyQuery( } } - // Add sorting by score first for relevance, then by fullyQualifiedName for consistent hierarchy - // ordering - requestBuilder.sort("_score", SortOrder.Desc, null); - requestBuilder.sort("fullyQualifiedName", SortOrder.Asc, "keyword"); + requestBuilder.sort(SORT_FIELD_SCORE, SortOrder.Desc, null); + requestBuilder.sort("fullyQualifiedName", SortOrder.Asc, SORT_TYPE_KEYWORD); return requestBuilder; } diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/search/opensearch/OpenSearchSearchManager.java b/openmetadata-service/src/main/java/org/openmetadata/service/search/opensearch/OpenSearchSearchManager.java index bd199564e00c..cf824a984379 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/search/opensearch/OpenSearchSearchManager.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/search/opensearch/OpenSearchSearchManager.java @@ -95,6 +95,8 @@ public class OpenSearchSearchManager implements SearchManagementClient { private final String clusterAlias; private final RBACConditionEvaluator rbacConditionEvaluator; private final NLQService nlqService; + private static final String SORT_FIELD_SCORE = "_score"; + private static final String SORT_TYPE_KEYWORD = "keyword"; private static final Set FIELDS_TO_REMOVE = Set.of( "suggest", @@ -1016,7 +1018,7 @@ public Response doSearch( String clusterAlias) throws IOException { OpenSearchRequestBuilder requestBuilder = - buildSearchRequestBuilder(request, subjectContext, searchSettings, clusterAlias); + buildSearchRequestBuilder(request, subjectContext, searchSettings, clusterAlias, false); LOG.debug("Executing search on index: {}, query: {}", request.getIndex(), request.getQuery()); @@ -1053,7 +1055,7 @@ public SearchResultListMapper searchForExport( SearchSettings searchSettings = SettingsCache.getSetting(SettingsType.SEARCH_SETTINGS, SearchSettings.class); OpenSearchRequestBuilder requestBuilder = - buildSearchRequestBuilder(request, subjectContext, searchSettings, clusterAlias); + buildSearchRequestBuilder(request, subjectContext, searchSettings, clusterAlias, true); try { SearchRequest searchRequest = requestBuilder.build(request.getIndex()); @@ -1096,7 +1098,8 @@ private OpenSearchRequestBuilder buildSearchRequestBuilder( org.openmetadata.schema.search.SearchRequest request, SubjectContext subjectContext, SearchSettings searchSettings, - String clusterAlias) + String clusterAlias, + boolean isExport) throws IOException { if (!isClientAvailable) { throw new IOException("OpenSearch client is not available"); @@ -1230,18 +1233,17 @@ private OpenSearchRequestBuilder buildSearchRequestBuilder( + request.getSortOrder().substring(1).toLowerCase(); SortOrder sortOrder = SortOrder.valueOf(sortTypeCapitalized); - if (!sortField.equalsIgnoreCase("_score")) { + if (!sortField.equalsIgnoreCase(SORT_FIELD_SCORE)) { boolean isKeywordField = sortField.endsWith(".keyword") || SearchSourceBuilderFactory.KEYWORD_SORT_FIELDS.contains(sortField); - requestBuilder.sort(sortField, sortOrder, isKeywordField ? "keyword" : "integer"); + requestBuilder.sort(sortField, sortOrder, isKeywordField ? SORT_TYPE_KEYWORD : "integer"); } else { requestBuilder.sort(sortField, sortOrder, null); } - // Add tiebreaker sort for stable pagination when sorting by score - if (sortField.equalsIgnoreCase("_score")) { - requestBuilder.sort("name.keyword", SortOrder.Asc, "keyword"); + if (sortField.equalsIgnoreCase(SORT_FIELD_SCORE) || isExport) { + requestBuilder.sort("name.keyword", SortOrder.Asc, SORT_TYPE_KEYWORD); } } @@ -1408,10 +1410,8 @@ private OpenSearchRequestBuilder buildHierarchyQuery( } } - // Add sorting by score first for relevance, then by fullyQualifiedName for consistent hierarchy - // ordering - requestBuilder.sort("_score", SortOrder.Desc, null); - requestBuilder.sort("fullyQualifiedName", SortOrder.Asc, "keyword"); + requestBuilder.sort(SORT_FIELD_SCORE, SortOrder.Desc, null); + requestBuilder.sort("fullyQualifiedName", SortOrder.Asc, SORT_TYPE_KEYWORD); return requestBuilder; }