markap14 commented on a change in pull request #4934:
URL: https://github.com/apache/nifi/pull/4934#discussion_r604190380
##########
File path:
nifi-nar-bundles/nifi-standard-services/nifi-record-serialization-services-bundle/nifi-record-serialization-services/src/test/java/org/apache/nifi/json/TestJsonTreeRowRecordReader.java
##########
@@ -726,4 +732,383 @@ public void testIncorrectSchema() throws IOException,
MalformedRecordException {
assertTrue(msg.contains("Boolean"));
}
}
+
+ @Test
+ public void testMergeOfSimilarRecords() throws Exception {
+ // GIVEN
+ String jsonPath = "src/test/resources/json/similar-records.json";
+
+ RecordSchema expectedSchema = new SimpleRecordSchema(Arrays.asList(
+ new RecordField("integer", RecordFieldType.INT.getDataType()),
+ new RecordField("boolean", RecordFieldType.BOOLEAN.getDataType()),
+ new RecordField("booleanOrString",
RecordFieldType.CHOICE.getChoiceDataType(
+ RecordFieldType.BOOLEAN.getDataType(),
+ RecordFieldType.STRING.getDataType()
+ )),
+ new RecordField("string", RecordFieldType.STRING.getDataType())
+ ));
+
+ List<Object> expected = Arrays.asList(
+ new MapRecord(expectedSchema, new HashMap<String, Object>(){{
+ put("integer", 1);
+ put("boolean", true);
+ put("booleanOrString", true);
+ }}),
+ new MapRecord(expectedSchema, new HashMap<String, Object>(){{
+ put("integer", 2);
+ put("string", "stringValue2");
+ put("booleanOrString", "booleanOrStringValue2");
+ }})
+ );
+
+ // WHEN
+ // THEN
+ testReadRecords(jsonPath, expected);
+ }
+
+ @Test
+ public void testChoiceOfEmbeddedSimilarRecords() throws Exception {
+ // GIVEN
+ String jsonPath =
"src/test/resources/json/choice-of-embedded-similar-records.json";
+
+ SimpleRecordSchema expectedRecordSchema1 = new
SimpleRecordSchema(Arrays.asList(
+ new RecordField("integer", RecordFieldType.INT.getDataType()),
+ new RecordField("boolean", RecordFieldType.BOOLEAN.getDataType())
+ ));
+ SimpleRecordSchema expectedRecordSchema2 = new
SimpleRecordSchema(Arrays.asList(
+ new RecordField("integer", RecordFieldType.INT.getDataType()),
+ new RecordField("string", RecordFieldType.STRING.getDataType())
+ ));
+ RecordSchema expectedRecordChoiceSchema = new
SimpleRecordSchema(Arrays.asList(
+ new RecordField("record", RecordFieldType.CHOICE.getChoiceDataType(
+
RecordFieldType.RECORD.getRecordDataType(expectedRecordSchema1),
+ RecordFieldType.RECORD.getRecordDataType(expectedRecordSchema2)
+ ))
+ ));
+
+ List<Object> expected = Arrays.asList(
+ new MapRecord(expectedRecordChoiceSchema, new HashMap<String,
Object>(){{
+ put("record", new MapRecord(expectedRecordSchema1, new
HashMap<String, Object>(){{
+ put("integer", 1);
+ put("boolean", true);
+ }}));
+ }}),
+ new MapRecord(expectedRecordChoiceSchema, new HashMap<String,
Object>(){{
+ put("record", new MapRecord(expectedRecordSchema2, new
HashMap<String, Object>(){{
+ put("integer", 2);
+ put("string", "stringValue2");
+ }}));
+ }})
+ );
+
+ // WHEN
+ // THEN
+ testReadRecords(jsonPath, expected);
+ }
+
+ @Test
+ public void testChoiceOfEmbeddedArraysAndSingleRecords() throws Exception {
+ // GIVEN
+ String jsonPath =
"src/test/resources/json/choice-of-embedded-arrays-and-single-records.json";
+
+ SimpleRecordSchema expectedRecordSchema1 = new
SimpleRecordSchema(Arrays.asList(
+ new RecordField("integer", RecordFieldType.INT.getDataType())
+ ));
+ SimpleRecordSchema expectedRecordSchema2 = new
SimpleRecordSchema(Arrays.asList(
+ new RecordField("integer", RecordFieldType.INT.getDataType()),
+ new RecordField("boolean", RecordFieldType.BOOLEAN.getDataType())
+ ));
+ SimpleRecordSchema expectedRecordSchema3 = new
SimpleRecordSchema(Arrays.asList(
+ new RecordField("integer", RecordFieldType.INT.getDataType()),
+ new RecordField("string", RecordFieldType.STRING.getDataType())
+ ));
+ SimpleRecordSchema expectedRecordSchema4 = new
SimpleRecordSchema(Arrays.asList(
+ new RecordField("integer", RecordFieldType.INT.getDataType()),
+ new RecordField("string", RecordFieldType.STRING.getDataType())
+ ));
+ RecordSchema expectedRecordChoiceSchema = new
SimpleRecordSchema(Arrays.asList(
+ new RecordField("record", RecordFieldType.CHOICE.getChoiceDataType(
+
RecordFieldType.RECORD.getRecordDataType(expectedRecordSchema1),
+
RecordFieldType.RECORD.getRecordDataType(expectedRecordSchema3),
+
RecordFieldType.ARRAY.getArrayDataType(RecordFieldType.RECORD.getRecordDataType(expectedRecordSchema2)),
+
RecordFieldType.ARRAY.getArrayDataType(RecordFieldType.RECORD.getRecordDataType(expectedRecordSchema4))
+ ))
+ ));
+
+ List<Object> expected = Arrays.asList(
+ new MapRecord(expectedRecordChoiceSchema, new HashMap<String,
Object>(){{
+ put("record", new MapRecord(expectedRecordSchema1, new
HashMap<String, Object>(){{
+ put("integer", 1);
+ }}));
+ }}),
+ new MapRecord(expectedRecordChoiceSchema, new HashMap<String,
Object>(){{
+ put("record", new Object[]{
+ new MapRecord(expectedRecordSchema2, new HashMap<String,
Object>() {{
+ put("integer", 21);
+ put("boolean", true);
+ }}),
+ new MapRecord(expectedRecordSchema2, new HashMap<String,
Object>() {{
+ put("integer", 22);
+ put("boolean", false);
+ }})
+ });
+ }}),
+ new MapRecord(expectedRecordChoiceSchema, new HashMap<String,
Object>(){{
+ put("record", new MapRecord(expectedRecordSchema3, new
HashMap<String, Object>(){{
+ put("integer", 3);
+ put("string", "stringValue3");
+ }}));
+ }}),
+ new MapRecord(expectedRecordChoiceSchema, new HashMap<String,
Object>(){{
+ put("record", new Object[]{
+ new MapRecord(expectedRecordSchema4, new HashMap<String,
Object>() {{
+ put("integer", 41);
+ put("string", "stringValue41");
+ }}),
+ new MapRecord(expectedRecordSchema4, new HashMap<String,
Object>() {{
+ put("integer", 42);
+ put("string", "stringValue42");
+ }})
+ });
+ }})
+ );
+
+ // WHEN
+ // THEN
+ testReadRecords(jsonPath, expected);
+ }
+
+ @Test
+ public void testChoiceOfMergedEmbeddedArraysAndSingleRecords() throws
Exception {
+ // GIVEN
+ String jsonPath =
"src/test/resources/json/choice-of-merged-embedded-arrays-and-single-records.json";
+
+ SimpleRecordSchema expectedRecordSchema1 = new
SimpleRecordSchema(Arrays.asList(
+ new RecordField("integer", RecordFieldType.INT.getDataType()),
+ new RecordField("boolean", RecordFieldType.BOOLEAN.getDataType())
+ ));
+ SimpleRecordSchema expectedRecordSchema2 = new
SimpleRecordSchema(Arrays.asList(
+ new RecordField("integer", RecordFieldType.INT.getDataType()),
+ new RecordField("boolean", RecordFieldType.BOOLEAN.getDataType())
+ ));
+ SimpleRecordSchema expectedRecordSchema3 = new
SimpleRecordSchema(Arrays.asList(
+ new RecordField("integer", RecordFieldType.INT.getDataType()),
+ new RecordField("string", RecordFieldType.STRING.getDataType())
+ ));
+ SimpleRecordSchema expectedRecordSchema4 = new
SimpleRecordSchema(Arrays.asList(
+ new RecordField("integer", RecordFieldType.INT.getDataType()),
+ new RecordField("string", RecordFieldType.STRING.getDataType()),
+ new RecordField("boolean", RecordFieldType.BOOLEAN.getDataType())
+ ));
+ RecordSchema expectedRecordChoiceSchema = new
SimpleRecordSchema(Arrays.asList(
+ new RecordField("record", RecordFieldType.CHOICE.getChoiceDataType(
+
RecordFieldType.RECORD.getRecordDataType(expectedRecordSchema1),
+
RecordFieldType.RECORD.getRecordDataType(expectedRecordSchema3),
+
RecordFieldType.ARRAY.getArrayDataType(RecordFieldType.RECORD.getRecordDataType(expectedRecordSchema2)),
+
RecordFieldType.ARRAY.getArrayDataType(RecordFieldType.RECORD.getRecordDataType(expectedRecordSchema4))
+ ))
+ ));
+
+ List<Object> expected = Arrays.asList(
+ new MapRecord(expectedRecordChoiceSchema, new HashMap<String,
Object>(){{
+ put("record", new MapRecord(expectedRecordSchema1, new
HashMap<String, Object>(){{
+ put("integer", 1);
+ put("boolean", false);
+ }}));
+ }}),
+ new MapRecord(expectedRecordChoiceSchema, new HashMap<String,
Object>(){{
+ put("record", new Object[]{
+ new MapRecord(expectedRecordSchema2, new HashMap<String,
Object>() {{
+ put("integer", 21);
+ put("boolean", true);
+ }}),
+ new MapRecord(expectedRecordSchema2, new HashMap<String,
Object>() {{
+ put("integer", 22);
+ put("boolean", false);
+ }})
+ });
+ }}),
+ new MapRecord(expectedRecordChoiceSchema, new HashMap<String,
Object>(){{
+ put("record", new MapRecord(expectedRecordSchema3, new
HashMap<String, Object>(){{
+ put("integer", 3);
+ put("string", "stringValue3");
+ }}));
+ }}),
+ new MapRecord(expectedRecordChoiceSchema, new HashMap<String,
Object>(){{
+ put("record", new Object[]{
+ new MapRecord(expectedRecordSchema4, new HashMap<String,
Object>() {{
+ put("integer", 41);
+ put("string", "stringValue41");
+ }}),
+ new MapRecord(expectedRecordSchema4, new HashMap<String,
Object>() {{
+ put("integer", 42);
+ put("string", "stringValue42");
+ }}),
+ new MapRecord(expectedRecordSchema4, new HashMap<String,
Object>() {{
+ put("integer", 43);
+ put("boolean", false);
+ }})
+ });
+ }})
+ );
+
+ // WHEN
+ // THEN
+ testReadRecords(jsonPath, expected);
+ }
+
+ @Test
+ public void testChoseSuboptimalSchemaWhenDataHasExtraFields() throws
Exception {
+ // GIVEN
+ String jsonPath =
"src/test/resources/json/choice-of-different-arrays-with-extra-fields.json";
+
+ SimpleRecordSchema recordSchema1 = new
SimpleRecordSchema(Arrays.asList(
+ new RecordField("integer", RecordFieldType.INT.getDataType()),
+ new RecordField("boolean", RecordFieldType.BOOLEAN.getDataType())
+ ));
+ SimpleRecordSchema recordSchema2 = new
SimpleRecordSchema(Arrays.asList(
+ new RecordField("integer", RecordFieldType.INT.getDataType()),
+ new RecordField("string", RecordFieldType.STRING.getDataType())
+ ));
+
+ RecordSchema recordChoiceSchema = new SimpleRecordSchema(Arrays.asList(
+ new RecordField("record", RecordFieldType.CHOICE.getChoiceDataType(
+
RecordFieldType.ARRAY.getArrayDataType(RecordFieldType.RECORD.getRecordDataType(recordSchema1)),
+
RecordFieldType.ARRAY.getArrayDataType(RecordFieldType.RECORD.getRecordDataType(recordSchema2))
+ ))
+ ));
+
+ RecordSchema schema = new SimpleRecordSchema(Arrays.asList(
+ new RecordField("dataCollection",
RecordFieldType.ARRAY.getArrayDataType(
+ RecordFieldType.RECORD.getRecordDataType(recordChoiceSchema)
+ )
+ )));
+
+ SimpleRecordSchema expectedChildSchema1 = new
SimpleRecordSchema(Arrays.asList(
+ new RecordField("integer", RecordFieldType.INT.getDataType()),
+ new RecordField("boolean", RecordFieldType.BOOLEAN.getDataType())
+ ));
+ SimpleRecordSchema expectedChildSchema2 = new
SimpleRecordSchema(Arrays.asList(
+ new RecordField("integer", RecordFieldType.INT.getDataType()),
+ new RecordField("string", RecordFieldType.STRING.getDataType())
+ ));
+ RecordSchema expectedRecordChoiceSchema = new
SimpleRecordSchema(Arrays.asList(
+ new RecordField("record", RecordFieldType.CHOICE.getChoiceDataType(
+
RecordFieldType.ARRAY.getArrayDataType(RecordFieldType.RECORD.getRecordDataType(expectedChildSchema1)),
+
RecordFieldType.ARRAY.getArrayDataType(RecordFieldType.RECORD.getRecordDataType(expectedChildSchema2))
+ ))
+ ));
+
+ // Since the actual arrays have records with either (INT, BOOLEAN,
STRING) or (INT, STRING, STRING)
+ // while the explicit schema defines only (INT, BOOLEAN) and (INT,
STRING) we can't tell which record schema to chose
+ // so we take the first one (INT, BOOLEAN) - as best effort - for
both cases
+ SimpleRecordSchema expectedSelectedRecordSchemaForRecordsInBothArrays
= expectedChildSchema1;
+
+ List<Object> expected = Arrays.asList(
+ new MapRecord(expectedRecordChoiceSchema, new HashMap<String,
Object>(){{
+ put("record", new Object[]{
+ new
MapRecord(expectedSelectedRecordSchemaForRecordsInBothArrays, new
HashMap<String, Object>() {{
+ put("integer", 11);
+ put("boolean", true);
+ put("extraString", "extraStringValue11");
+ }}),
+ new
MapRecord(expectedSelectedRecordSchemaForRecordsInBothArrays, new
HashMap<String, Object>() {{
+ put("integer", 12);
+ put("boolean", false);
+ put("extraString", "extraStringValue12");
+ }})
+ });
+ }}),
+ new MapRecord(expectedRecordChoiceSchema, new HashMap<String,
Object>(){{
+ put("record", new Object[]{
+ new
MapRecord(expectedSelectedRecordSchemaForRecordsInBothArrays, new
HashMap<String, Object>() {{
+ put("integer", 21);
+ put("extraString", "extraStringValue21");
+ put("string", "stringValue21");
+ }}),
+ new
MapRecord(expectedSelectedRecordSchemaForRecordsInBothArrays, new
HashMap<String, Object>() {{
+ put("integer", 22);
+ put("extraString", "extraStringValue22");
+ put("string", "stringValue22");
+ }})
+ });
+ }})
+ );
+
+ // WHEN
+ // THEN
+ testReadRecords(jsonPath, schema, expected);
+ }
+
+ private void testReadRecords(String jsonPath, List<Object> expected)
throws IOException, MalformedRecordException {
+ // GIVEN
+ final File jsonFile = new File(jsonPath);
+
+ try (
+ InputStream jsonStream = new
ByteArrayInputStream(FileUtils.readFileToByteArray(jsonFile));
+ ) {
+ RecordSchema schema = inferSchema(jsonStream);
+
+ // WHEN
+ // THEN
+ testReadRecords(jsonStream, schema, expected);
+ }
+ }
+
+ private void testReadRecords(String jsonPath, RecordSchema schema,
List<Object> expected) throws IOException, MalformedRecordException {
+ // GIVEN
+ final File jsonFile = new File(jsonPath);
+
+ try (
+ InputStream jsonStream = new
ByteArrayInputStream(FileUtils.readFileToByteArray(jsonFile));
+ ) {
+ // WHEN
+ // THEN
+ testReadRecords(jsonStream, schema, expected);
+ }
+ }
+
+ private void testReadRecords(InputStream jsonStream, RecordSchema schema,
List<Object> expected) throws IOException, MalformedRecordException {
+ // GIVEN
+ try (
+ JsonTreeRowRecordReader reader = new
JsonTreeRowRecordReader(jsonStream, mock(ComponentLog.class), schema,
dateFormat, timeFormat, timestampFormat);
+ ) {
+ // WHEN
+ List<Object> actual = new ArrayList<>();
+ Record record;
+ while ((record = reader.nextRecord()) != null) {
+ List<Object> dataCollection = Arrays.asList((Object[])
record.getValue("dataCollection"));
+ actual.addAll(dataCollection);
+ }
+
+ // THEN
+ List<Function<Object, Object>> propertyProviders = Arrays.asList(
Review comment:
I find this logic very confusing, to have a List of functions that
transform one object, chained together, and then to wrap an existing list with
these transforms, just to compare them against the result of applying the same
chain of transformations.
It would be far simpler to just transform the objects here and then make the
assertion against the transformed objects.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
For queries about this service, please contact Infrastructure at:
[email protected]