Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 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
42 changes: 0 additions & 42 deletions muted-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -630,60 +630,18 @@ tests:
- class: org.elasticsearch.index.mapper.LongFieldMapperTests
method: testSyntheticSource
issue: https://github.com/elastic/elasticsearch/issues/133496
- class: org.elasticsearch.xpack.logsdb.qa.StandardVersusStandardReindexedIntoLogsDbChallengeRestIT
method: testEsqlSource
issue: https://github.com/elastic/elasticsearch/issues/132601
- class: org.elasticsearch.xpack.logsdb.qa.BulkChallengeRestIT
method: testEsqlSource
issue: https://github.com/elastic/elasticsearch/issues/132600
- class: org.elasticsearch.xpack.logsdb.qa.StoredSourceLogsDbVersusReindexedLogsDbChallengeRestIT
method: testEsqlSource
issue: https://github.com/elastic/elasticsearch/issues/132602
- class: org.elasticsearch.xpack.logsdb.qa.BulkChallengeRestIT
method: testMatchAllQuery
issue: https://github.com/elastic/elasticsearch/issues/133497
- class: org.elasticsearch.xpack.logsdb.qa.StandardVersusStandardReindexedIntoLogsDbChallengeRestIT
method: testMatchAllQuery
issue: https://github.com/elastic/elasticsearch/issues/133498
- class: org.elasticsearch.xpack.logsdb.qa.StoredSourceLogsDbVersusReindexedLogsDbChallengeRestIT
method: testMatchAllQuery
issue: https://github.com/elastic/elasticsearch/issues/133499
- class: org.elasticsearch.xpack.logsdb.qa.BulkChallengeRestIT
method: testRandomQueries
issue: https://github.com/elastic/elasticsearch/issues/133503
- class: org.elasticsearch.xpack.logsdb.qa.StandardVersusStandardReindexedIntoLogsDbChallengeRestIT
method: testRandomQueries
issue: https://github.com/elastic/elasticsearch/issues/133504
- class: org.elasticsearch.xpack.logsdb.qa.StoredSourceLogsDbVersusReindexedLogsDbChallengeRestIT
method: testRandomQueries
issue: https://github.com/elastic/elasticsearch/issues/133505
- class: org.elasticsearch.xpack.esql.qa.mixed.MixedClusterEsqlSpecIT
method: test {csv-spec:spatial.ConvertFromStringParseError}
issue: https://github.com/elastic/elasticsearch/issues/133507
- class: org.elasticsearch.test.rest.yaml.RcsCcsCommonYamlTestSuiteIT
method: test {p0=search.vectors/90_sparse_vector/Indexing and searching multi-value sparse vectors in >=8.15}
issue: https://github.com/elastic/elasticsearch/issues/133508
- class: org.elasticsearch.xpack.logsdb.qa.BulkChallengeRestIT
method: testTermsQuery
issue: https://github.com/elastic/elasticsearch/issues/133516
- class: org.elasticsearch.xpack.logsdb.qa.StandardVersusStandardReindexedIntoLogsDbChallengeRestIT
method: testTermsQuery
issue: https://github.com/elastic/elasticsearch/issues/133517
- class: org.elasticsearch.xpack.logsdb.qa.StoredSourceLogsDbVersusReindexedLogsDbChallengeRestIT
method: testTermsQuery
issue: https://github.com/elastic/elasticsearch/issues/133518
- class: org.elasticsearch.xpack.logsdb.qa.LogsDbVersusReindexedLogsDbChallengeRestIT
method: testEsqlSource
issue: https://github.com/elastic/elasticsearch/issues/133520
- class: org.elasticsearch.xpack.esql.action.EsqlActionBreakerIT
method: testTopNPushedToLucene
issue: https://github.com/elastic/elasticsearch/issues/133556
- class: org.elasticsearch.xpack.esql.action.EsqlActionBreakerIT
method: testFilterNestedFields
issue: https://github.com/elastic/elasticsearch/issues/133557
- class: org.elasticsearch.xpack.logsdb.qa.LogsDbVersusReindexedLogsDbChallengeRestIT
method: testMatchAllQuery
issue: https://github.com/elastic/elasticsearch/issues/133560
- class: org.elasticsearch.test.rest.yaml.RcsCcsCommonYamlTestSuiteIT
method: test {p0=search/10_source_filtering/no filtering}
issue: https://github.com/elastic/elasticsearch/issues/133561
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,13 @@ static Map<String, FieldSpecificMatcher> matchers(
return new HashMap<>() {
{
put("keyword", new KeywordMatcher(actualMappings, actualSettings, expectedMappings, expectedSettings));
put("long", new NumberMatcher("long", actualMappings, actualSettings, expectedMappings, expectedSettings));
put("long", new LongMatcher(actualMappings, actualSettings, expectedMappings, expectedSettings));
put("unsigned_long", new UnsignedLongMatcher(actualMappings, actualSettings, expectedMappings, expectedSettings));
put("integer", new NumberMatcher("integer", actualMappings, actualSettings, expectedMappings, expectedSettings));
put("short", new NumberMatcher("short", actualMappings, actualSettings, expectedMappings, expectedSettings));
put("byte", new NumberMatcher("byte", actualMappings, actualSettings, expectedMappings, expectedSettings));
put("double", new NumberMatcher("double", actualMappings, actualSettings, expectedMappings, expectedSettings));
put("float", new NumberMatcher("float", actualMappings, actualSettings, expectedMappings, expectedSettings));
put("integer", new IntegerMatcher(actualMappings, actualSettings, expectedMappings, expectedSettings));
put("short", new ShortMatcher(actualMappings, actualSettings, expectedMappings, expectedSettings));
put("byte", new ByteMatcher( actualMappings, actualSettings, expectedMappings, expectedSettings));
put("double", new DoubleMatcher(actualMappings, actualSettings, expectedMappings, expectedSettings));
put("float", new FloatMatcher(actualMappings, actualSettings, expectedMappings, expectedSettings));
put("half_float", new HalfFloatMatcher(actualMappings, actualSettings, expectedMappings, expectedSettings));
put("scaled_float", new ScaledFloatMatcher(actualMappings, actualSettings, expectedMappings, expectedSettings));
put("counted_keyword", new CountedKeywordMatcher(actualMappings, actualSettings, expectedMappings, expectedSettings));
Expand Down Expand Up @@ -327,9 +327,15 @@ Object convert(Object value, Object nullValue) {
yield nullValueBigInt;
}

yield s;
try {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While testing, found that the unsigned long mapper can accept unicode numbers and needs to be updates.

yield new BigInteger(s);
} catch (NumberFormatException e) {
// malformed
yield value;
}
}
case Long l -> BigInteger.valueOf(l);
case Integer i -> BigInteger.valueOf(i);
default -> value;
};

Expand All @@ -356,31 +362,30 @@ Object convert(Object value, Object nullValue) {
}
}

