Allows nanosecond resolution in search_after (#60328) This fixes `search_after` to properly parse string formatted dates that have nanosecond resolution. Closes #52424
This commit is contained in:
parent
b0d601fa63
commit
2cde43b799
|
@ -1,4 +1,4 @@
|
||||||
setup:
|
"search with search_after parameter":
|
||||||
- do:
|
- do:
|
||||||
indices.create:
|
indices.create:
|
||||||
index: test
|
index: test
|
||||||
|
@ -24,9 +24,6 @@ setup:
|
||||||
indices.refresh:
|
indices.refresh:
|
||||||
index: test
|
index: test
|
||||||
|
|
||||||
---
|
|
||||||
"search with search_after parameter":
|
|
||||||
|
|
||||||
- do:
|
- do:
|
||||||
search:
|
search:
|
||||||
rest_total_hits_as_int: true
|
rest_total_hits_as_int: true
|
||||||
|
@ -97,3 +94,137 @@ setup:
|
||||||
|
|
||||||
- match: {hits.total: 3}
|
- match: {hits.total: 3}
|
||||||
- length: {hits.hits: 0 }
|
- length: {hits.hits: 0 }
|
||||||
|
|
||||||
|
---
|
||||||
|
"date":
|
||||||
|
- skip:
|
||||||
|
version: " - 7.9.99"
|
||||||
|
reason: fixed in 7.10.0
|
||||||
|
|
||||||
|
- do:
|
||||||
|
indices.create:
|
||||||
|
index: test
|
||||||
|
body:
|
||||||
|
mappings:
|
||||||
|
properties:
|
||||||
|
timestamp:
|
||||||
|
type: date
|
||||||
|
format: yyyy-MM-dd HH:mm:ss.SSS
|
||||||
|
- do:
|
||||||
|
bulk:
|
||||||
|
refresh: true
|
||||||
|
index: test
|
||||||
|
body: |
|
||||||
|
{"index":{}}
|
||||||
|
{"timestamp":"2019-10-21 00:30:04.828"}
|
||||||
|
{"index":{}}
|
||||||
|
{"timestamp":"2019-10-21 08:30:04.828"}
|
||||||
|
|
||||||
|
- do:
|
||||||
|
search:
|
||||||
|
index: test
|
||||||
|
rest_total_hits_as_int: true
|
||||||
|
body:
|
||||||
|
size: 1
|
||||||
|
sort: [{ timestamp: desc }]
|
||||||
|
- match: {hits.total: 2 }
|
||||||
|
- length: {hits.hits: 1 }
|
||||||
|
- match: {hits.hits.0._index: test }
|
||||||
|
- match: {hits.hits.0._source.timestamp: "2019-10-21 08:30:04.828" }
|
||||||
|
- match: {hits.hits.0.sort: [1571646604828] }
|
||||||
|
|
||||||
|
# search_after with the sort
|
||||||
|
- do:
|
||||||
|
search:
|
||||||
|
index: test
|
||||||
|
rest_total_hits_as_int: true
|
||||||
|
body:
|
||||||
|
size: 1
|
||||||
|
sort: [{ timestamp: desc }]
|
||||||
|
search_after: [1571646604828]
|
||||||
|
- match: {hits.total: 2 }
|
||||||
|
- length: {hits.hits: 1 }
|
||||||
|
- match: {hits.hits.0._index: test }
|
||||||
|
- match: {hits.hits.0._source.timestamp: "2019-10-21 00:30:04.828" }
|
||||||
|
- match: {hits.hits.0.sort: [1571617804828] }
|
||||||
|
|
||||||
|
# search_after with the formatted date
|
||||||
|
- do:
|
||||||
|
search:
|
||||||
|
index: test
|
||||||
|
rest_total_hits_as_int: true
|
||||||
|
body:
|
||||||
|
size: 1
|
||||||
|
sort: [{ timestamp: desc }]
|
||||||
|
search_after: ["2019-10-21 08:30:04.828"]
|
||||||
|
- match: {hits.total: 2 }
|
||||||
|
- length: {hits.hits: 1 }
|
||||||
|
- match: {hits.hits.0._index: test }
|
||||||
|
- match: {hits.hits.0._source.timestamp: "2019-10-21 00:30:04.828" }
|
||||||
|
- match: {hits.hits.0.sort: [1571617804828] }
|
||||||
|
|
||||||
|
---
|
||||||
|
"date_nanos":
|
||||||
|
- skip:
|
||||||
|
version: " - 7.9.99"
|
||||||
|
reason: fixed in 7.10.0
|
||||||
|
|
||||||
|
- do:
|
||||||
|
indices.create:
|
||||||
|
index: test
|
||||||
|
body:
|
||||||
|
mappings:
|
||||||
|
properties:
|
||||||
|
timestamp:
|
||||||
|
type: date_nanos
|
||||||
|
format: yyyy-MM-dd HH:mm:ss.SSSSSS
|
||||||
|
- do:
|
||||||
|
bulk:
|
||||||
|
refresh: true
|
||||||
|
index: test
|
||||||
|
body: |
|
||||||
|
{"index":{}}
|
||||||
|
{"timestamp":"2019-10-21 00:30:04.828740"}
|
||||||
|
{"index":{}}
|
||||||
|
{"timestamp":"2019-10-21 08:30:04.828733"}
|
||||||
|
|
||||||
|
- do:
|
||||||
|
search:
|
||||||
|
index: test
|
||||||
|
body:
|
||||||
|
size: 1
|
||||||
|
sort: [{ timestamp: desc }]
|
||||||
|
- match: {hits.total.value: 2 }
|
||||||
|
- length: {hits.hits: 1 }
|
||||||
|
- match: {hits.hits.0._index: test }
|
||||||
|
- match: {hits.hits.0._source.timestamp: "2019-10-21 08:30:04.828733" }
|
||||||
|
- match: {hits.hits.0.sort: [1571646604828733000] }
|
||||||
|
|
||||||
|
# search_after with the sort
|
||||||
|
- do:
|
||||||
|
search:
|
||||||
|
index: test
|
||||||
|
body:
|
||||||
|
size: 1
|
||||||
|
sort: [{ timestamp: desc }]
|
||||||
|
search_after: [1571646604828733000]
|
||||||
|
- match: {hits.total.value: 2 }
|
||||||
|
- length: {hits.hits: 1 }
|
||||||
|
- match: {hits.hits.0._index: test }
|
||||||
|
- match: {hits.hits.0._source.timestamp: "2019-10-21 00:30:04.828740" }
|
||||||
|
- match: {hits.hits.0.sort: [1571617804828740000] }
|
||||||
|
|
||||||
|
# search_after with the formatted date
|
||||||
|
- do:
|
||||||
|
search:
|
||||||
|
index: test
|
||||||
|
body:
|
||||||
|
size: 1
|
||||||
|
sort: [{ timestamp: desc }]
|
||||||
|
search_after: ["2019-10-21 08:30:04.828733"]
|
||||||
|
- match: {hits.total.value: 2 }
|
||||||
|
- length: {hits.hits: 1 }
|
||||||
|
- match: {hits.hits.0._index: test }
|
||||||
|
- match: {hits.hits.0._source.timestamp: "2019-10-21 00:30:04.828740" }
|
||||||
|
- match: {hits.hits.0.sort: [1571617804828740000] }
|
||||||
|
|
||||||
|
|
|
@ -100,7 +100,7 @@ public class SortedNumericIndexFieldData extends IndexNumericFieldData {
|
||||||
@Override
|
@Override
|
||||||
protected XFieldComparatorSource dateComparatorSource(Object missingValue, MultiValueMode sortMode, Nested nested) {
|
protected XFieldComparatorSource dateComparatorSource(Object missingValue, MultiValueMode sortMode, Nested nested) {
|
||||||
if (numericType == NumericType.DATE_NANOSECONDS) {
|
if (numericType == NumericType.DATE_NANOSECONDS) {
|
||||||
// converts date values to nanosecond resolution
|
// converts date_nanos values to millisecond resolution
|
||||||
return new LongValuesComparatorSource(this, missingValue,
|
return new LongValuesComparatorSource(this, missingValue,
|
||||||
sortMode, nested, dvs -> convertNumeric(dvs, DateUtils::toMilliSeconds));
|
sortMode, nested, dvs -> convertNumeric(dvs, DateUtils::toMilliSeconds));
|
||||||
}
|
}
|
||||||
|
@ -110,7 +110,7 @@ public class SortedNumericIndexFieldData extends IndexNumericFieldData {
|
||||||
@Override
|
@Override
|
||||||
protected XFieldComparatorSource dateNanosComparatorSource(Object missingValue, MultiValueMode sortMode, Nested nested) {
|
protected XFieldComparatorSource dateNanosComparatorSource(Object missingValue, MultiValueMode sortMode, Nested nested) {
|
||||||
if (numericType == NumericType.DATE) {
|
if (numericType == NumericType.DATE) {
|
||||||
// converts date_nanos values to millisecond resolution
|
// converts date values to nanosecond resolution
|
||||||
return new LongValuesComparatorSource(this, missingValue,
|
return new LongValuesComparatorSource(this, missingValue,
|
||||||
sortMode, nested, dvs -> convertNumeric(dvs, DateUtils::toNanoSeconds));
|
sortMode, nested, dvs -> convertNumeric(dvs, DateUtils::toNanoSeconds));
|
||||||
}
|
}
|
||||||
|
|
|
@ -451,6 +451,7 @@ public final class DateFieldMapper extends ParametrizedFieldMapper {
|
||||||
}
|
}
|
||||||
// the resolution here is always set to milliseconds, as aggregations use this formatter mainly and those are always in
|
// the resolution here is always set to milliseconds, as aggregations use this formatter mainly and those are always in
|
||||||
// milliseconds. The only special case here is docvalue fields, which are handled somewhere else
|
// milliseconds. The only special case here is docvalue fields, which are handled somewhere else
|
||||||
|
// TODO maybe aggs should force millis because lots so of other places want nanos?
|
||||||
return new DocValueFormat.DateTime(dateTimeFormatter, timeZone, Resolution.MILLISECONDS);
|
return new DocValueFormat.DateTime(dateTimeFormatter, timeZone, Resolution.MILLISECONDS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -274,6 +274,11 @@ public interface DocValueFormat extends NamedWriteable {
|
||||||
public double parseDouble(String value, boolean roundUp, LongSupplier now) {
|
public double parseDouble(String value, boolean roundUp, LongSupplier now) {
|
||||||
return parseLong(value, roundUp, now);
|
return parseLong(value, roundUp, now);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "DocValueFormat.DateTime(" + formatter + ", " + timeZone + ", " + resolution + ")";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DocValueFormat GEOHASH = new DocValueFormat() {
|
DocValueFormat GEOHASH = new DocValueFormat() {
|
||||||
|
|
|
@ -404,6 +404,7 @@ public class FieldSortBuilder extends SortBuilder<FieldSortBuilder> {
|
||||||
throw new QueryShardException(context, "we only support AVG, MEDIAN and SUM on number based fields");
|
throw new QueryShardException(context, "we only support AVG, MEDIAN and SUM on number based fields");
|
||||||
}
|
}
|
||||||
final SortField field;
|
final SortField field;
|
||||||
|
boolean isNanosecond = false;
|
||||||
if (numericType != null) {
|
if (numericType != null) {
|
||||||
if (fieldData instanceof IndexNumericFieldData == false) {
|
if (fieldData instanceof IndexNumericFieldData == false) {
|
||||||
throw new QueryShardException(context,
|
throw new QueryShardException(context,
|
||||||
|
@ -414,8 +415,15 @@ public class FieldSortBuilder extends SortBuilder<FieldSortBuilder> {
|
||||||
field = numericFieldData.sortField(resolvedType, missing, localSortMode(), nested, reverse);
|
field = numericFieldData.sortField(resolvedType, missing, localSortMode(), nested, reverse);
|
||||||
} else {
|
} else {
|
||||||
field = fieldData.sortField(missing, localSortMode(), nested, reverse);
|
field = fieldData.sortField(missing, localSortMode(), nested, reverse);
|
||||||
|
if (fieldData instanceof IndexNumericFieldData) {
|
||||||
|
isNanosecond = ((IndexNumericFieldData) fieldData).getNumericType() == NumericType.DATE_NANOSECONDS;
|
||||||
}
|
}
|
||||||
return new SortFieldAndFormat(field, fieldType.docValueFormat(null, null));
|
}
|
||||||
|
DocValueFormat format = fieldType.docValueFormat(null, null);
|
||||||
|
if (isNanosecond) {
|
||||||
|
format = DocValueFormat.withNanosecondResolution(format);
|
||||||
|
}
|
||||||
|
return new SortFieldAndFormat(field, format);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean canRewriteToMatchNone() {
|
public boolean canRewriteToMatchNone() {
|
||||||
|
|
|
@ -80,7 +80,7 @@ public class FieldSortBuilderTests extends AbstractSortTestCase<FieldSortBuilder
|
||||||
/**
|
/**
|
||||||
* {@link #provideMappedFieldType(String)} will return a
|
* {@link #provideMappedFieldType(String)} will return a
|
||||||
*/
|
*/
|
||||||
private static String MAPPED_STRING_FIELDNAME = "_stringField";
|
private static final String MAPPED_STRING_FIELDNAME = "_stringField";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected FieldSortBuilder createTestItem() {
|
protected FieldSortBuilder createTestItem() {
|
||||||
|
|
Loading…
Reference in New Issue