Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/changelog/132162.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
pr: 132162
summary: Fix default missing index sort value of `data_nanos` pre 7.14
area: Search
type: bug
issues:
- 132040
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

import org.apache.lucene.search.Sort;
import org.apache.lucene.search.SortField;
import org.apache.lucene.search.SortedNumericSelector;
import org.apache.lucene.search.SortedNumericSortField;
import org.apache.lucene.search.SortedSetSortField;
import org.elasticsearch.common.settings.Settings;
Expand Down Expand Up @@ -80,6 +81,33 @@ public void testIndexSort() {
assertSortedSegments("test", indexSort);
}

public void testIndexSortDateNanos() {
prepareCreate("test").setSettings(
Settings.builder()
.put(indexSettings())
.put("index.number_of_shards", "1")
.put("index.number_of_replicas", "1")
.put("index.sort.field", "@timestamp")
.put("index.sort.order", "desc")
).setMapping("""
{
"properties": {
"@timestamp": {
"type": "date_nanos"
}
}
}
""").get();

flushAndRefresh();
ensureYellow();

SortField sf = new SortedNumericSortField("@timestamp", SortField.Type.LONG, true, SortedNumericSelector.Type.MAX);
sf.setMissingValue(0L);
Sort expectedIndexSort = new Sort(sf);
assertSortedSegments("test", expectedIndexSort);
}

public void testInvalidIndexSort() {
IllegalArgumentException exc = expectThrows(
IllegalArgumentException.class,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,12 +146,21 @@ public SortField sortField(
boolean reverse
) {
SortField sortField = sortField(missingValue, sortMode, nested, reverse);
// we introduced INT sort type in 8.19 and from 9.1
if (getNumericType().sortFieldType != SortField.Type.INT
if (getNumericType() == NumericType.DATE_NANOSECONDS
&& indexCreatedVersion.before(IndexVersions.V_7_14_0)
&& missingValue == null
&& Long.valueOf(0L).equals(sortField.getMissingValue())) {
// 7.14 changed the default missing value of sort on date_nanos, from Long.MIN_VALUE
// to 0L - for compatibility we require to a missing value of MIN_VALUE to allow to
// open the index.
sortField.setMissingValue(Long.MIN_VALUE);
return sortField;
} else if (getNumericType().sortFieldType != SortField.Type.INT
// we introduced INT sort type in 8.19 and from 9.1
|| indexCreatedVersion.onOrAfter(IndexVersions.INDEX_INT_SORT_INT_TYPE)
|| indexCreatedVersion.between(IndexVersions.INDEX_INT_SORT_INT_TYPE_8_19, UPGRADE_TO_LUCENE_10_0_0)) {
return sortField;
}
return sortField;
}
if ((sortField instanceof SortedNumericSortField) == false) {
return sortField;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.apache.lucene.search.Sort;
import org.elasticsearch.cluster.metadata.IndexMetadata;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.time.DateUtils;
import org.elasticsearch.common.util.Maps;
import org.elasticsearch.index.fielddata.FieldDataContext;
import org.elasticsearch.index.fielddata.IndexFieldData;
Expand All @@ -30,9 +31,12 @@
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.test.ESTestCase;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;

import static org.elasticsearch.index.IndexSettingsTests.newIndexMeta;
import static org.hamcrest.Matchers.arrayWithSize;
Expand Down Expand Up @@ -174,6 +178,58 @@ public void testSortingAgainstAliasesPre713() {
);
}

public void testSortMissingValueDateNanoFieldPre714() {
MappedFieldType tsField = new DateFieldMapper.DateFieldType("@timestamp", true, DateFieldMapper.Resolution.NANOSECONDS);
var indexSettingsBuilder = Settings.builder();
indexSettingsBuilder.put("index.sort.field", "@timestamp");
indexSettingsBuilder.put("index.sort.order", "desc");

// test with index version 7.13 and before
var pre714Versions = Stream.concat(Stream.of(IndexVersions.V_7_13_0), randomVersionsBefore(IndexVersions.V_7_13_0)).toList();
for (var version : pre714Versions) {
indexSettingsBuilder.put(IndexMetadata.SETTING_VERSION_CREATED, version);
Sort sort = buildIndexSort(indexSettings(indexSettingsBuilder.build()), Map.of("@timestamp", tsField));
assertThat(sort.getSort(), arrayWithSize(1));
assertThat(sort.getSort()[0].getField(), equalTo("@timestamp"));
assertThat(sort.getSort()[0].getMissingValue(), equalTo(Long.MIN_VALUE));
}

// now test with index version 7.14 and after
var post713Versions = Stream.concat(Stream.of(IndexVersions.V_7_14_0), randomVersionsAfter(IndexVersions.V_7_14_0)).toList();
for (var version : post713Versions) {
indexSettingsBuilder.put(IndexMetadata.SETTING_VERSION_CREATED, version);
Sort sort = buildIndexSort(indexSettings(indexSettingsBuilder.build()), Map.of("@timestamp", tsField));
assertThat(sort.getSort(), arrayWithSize(1));
assertThat(sort.getSort()[0].getField(), equalTo("@timestamp"));
assertThat(sort.getSort()[0].getMissingValue(), equalTo(0L));
}

// asc order has not changed behaviour in any version
indexSettingsBuilder.put("index.sort.order", "asc");
var allVersions = Stream.concat(post713Versions.stream(), pre714Versions.stream()).toList();
for (var version : allVersions) {
indexSettingsBuilder.put(IndexMetadata.SETTING_VERSION_CREATED, version);
Sort sort = buildIndexSort(indexSettings(indexSettingsBuilder.build()), Map.of("@timestamp", tsField));
assertThat(sort.getSort(), arrayWithSize(1));
assertThat(sort.getSort()[0].getField(), equalTo("@timestamp"));
assertThat(sort.getSort()[0].getMissingValue(), equalTo(DateUtils.MAX_NANOSECOND));
}

// ensure no change in behaviour when a missing value is set
indexSettingsBuilder.put("index.sort.missing", "_first");
for (var version : allVersions) {
indexSettingsBuilder.put(IndexMetadata.SETTING_VERSION_CREATED, version);
Sort sort = buildIndexSort(indexSettings(indexSettingsBuilder.build()), Map.of("@timestamp", tsField));
assertThat(sort.getSort()[0].getMissingValue(), equalTo(0L));
}
indexSettingsBuilder.put("index.sort.missing", "_last");
for (var version : allVersions) {
indexSettingsBuilder.put(IndexMetadata.SETTING_VERSION_CREATED, version);
Sort sort = buildIndexSort(indexSettings(indexSettingsBuilder.build()), Map.of("@timestamp", tsField));
assertThat(sort.getSort()[0].getMissingValue(), equalTo(Long.MAX_VALUE));
}
}

public void testTimeSeriesMode() {
IndexSettings indexSettings = indexSettings(
Settings.builder()
Expand Down Expand Up @@ -224,4 +280,24 @@ private Sort buildIndexSort(IndexSettings indexSettings, Map<String, MappedField
)
);
}

/* Returns a stream of versions before the given version */
Stream<IndexVersion> randomVersionsBefore(IndexVersion indexVersion) {
var versions = IndexVersions.getAllVersions().stream().filter(v -> v.before(indexVersion)).toList();
List<IndexVersion> ret = new ArrayList<>();
for (int i = 0; i < 10; i++) {
ret.add(randomValueOtherThanMany(ret::contains, () -> randomFrom(versions)));
}
return ret.stream();
}

/* Returns a stream of versions after the given version */
Stream<IndexVersion> randomVersionsAfter(IndexVersion indexVersion) {
var versions = IndexVersions.getAllVersions().stream().filter(v -> v.after(indexVersion)).toList();
List<IndexVersion> ret = new ArrayList<>();
for (int i = 0; i < 10; i++) {
ret.add(randomValueOtherThanMany(ret::contains, () -> randomFrom(versions)));
}
return ret.stream();
}
}