class NumberMatcher extends GenericMappingAwareMatcher {
abstract class NumberMatcher extends GenericMappingAwareMatcher {

private final FieldType fieldType;
private final NumberFieldMapper.NumberType numberType;

NumberMatcher(
String fieldType,
private NumberMatcher(
FieldType fieldType,
XContentBuilder actualMappings,
Settings.Builder actualSettings,
XContentBuilder expectedMappings,
Settings.Builder expectedSettings
) {
super(fieldType, actualMappings, actualSettings, expectedMappings, expectedSettings);
this.fieldType = FieldType.tryParse(fieldType);
this.numberType = NumberFieldMapper.NumberType.valueOf(this.fieldType.name());
super(fieldType.toString(), actualMappings, actualSettings, expectedMappings, expectedSettings);
this.numberType = NumberFieldMapper.NumberType.valueOf(fieldType.name());
}

@Override
Object convert(Object value, Object nullValue) {
if (value == null) {
return nullValue;
return cast(nullValue);
}

// Special case for number coercion from strings
if (value instanceof String s && s.isEmpty()) {
return nullValue;
return cast(nullValue);
}

// Attempt to coerce string values into numbers
Expand All @@ -394,25 +399,81 @@ Object convert(Object value, Object nullValue) {
}
}

// When a number mapping is coerced, the expected value will come from the above parser and will have the correct java type.
// Whereas, if it fits, the actual value will be in an Integer or a Double. To correctly treat expected and actual values as
// equal the actual value must be cast to the appropriate type.
if (value instanceof Integer v) {
return switch (fieldType) {
case LONG -> v.longValue();
case SHORT -> v.shortValue();
case BYTE -> v.byteValue();
default -> value;
};
}
if (value instanceof Double v) {
return fieldType == FieldType.FLOAT ? v.floatValue() : value;
}
return cast(value);
}

// When a number mapping is coerced, the expected value will come from the above parser and will have the correct java type.
// Whereas, if it fits, the actual value will be in an Integer or a Double. To correctly treat expected and actual values as
// equal the actual value must be cast to the appropriate type.
abstract Object cast(Object value);
}

class LongMatcher extends NumberMatcher {
LongMatcher(XContentBuilder actualMappings, Settings.Builder actualSettings, XContentBuilder expectedMappings, Settings.Builder expectedSettings) {
super(FieldType.LONG, actualMappings, actualSettings, expectedMappings, expectedSettings);
}

@Override
protected Object cast(Object value) {
return value instanceof Integer v ? v.longValue() : value;
}
}

class IntegerMatcher extends NumberMatcher {
IntegerMatcher(XContentBuilder actualMappings, Settings.Builder actualSettings, XContentBuilder expectedMappings, Settings.Builder expectedSettings) {
super(FieldType.INTEGER, actualMappings, actualSettings, expectedMappings, expectedSettings);
}

@Override
protected Object cast(Object value) {
return value;
}
}

class ShortMatcher extends NumberMatcher {
ShortMatcher(XContentBuilder actualMappings, Settings.Builder actualSettings, XContentBuilder expectedMappings, Settings.Builder expectedSettings) {
super(FieldType.SHORT, actualMappings, actualSettings, expectedMappings, expectedSettings);
}

@Override
protected Object cast(Object value) {
return value instanceof Integer v ? v.shortValue() : value;
}
}

class ByteMatcher extends NumberMatcher {
ByteMatcher(XContentBuilder actualMappings, Settings.Builder actualSettings, XContentBuilder expectedMappings, Settings.Builder expectedSettings) {
super(FieldType.BYTE, actualMappings, actualSettings, expectedMappings, expectedSettings);
}

@Override
protected Object cast(Object value) {
return value instanceof Integer v ? v.byteValue() : value;
}
}

class DoubleMatcher extends NumberMatcher {
DoubleMatcher(XContentBuilder actualMappings, Settings.Builder actualSettings, XContentBuilder expectedMappings, Settings.Builder expectedSettings) {
super(FieldType.DOUBLE, actualMappings, actualSettings, expectedMappings, expectedSettings);
}

@Override
protected Object cast(Object value) {
return value;
}
}

class FloatMatcher extends NumberMatcher {
FloatMatcher(XContentBuilder actualMappings, Settings.Builder actualSettings, XContentBuilder expectedMappings, Settings.Builder expectedSettings) {
super(FieldType.FLOAT, actualMappings, actualSettings, expectedMappings, expectedSettings);
}

@Override
protected Object cast(Object value) {
return value instanceof Integer v ? v.floatValue() : value;
}
}

class BooleanMatcher extends GenericMappingAwareMatcher {
BooleanMatcher(
XContentBuilder actualMappings,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,4 +160,52 @@ public void testCoercedNumberField() throws IOException {
var sut = new SourceMatcher(Map.of(), mapping, Settings.builder(), mapping, Settings.builder(), actual, expected, false);
assertTrue(sut.match().isMatch());
}

public void testNullValueIntegerNumericMatcherCastsType() throws IOException {
String type = randomFrom("byte", "short", "integer", "long");
int nullValue = 51;
List<Map<String, Object>> actual = List.of(Map.of("field", List.of(5, nullValue, "")));
List<Map<String, Object>> expected = List.of(Map.of("field", List.of(5, nullValue, nullValue)));

var mapping = XContentBuilder.builder(XContentType.JSON.xContent());
mapping.startObject();
mapping.startObject("_doc");
{
mapping.startObject("field");
{
mapping.field("type", type);
mapping.field("null_value", nullValue);
}
mapping.endObject();
}
mapping.endObject();
mapping.endObject();

var sut = new SourceMatcher(Map.of(), mapping, Settings.builder(), mapping, Settings.builder(), actual, expected, false);
assertTrue(sut.match().isMatch());
}

public void testNullValueFloatingPointNumericMatcherCastsType() throws IOException {
String type = randomFrom("float", "double");
double nullValue = 51.123;
List<Map<String, Object>> actual = List.of(Map.of("field", List.of(5, nullValue, "")));
List<Map<String, Object>> expected = List.of(Map.of("field", List.of(5, nullValue, nullValue)));

var mapping = XContentBuilder.builder(XContentType.JSON.xContent());
mapping.startObject();
mapping.startObject("_doc");
{
mapping.startObject("field");
{
mapping.field("type", type);
mapping.field("null_value", nullValue);
}
mapping.endObject();
}
mapping.endObject();
mapping.endObject();

var sut = new SourceMatcher(Map.of(), mapping, Settings.builder(), mapping, Settings.builder(), actual, expected, false);
assertTrue(sut.match().isMatch());
}
}