Skip to content

Commit a8d1f08

Browse files
Coerce numeric strings in data generation matchers (#133264)
Number mapping types can accept strings which are coerced into the correct number types. To correctly check for equality between numeric types, the NumberMatcher in the data generation tests needs to parse numeric strings.
1 parent 35aea72 commit a8d1f08

File tree

3 files changed

+55
-9
lines changed

3 files changed

+55
-9
lines changed

muted-tests.yml

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -441,15 +441,6 @@ tests:
441441
- class: org.elasticsearch.xpack.esql.qa.single_node.EsqlSpecIT
442442
method: test {csv-spec:spatial.ConvertFromStringParseError}
443443
issue: https://github.com/elastic/elasticsearch/issues/132558
444-
- class: org.elasticsearch.xpack.logsdb.qa.BulkChallengeRestIT
445-
method: testEsqlSource
446-
issue: https://github.com/elastic/elasticsearch/issues/132600
447-
- class: org.elasticsearch.xpack.logsdb.qa.StandardVersusStandardReindexedIntoLogsDbChallengeRestIT
448-
method: testEsqlSource
449-
issue: https://github.com/elastic/elasticsearch/issues/132601
450-
- class: org.elasticsearch.xpack.logsdb.qa.StoredSourceLogsDbVersusReindexedLogsDbChallengeRestIT
451-
method: testEsqlSource
452-
issue: https://github.com/elastic/elasticsearch/issues/132602
453444
- class: org.elasticsearch.xpack.ml.integration.RevertModelSnapshotIT
454445
method: testRevertModelSnapshot_DeleteInterveningResults
455446
issue: https://github.com/elastic/elasticsearch/issues/132349

test/framework/src/main/java/org/elasticsearch/datageneration/matchers/source/FieldSpecificMatcher.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,13 @@
1313
import org.elasticsearch.common.geo.GeoPoint;
1414
import org.elasticsearch.common.settings.Settings;
1515
import org.elasticsearch.core.Booleans;
16+
import org.elasticsearch.datageneration.FieldType;
1617
import org.elasticsearch.datageneration.matchers.MatchResult;
1718
import org.elasticsearch.index.mapper.DateFieldMapper;
19+
import org.elasticsearch.index.mapper.NumberFieldMapper;
1820
import org.elasticsearch.xcontent.XContentBuilder;
21+
import org.elasticsearch.xcontent.XContentParserConfiguration;
22+
import org.elasticsearch.xcontent.XContentType;
1923

2024
import java.math.BigInteger;
2125
import java.time.Instant;
@@ -353,6 +357,10 @@ Object convert(Object value, Object nullValue) {
353357
}
354358

355359
class NumberMatcher extends GenericMappingAwareMatcher {
360+
361+
private final FieldType fieldType;
362+
private final NumberFieldMapper.NumberType numberType;
363+
356364
NumberMatcher(
357365
String fieldType,
358366
XContentBuilder actualMappings,
@@ -361,6 +369,8 @@ class NumberMatcher extends GenericMappingAwareMatcher {
361369
Settings.Builder expectedSettings
362370
) {
363371
super(fieldType, actualMappings, actualSettings, expectedMappings, expectedSettings);
372+
this.fieldType = FieldType.tryParse(fieldType);
373+
this.numberType = NumberFieldMapper.NumberType.valueOf(this.fieldType.name());
364374
}
365375

366376
@Override
@@ -373,6 +383,32 @@ Object convert(Object value, Object nullValue) {
373383
return nullValue;
374384
}
375385

386+
// Attempt to coerce string values into numbers
387+
if (value instanceof String s) {
388+
try (var parser = XContentType.JSON.xContent().createParser(XContentParserConfiguration.EMPTY, "\"" + s + "\"")) {
389+
parser.nextToken();
390+
return numberType.parse(parser, true);
391+
} catch (Exception e) {
392+
// malformed string
393+
return value;
394+
}
395+
}
396+
397+
// When a number mapping is coerced, the expected value will come from the above parser and will have the correct java type.
398+
// Whereas, if it fits, the actual value will be in an Integer or a Double. To correctly treat expected and actual values as
399+
// equal the actual value must be cast to the appropriate type.
400+
if (value instanceof Integer v) {
401+
return switch (fieldType) {
402+
case LONG -> v.longValue();
403+
case SHORT -> v.shortValue();
404+
case BYTE -> v.byteValue();
405+
default -> value;
406+
};
407+
}
408+
if (value instanceof Double v) {
409+
return fieldType == FieldType.FLOAT ? v.floatValue() : value;
410+
}
411+
376412
return value;
377413
}
378414
}

test/framework/src/test/java/org/elasticsearch/logsdb/datageneration/SourceMatcherTests.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,4 +141,23 @@ public void testCountedKeywordMismatch() throws IOException {
141141
var sut = new SourceMatcher(Map.of(), mapping, Settings.builder(), mapping, Settings.builder(), actual, expected, false);
142142
assertFalse(sut.match().isMatch());
143143
}
144+
145+
public void testCoercedNumberField() throws IOException {
146+
147+
// Parsing non-ascii digit strings only works for `long` mappings.
148+
List<Map<String, Object>> expected = List.of(Map.of("field", List.of("꧕", "123")));
149+
List<Map<String, Object>> actual = List.of(Map.of("field", List.of(5, 123)));
150+
151+
var mapping = XContentBuilder.builder(XContentType.JSON.xContent());
152+
mapping.startObject();
153+
mapping.startObject("_doc");
154+
{
155+
mapping.startObject("field").field("type", "long").endObject();
156+
}
157+
mapping.endObject();
158+
mapping.endObject();
159+
160+
var sut = new SourceMatcher(Map.of(), mapping, Settings.builder(), mapping, Settings.builder(), actual, expected, false);
161+
assertTrue(sut.match().isMatch());
162+
}
144163
}

0 commit comments

Comments
 (0)