diff --git a/src/main/java/org/influxdb/impl/InfluxDBResultMapper.java b/src/main/java/org/influxdb/impl/InfluxDBResultMapper.java index cd20bb6ed..75884eed7 100644 --- a/src/main/java/org/influxdb/impl/InfluxDBResultMapper.java +++ b/src/main/java/org/influxdb/impl/InfluxDBResultMapper.java @@ -28,6 +28,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Objects; +import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.TimeUnit; @@ -147,12 +148,12 @@ String getMeasurementName(final Class clazz) { List parseSeriesAs(final QueryResult.Series series, final Class clazz, final List result) { int columnSize = series.getColumns().size(); + ConcurrentMap colNameAndFieldMap = CLASS_FIELD_CACHE.get(clazz.getName()); try { T object = null; for (List row : series.getValues()) { for (int i = 0; i < columnSize; i++) { - String resultColumnName = series.getColumns().get(i); - Field correspondingField = CLASS_FIELD_CACHE.get(clazz.getName()).get(resultColumnName); + Field correspondingField = colNameAndFieldMap.get(series.getColumns().get(i)/*InfluxDB columnName*/); if (correspondingField != null) { if (object == null) { object = clazz.newInstance(); @@ -160,6 +161,19 @@ List parseSeriesAs(final QueryResult.Series series, final Class clazz, setFieldValue(object, correspondingField, row.get(i)); } } + // When the "GROUP BY" clause is used, "tags" are returned as Map and + // accordingly with InfluxDB documentation + // https://docs.influxdata.com/influxdb/v1.2/concepts/glossary/#tag-value + // "tag" values are always String. + if (series.getTags() != null && !series.getTags().isEmpty()) { + for (Entry entry : series.getTags().entrySet()) { + Field correspondingField = colNameAndFieldMap.get(entry.getKey()/*InfluxDB columnName*/); + if (correspondingField != null) { + // I don't think it is possible to reach here without a valid "object" + setFieldValue(object, correspondingField, entry.getValue()); + } + } + } if (object != null) { result.add(object); object = null; @@ -233,8 +247,7 @@ boolean fieldValueModified(final Class fieldType, final Field field, fina } boolean fieldValueForPrimitivesModified(final Class fieldType, final Field field, final T object, - final Object value) - throws IllegalArgumentException, IllegalAccessException { + final Object value) throws IllegalArgumentException, IllegalAccessException { if (double.class.isAssignableFrom(fieldType)) { field.setDouble(object, ((Double) value).doubleValue()); return true; @@ -255,8 +268,7 @@ boolean fieldValueForPrimitivesModified(final Class fieldType, final Fiel } boolean fieldValueForPrimitiveWrappersModified(final Class fieldType, final Field field, final T object, - final Object value) - throws IllegalArgumentException, IllegalAccessException { + final Object value) throws IllegalArgumentException, IllegalAccessException { if (Double.class.isAssignableFrom(fieldType)) { field.set(object, value); return true; diff --git a/src/test/java/org/influxdb/impl/InfluxDBResultMapperTest.java b/src/test/java/org/influxdb/impl/InfluxDBResultMapperTest.java index 61409a77b..0c9b86a05 100644 --- a/src/test/java/org/influxdb/impl/InfluxDBResultMapperTest.java +++ b/src/test/java/org/influxdb/impl/InfluxDBResultMapperTest.java @@ -26,8 +26,10 @@ import java.time.Instant; import java.util.Arrays; import java.util.Date; +import java.util.HashMap; import java.util.LinkedList; import java.util.List; +import java.util.Map; import java.util.Random; import java.util.UUID; @@ -243,7 +245,61 @@ public void testToPOJO_SeriesFromQueryResultIsNull() { // Then... assertTrue("there must NO entry in the result list", myList.isEmpty()); } - + + @Test + public void testToPOJO_QueryResultCreatedByGroupByClause() { + // Given... + mapper.cacheMeasurementClass(GroupByCarrierDeviceOS.class); + + List columnList = Arrays.asList("time", "median", "min", "max"); + + // InfluxDB client returns the time representation as Double. + Double now = Long.valueOf(System.currentTimeMillis()).doubleValue(); + + List firstSeriesResult = Arrays.asList(now, new Double("233.8"), new Double("0.0"), + new Double("3090744.0")); + // When the "GROUP BY" clause is used, "tags" are returned as Map + Map firstSeriesTagMap = new HashMap<>(); + firstSeriesTagMap.put("CARRIER", "000/00"); + firstSeriesTagMap.put("DEVICE_OS_VERSION", "4.4.2"); + + List secondSeriesResult = Arrays.asList(now, new Double("552.0"), new Double("135.0"), + new Double("267705.0")); + Map secondSeriesTagMap = new HashMap<>(); + secondSeriesTagMap.put("CARRIER", "000/01"); + secondSeriesTagMap.put("DEVICE_OS_VERSION", "9.3.5"); + + QueryResult.Series firstSeries = new QueryResult.Series(); + firstSeries.setColumns(columnList); + firstSeries.setValues(Arrays.asList(firstSeriesResult)); + firstSeries.setTags(firstSeriesTagMap); + firstSeries.setName("tb_network"); + + QueryResult.Series secondSeries = new QueryResult.Series(); + secondSeries.setColumns(columnList); + secondSeries.setValues(Arrays.asList(secondSeriesResult)); + secondSeries.setTags(secondSeriesTagMap); + secondSeries.setName("tb_network"); + + QueryResult.Result internalResult = new QueryResult.Result(); + internalResult.setSeries(Arrays.asList(firstSeries, secondSeries)); + + QueryResult queryResult = new QueryResult(); + queryResult.setResults(Arrays.asList(internalResult)); + + // When... + List myList = mapper.toPOJO(queryResult, GroupByCarrierDeviceOS.class); + + // Then... + GroupByCarrierDeviceOS firstGroupByEntry = myList.get(0); + assertEquals("field 'carrier' does not match", "000/00", firstGroupByEntry.carrier); + assertEquals("field 'deviceOsVersion' does not match", "4.4.2", firstGroupByEntry.deviceOsVersion); + + GroupByCarrierDeviceOS secondGroupByEntry = myList.get(1); + assertEquals("field 'carrier' does not match", "000/01", secondGroupByEntry.carrier); + assertEquals("field 'deviceOsVersion' does not match", "9.3.5", secondGroupByEntry.deviceOsVersion); + } + @Measurement(name = "CustomMeasurement") static class MyCustomMeasurement { @@ -297,4 +353,35 @@ static class MyPojoWithUnsupportedField { @Column(name = "bar") private Date myDate; } + + /** + * Class created based on example from https://github.com/influxdata/influxdb-java/issues/343 + */ + @Measurement(name = "tb_network") + static class GroupByCarrierDeviceOS { + + @Column(name = "time") + private Instant time; + + @Column(name = "CARRIER", tag = true) + private String carrier; + + @Column(name = "DEVICE_OS_VERSION", tag = true) + private String deviceOsVersion; + + @Column(name = "median") + private Double median; + + @Column(name = "min") + private Double min; + + @Column(name = "max") + private Double max; + + @Override + public String toString() { + return "GroupByCarrierDeviceOS [time=" + time + ", carrier=" + carrier + ", deviceOsVersion=" + deviceOsVersion + + ", median=" + median + ", min=" + min + ", max=" + max + "]"; + } + } } \ No newline at end of file