2727import java .time .temporal .ChronoField ;
2828import java .util .LinkedList ;
2929import java .util .List ;
30- import java .util .Objects ;
3130import java .util .Map .Entry ;
31+ import java .util .Objects ;
3232import java .util .concurrent .ConcurrentHashMap ;
3333import java .util .concurrent .ConcurrentMap ;
3434import 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 @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 @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}
0 commit comments