mirror of
https://github.com/elastic/elasticsearch.git
synced 2025-06-28 09:28:55 -04:00
Enable sort optimization on int, short and byte fields (#127968)
Before this PR sorting on integer, short and byte fields types used SortField.Type.LONG. This made sort optimization impossible for these field types. This PR uses SortField.Type.INT for integer, short and byte fields. This enables sort optimization. There are several caveats with changing sort type that are addressed: - Before mixed sort on integer and long fields was automatically supported, as both field types used SortField.TYPE.LONG. Now when merging results from different shards, we need to convert sort to LONG and results to long values. - Similar for collapsing when there is mixed INT and LONG sort types. - Index sorting. Similarly, before for index sorting on integer field, SortField.Type.LONG was used. This sort type is stored in the index writer config on disk and can't be modified. Now when providing sortField() for index sorting, we need to account for index version: for older indices return sort with SortField.Type.LONG and for new indices return SortField.Type.INT. --- There is only 1 change that may be considered not backwards compatible: Before if an integer field was [missing a value](https://www.elastic.co/docs/reference/elasticsearch/rest-apis/sort-search-results#_missing_values) , it sort values will return Long.MAX_VALUE in a search response. With this integer, it sort valeu will return Integer.MAX_VALUE. But I think this change is ok, as in our documentation, we don't provide information what value will be returned, we just say it will be sorted last. --- Also closes #127965 (as same type validation in added for collapse queries)
This commit is contained in:
parent
d75daa7155
commit
080a0cdd89
20 changed files with 670 additions and 85 deletions
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the "Elastic License
|
||||
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
|
||||
* Public License v 1"; you may not use this file except in compliance with, at
|
||||
* your election, the "Elastic License 2.0", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
package org.elasticsearch.upgrades;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.Name;
|
||||
|
||||
import org.elasticsearch.client.Request;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Tests that index sorting works correctly after a rolling upgrade.
|
||||
*/
|
||||
public class IndexSortUpgradeIT extends AbstractRollingUpgradeTestCase {
|
||||
|
||||
public IndexSortUpgradeIT(@Name("upgradedNodes") int upgradedNodes) {
|
||||
super(upgradedNodes);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testIndexSortForNumericTypes() throws Exception {
|
||||
record IndexConfig(String indexName, String fieldName, String fieldType) {}
|
||||
var configs = new IndexConfig[] {
|
||||
new IndexConfig("index_byte", "byte_field", "byte"),
|
||||
new IndexConfig("index_short", "short_field", "short"),
|
||||
new IndexConfig("index_int", "int_field", "integer") };
|
||||
|
||||
if (isOldCluster()) {
|
||||
int numShards = randomIntBetween(1, 3);
|
||||
for (var config : configs) {
|
||||
createIndex(
|
||||
config.indexName(),
|
||||
Settings.builder()
|
||||
.put("index.number_of_shards", numShards)
|
||||
.put("index.number_of_replicas", 0)
|
||||
.put("index.sort.field", config.fieldName())
|
||||
.put("index.sort.order", "desc")
|
||||
.build(),
|
||||
"""
|
||||
{
|
||||
"properties": {
|
||||
"%s": {
|
||||
"type": "%s"
|
||||
}
|
||||
}
|
||||
}
|
||||
""".formatted(config.fieldName(), config.fieldType())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final int numDocs = randomIntBetween(10, 25);
|
||||
for (var config : configs) {
|
||||
var bulkRequest = new Request("POST", "/" + config.indexName() + "/_bulk");
|
||||
StringBuilder bulkBody = new StringBuilder();
|
||||
for (int i = 0; i < numDocs; i++) {
|
||||
bulkBody.append("{\"index\": {}}\n");
|
||||
bulkBody.append("{\"" + config.fieldName() + "\": ").append(i).append("}\n");
|
||||
}
|
||||
bulkRequest.setJsonEntity(bulkBody.toString());
|
||||
bulkRequest.addParameter("refresh", "true");
|
||||
var bulkResponse = client().performRequest(bulkRequest);
|
||||
assertOK(bulkResponse);
|
||||
|
||||
var searchRequest = new Request("GET", "/" + config.indexName() + "/_search");
|
||||
searchRequest.setJsonEntity("""
|
||||
{
|
||||
"query": {
|
||||
"match_all": {}
|
||||
},
|
||||
"sort": {
|
||||
"%s": {
|
||||
"order": "desc"
|
||||
}
|
||||
}
|
||||
}
|
||||
""".formatted(config.fieldName()));
|
||||
var searchResponse = client().performRequest(searchRequest);
|
||||
assertOK(searchResponse);
|
||||
var responseBody = entityAsMap(searchResponse);
|
||||
var hits = (List<Map<String, Object>>) ((Map<String, Object>) responseBody.get("hits")).get("hits");
|
||||
int previousValue = ((Number) ((List<Object>) hits.get(0).get("sort")).get(0)).intValue();
|
||||
;
|
||||
for (int i = 1; i < hits.size(); i++) {
|
||||
int currentValue = ((Number) ((List<Object>) hits.get(i).get("sort")).get(0)).intValue();
|
||||
assertTrue("Sort values are not in desc order ", previousValue >= currentValue);
|
||||
previousValue = currentValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue