Skip to content

Commit 89dbcf4

Browse files
committed
The InfluxDBResultMapper is able to handle results with a different time precision
1 parent 24c5542 commit 89dbcf4

File tree

2 files changed

+156
-14
lines changed

2 files changed

+156
-14
lines changed

src/main/java/org/influxdb/impl/InfluxDBResultMapper.java

Lines changed: 81 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@
2727
import java.time.temporal.ChronoField;
2828
import java.util.LinkedList;
2929
import java.util.List;
30-
import java.util.Objects;
3130
import java.util.Map.Entry;
31+
import java.util.Objects;
3232
import java.util.concurrent.ConcurrentHashMap;
3333
import java.util.concurrent.ConcurrentMap;
3434
import java.util.concurrent.TimeUnit;
@@ -84,9 +84,33 @@ public class InfluxDBResultMapper {
8484
* possible to define the values of your POJO (e.g. due to an unsupported field type).
8585
*/
8686
public <T> List<T> toPOJO(final QueryResult queryResult, final Class<T> clazz) throws InfluxDBMapperException {
87+
return toPOJO(queryResult, clazz, TimeUnit.MILLISECONDS);
88+
}
89+
90+
/**
91+
* <p>
92+
* Process a {@link QueryResult} object returned by the InfluxDB client inspecting the internal
93+
* data structure and creating the respective object instances based on the Class passed as
94+
* parameter.
95+
* </p>
96+
*
97+
* @param queryResult the InfluxDB result object
98+
* @param clazz the Class that will be used to hold your measurement data
99+
* @param precision the time precision of results
100+
* @param <T> the target type
101+
*
102+
* @return a {@link List} of objects from the same Class passed as parameter and sorted on the
103+
* same order as received from InfluxDB.
104+
*
105+
* @throws InfluxDBMapperException If {@link QueryResult} parameter contain errors,
106+
* <tt>clazz</tt> parameter is not annotated with &#64;Measurement or it was not
107+
* possible to define the values of your POJO (e.g. due to an unsupported field type).
108+
*/
109+
public <T> List<T> toPOJO(final QueryResult queryResult, final Class<T> clazz,
110+
final TimeUnit precision) throws InfluxDBMapperException {
87111
throwExceptionIfMissingAnnotation(clazz);
88112
String measurementName = getMeasurementName(clazz);
89-
return this.toPOJO(queryResult, clazz, measurementName);
113+
return this.toPOJO(queryResult, clazz, measurementName, precision);
90114
}
91115

92116
/**
@@ -110,6 +134,32 @@ public <T> List<T> toPOJO(final QueryResult queryResult, final Class<T> clazz) t
110134
*/
111135
public <T> List<T> toPOJO(final QueryResult queryResult, final Class<T> clazz, final String measurementName)
112136
throws InfluxDBMapperException {
137+
return toPOJO(queryResult, clazz, measurementName, TimeUnit.MILLISECONDS);
138+
}
139+
140+
/**
141+
* <p>
142+
* Process a {@link QueryResult} object returned by the InfluxDB client inspecting the internal
143+
* data structure and creating the respective object instances based on the Class passed as
144+
* parameter.
145+
* </p>
146+
*
147+
* @param queryResult the InfluxDB result object
148+
* @param clazz the Class that will be used to hold your measurement data
149+
* @param <T> the target type
150+
* @param measurementName name of the Measurement
151+
* @param precision the time precision of results
152+
*
153+
* @return a {@link List} of objects from the same Class passed as parameter and sorted on the
154+
* same order as received from InfluxDB.
155+
*
156+
* @throws InfluxDBMapperException If {@link QueryResult} parameter contain errors,
157+
* <tt>clazz</tt> parameter is not annotated with &#64;Measurement or it was not
158+
* possible to define the values of your POJO (e.g. due to an unsupported field type).
159+
*/
160+
public <T> List<T> toPOJO(final QueryResult queryResult, final Class<T> clazz, final String measurementName,
161+
final TimeUnit precision)
162+
throws InfluxDBMapperException {
113163

114164
Objects.requireNonNull(measurementName, "measurementName");
115165
Objects.requireNonNull(queryResult, "queryResult");
@@ -126,7 +176,7 @@ public <T> List<T> toPOJO(final QueryResult queryResult, final Class<T> clazz, f
126176
internalResult.getSeries().stream()
127177
.filter(series -> series.getName().equals(measurementName))
128178
.forEachOrdered(series -> {
129-
parseSeriesAs(series, clazz, result);
179+
parseSeriesAs(series, clazz, result, precision);
130180
});
131181
});
132182

@@ -152,7 +202,7 @@ void throwExceptionIfResultWithError(final QueryResult queryResult) {
152202
});
153203
}
154204

155-
void cacheMeasurementClass(final Class<?>... classVarAgrs) {
205+
public void cacheMeasurementClass(final Class<?>... classVarAgrs) {
156206
for (Class<?> clazz : classVarAgrs) {
157207
if (CLASS_FIELD_CACHE.containsKey(clazz.getName())) {
158208
continue;
@@ -172,13 +222,22 @@ void cacheMeasurementClass(final Class<?>... classVarAgrs) {
172222
}
173223
}
174224

175-
String getMeasurementName(final Class<?> clazz) {
225+
public String getMeasurementName(final Class<?> clazz) {
176226
return ((Measurement) clazz.getAnnotation(Measurement.class)).name();
177227
}
178228

229+
public <T> ConcurrentMap<String, Field> getColNameAndFieldMap(final Class<T> clazz) {
230+
return CLASS_FIELD_CACHE.get(clazz.getName());
231+
}
232+
179233
<T> List<T> parseSeriesAs(final QueryResult.Series series, final Class<T> clazz, final List<T> result) {
234+
return parseSeriesAs(series, clazz, result, TimeUnit.MILLISECONDS);
235+
}
236+
237+
<T> List<T> parseSeriesAs(final QueryResult.Series series, final Class<T> clazz, final List<T> result,
238+
final TimeUnit precision) {
180239
int columnSize = series.getColumns().size();
181-
ConcurrentMap<String, Field> colNameAndFieldMap = CLASS_FIELD_CACHE.get(clazz.getName());
240+
ConcurrentMap<String, Field> colNameAndFieldMap = getColNameAndFieldMap(clazz);
182241
try {
183242
T object = null;
184243
for (List<Object> row : series.getValues()) {
@@ -188,7 +247,7 @@ <T> List<T> parseSeriesAs(final QueryResult.Series series, final Class<T> clazz,
188247
if (object == null) {
189248
object = clazz.newInstance();
190249
}
191-
setFieldValue(object, correspondingField, row.get(i));
250+
setFieldValue(object, correspondingField, row.get(i), precision);
192251
}
193252
}
194253
// When the "GROUP BY" clause is used, "tags" are returned as Map<String,String> and
@@ -200,7 +259,7 @@ <T> List<T> parseSeriesAs(final QueryResult.Series series, final Class<T> clazz,
200259
Field correspondingField = colNameAndFieldMap.get(entry.getKey()/*InfluxDB columnName*/);
201260
if (correspondingField != null) {
202261
// I don't think it is possible to reach here without a valid "object"
203-
setFieldValue(object, correspondingField, entry.getValue());
262+
setFieldValue(object, correspondingField, entry.getValue(), precision);
204263
}
205264
}
206265
}
@@ -223,10 +282,11 @@ <T> List<T> parseSeriesAs(final QueryResult.Series series, final Class<T> clazz,
223282
* @param object
224283
* @param field
225284
* @param value
285+
* @param precision
226286
* @throws IllegalArgumentException
227287
* @throws IllegalAccessException
228288
*/
229-
<T> void setFieldValue(final T object, final Field field, final Object value)
289+
<T> void setFieldValue(final T object, final Field field, final Object value, final TimeUnit precision)
230290
throws IllegalArgumentException, IllegalAccessException {
231291
if (value == null) {
232292
return;
@@ -236,7 +296,7 @@ <T> void setFieldValue(final T object, final Field field, final Object value)
236296
if (!field.isAccessible()) {
237297
field.setAccessible(true);
238298
}
239-
if (fieldValueModified(fieldType, field, object, value)
299+
if (fieldValueModified(fieldType, field, object, value, precision)
240300
|| fieldValueForPrimitivesModified(fieldType, field, object, value)
241301
|| fieldValueForPrimitiveWrappersModified(fieldType, field, object, value)) {
242302
return;
@@ -252,7 +312,8 @@ <T> void setFieldValue(final T object, final Field field, final Object value)
252312
}
253313
}
254314

255-
<T> boolean fieldValueModified(final Class<?> fieldType, final Field field, final T object, final Object value)
315+
<T> boolean fieldValueModified(final Class<?> fieldType, final Field field, final T object, final Object value,
316+
final TimeUnit precision)
256317
throws IllegalArgumentException, IllegalAccessException {
257318
if (String.class.isAssignableFrom(fieldType)) {
258319
field.set(object, String.valueOf(value));
@@ -263,9 +324,11 @@ <T> boolean fieldValueModified(final Class<?> fieldType, final Field field, fina
263324
if (value instanceof String) {
264325
instant = Instant.from(ISO8601_FORMATTER.parse(String.valueOf(value)));
265326
} else if (value instanceof Long) {
266-
instant = Instant.ofEpochMilli((Long) value);
327+
instant = Instant.ofEpochMilli(toMillis((Long) value, precision));
267328
} else if (value instanceof Double) {
268-
instant = Instant.ofEpochMilli(((Double) value).longValue());
329+
instant = Instant.ofEpochMilli(toMillis(((Double) value).longValue(), precision));
330+
} else if (value instanceof Integer) {
331+
instant = Instant.ofEpochMilli(toMillis(((Integer) value).longValue(), precision));
269332
} else {
270333
throw new InfluxDBMapperException("Unsupported type " + field.getClass() + " for field " + field.getName());
271334
}
@@ -316,4 +379,9 @@ <T> boolean fieldValueForPrimitiveWrappersModified(final Class<?> fieldType, fin
316379
}
317380
return false;
318381
}
382+
383+
private Long toMillis(final Long value, final TimeUnit precision) {
384+
385+
return TimeUnit.MILLISECONDS.convert(value, precision);
386+
}
319387
}

src/test/java/org/influxdb/impl/InfluxDBResultMapperTest.java

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,12 @@
2929
import java.util.Map;
3030
import java.util.Random;
3131
import java.util.UUID;
32+
import java.util.concurrent.TimeUnit;
3233

3334
import org.influxdb.InfluxDBMapperException;
3435
import org.influxdb.annotation.Column;
3536
import org.influxdb.annotation.Measurement;
3637
import org.influxdb.dto.QueryResult;
37-
import org.influxdb.impl.InfluxDBResultMapper;
3838
import org.junit.jupiter.api.Assertions;
3939
import org.junit.jupiter.api.Test;
4040
import org.junit.platform.runner.JUnitPlatform;
@@ -220,6 +220,26 @@ public void testFieldValueModified_DateAsISO8601() {
220220
Assertions.assertTrue(result.size() == 1);
221221
}
222222

223+
@Test
224+
public void testFieldValueModified_DateAsInteger() {
225+
// Given...
226+
mapper.cacheMeasurementClass(MyCustomMeasurement.class);
227+
228+
List<String> columnList = Arrays.asList("time");
229+
List<Object> firstSeriesResult = Arrays.asList(1_000);
230+
231+
QueryResult.Series series = new QueryResult.Series();
232+
series.setColumns(columnList);
233+
series.setValues(Arrays.asList(firstSeriesResult));
234+
235+
//When...
236+
List<MyCustomMeasurement> result = new LinkedList<>();
237+
mapper.parseSeriesAs(series, MyCustomMeasurement.class, result);
238+
239+
//Then...
240+
Assertions.assertTrue(result.size() == 1);
241+
}
242+
223243
@Test
224244
public void testUnsupportedField() {
225245
// Given...
@@ -335,6 +355,60 @@ public void testToPOJO_ticket363() {
335355
Assertions.assertEquals(1, result.get(0).time.getNano(), "incorrect value for the nanoseconds field");
336356
}
337357

358+
@Test
359+
void testToPOJO_Precision() {
360+
// Given...
361+
mapper.cacheMeasurementClass(MyCustomMeasurement.class);
362+
363+
List<String> columnList = Arrays.asList("time");
364+
List<Object> firstSeriesResult = Arrays.asList(1_500_000L);
365+
366+
QueryResult.Series series = new QueryResult.Series();
367+
series.setName("CustomMeasurement");
368+
series.setColumns(columnList);
369+
series.setValues(Arrays.asList(firstSeriesResult));
370+
371+
QueryResult.Result internalResult = new QueryResult.Result();
372+
internalResult.setSeries(Arrays.asList(series));
373+
374+
QueryResult queryResult = new QueryResult();
375+
queryResult.setResults(Arrays.asList(internalResult));
376+
377+
// When...
378+
List<MyCustomMeasurement> result = mapper.toPOJO(queryResult, MyCustomMeasurement.class, TimeUnit.SECONDS);
379+
380+
// Then...
381+
Assertions.assertEquals(1, result.size(), "incorrect number of elements");
382+
Assertions.assertEquals(1_500_000_000L, result.get(0).time.toEpochMilli(), "incorrect value for the millis field");
383+
}
384+
385+
@Test
386+
void testToPOJO_SetMeasureName() {
387+
// Given...
388+
mapper.cacheMeasurementClass(MyCustomMeasurement.class);
389+
390+
List<String> columnList = Arrays.asList("uuid");
391+
List<Object> firstSeriesResult = Arrays.asList(UUID.randomUUID().toString());
392+
393+
QueryResult.Series series = new QueryResult.Series();
394+
series.setName("MySeriesName");
395+
series.setColumns(columnList);
396+
series.setValues(Arrays.asList(firstSeriesResult));
397+
398+
QueryResult.Result internalResult = new QueryResult.Result();
399+
internalResult.setSeries(Arrays.asList(series));
400+
401+
QueryResult queryResult = new QueryResult();
402+
queryResult.setResults(Arrays.asList(internalResult));
403+
404+
//When...
405+
List<MyCustomMeasurement> result =
406+
mapper.toPOJO(queryResult, MyCustomMeasurement.class, "MySeriesName");
407+
408+
//Then...
409+
Assertions.assertTrue(result.size() == 1);
410+
}
411+
338412
@Measurement(name = "CustomMeasurement")
339413
static class MyCustomMeasurement {
340414

0 commit comments

Comments
 (0)