Browse Source

Merge pull request #15560 from dashevchenko/maxAggrFix

Fixed MAX aggregation for mixed double and long telemetry values
pull/15581/head
Viacheslav Klimov 4 weeks ago
committed by GitHub
parent
commit
28b376aa3c
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 2
      common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbelCfTsRollingArg.java
  2. 13
      common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbelCfTsRollingArgTest.java
  3. 4
      dao/src/main/java/org/thingsboard/server/dao/sqlts/ts/TsKvRepository.java
  4. 2
      dao/src/main/java/org/thingsboard/server/dao/timeseries/AggregatePartitionsFunction.java
  5. 25
      dao/src/test/java/org/thingsboard/server/dao/service/timeseries/BaseTimeseriesServiceTest.java

2
common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbelCfTsRollingArg.java

@ -73,7 +73,7 @@ public class TbelCfTsRollingArg implements TbelCfArg, Iterable<TbelCfTsDoubleVal
throw new IllegalArgumentException("Rolling argument values are empty.");
}
double max = Double.MIN_VALUE;
double max = -Double.MAX_VALUE;
for (TbelCfTsDoubleVal value : values) {
double val = value.getValue();
if (!ignoreNaN && Double.isNaN(val)) {

13
common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbelCfTsRollingArgTest.java

@ -57,6 +57,19 @@ public class TbelCfTsRollingArgTest {
assertThat(rollingArg.max(false)).isNaN();
}
@Test
void testMaxOverAllNegativeValues() {
TbelCfTsRollingArg negativeArg = new TbelCfTsRollingArg(
new TbTimeWindow(ts - 30000, ts - 10),
List.of(
new TbelCfTsDoubleVal(ts - 10, -50.0),
new TbelCfTsDoubleVal(ts - 20, -100.0),
new TbelCfTsDoubleVal(ts - 30, -75.0)
)
);
assertThat(negativeArg.max()).isEqualTo(-50.0);
}
@Test
void testMin() {
assertThat(rollingArg.min()).isEqualTo(2.0);

4
dao/src/main/java/org/thingsboard/server/dao/sqlts/ts/TsKvRepository.java

@ -61,8 +61,10 @@ public interface TsKvRepository extends JpaRepository<TsKvEntity, TsKvCompositeK
@Param("startTs") long startTs,
@Param("endTs") long endTs);
// -1.7976931348623157E308 = -Double.MAX_VALUE — the most negative finite double, used as a "less than any value" sentinel for MAX.
// Double.MIN_VALUE is +4.9E-324 (smallest positive), which would beat any negative real value and corrupt MAX.
@Query("SELECT new TsKvEntity(MAX(COALESCE(tskv.longValue, -9223372036854775807)), " +
"MAX(COALESCE(tskv.doubleValue, java.lang.Double.MIN_VALUE)), " +
"MAX(COALESCE(tskv.doubleValue, -1.7976931348623157E308)), " +
"SUM(CASE WHEN tskv.longValue IS NULL THEN 0 ELSE 1 END), " +
"SUM(CASE WHEN tskv.doubleValue IS NULL THEN 0 ELSE 1 END), " +
"'MAX', MAX(tskv.ts)) FROM TsKvEntity tskv " +

2
dao/src/main/java/org/thingsboard/server/dao/timeseries/AggregatePartitionsFunction.java

@ -277,7 +277,7 @@ public class AggregatePartitionsFunction implements com.google.common.util.concu
private Optional<TsKvEntry> processMinOrMaxResult(AggregationResult aggResult) {
if (aggResult.dataType == DataType.DOUBLE || aggResult.dataType == DataType.LONG) {
if (aggResult.hasDouble) {
double currentD = aggregation == Aggregation.MIN ? Optional.ofNullable(aggResult.dValue).orElse(Double.MAX_VALUE) : Optional.ofNullable(aggResult.dValue).orElse(Double.MIN_VALUE);
double currentD = aggregation == Aggregation.MIN ? Optional.ofNullable(aggResult.dValue).orElse(Double.MAX_VALUE) : Optional.ofNullable(aggResult.dValue).orElse(-Double.MAX_VALUE);
double currentL = aggregation == Aggregation.MIN ? Optional.ofNullable(aggResult.lValue).orElse(Long.MAX_VALUE) : Optional.ofNullable(aggResult.lValue).orElse(Long.MIN_VALUE);
return Optional.of(new BasicTsKvEntry(ts, new DoubleDataEntry(key, aggregation == Aggregation.MIN ? Math.min(currentD, currentL) : Math.max(currentD, currentL))));
} else {

25
dao/src/test/java/org/thingsboard/server/dao/service/timeseries/BaseTimeseriesServiceTest.java

@ -710,6 +710,31 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest {
assertEquals(java.util.Optional.of(2L), list.get(2).getLongValue());
}
@Test
public void testFindDeviceMaxAggregationOverNegativeMixedLongAndDoubleTsData() throws Exception {
save(deviceId, 5000, -100L);
save(deviceId, 15000, -50.0);
List<TsKvEntry> list = tsService.findAll(tenantId, deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 0,
60000, 60000, 1, Aggregation.MAX))).get(MAX_TIMEOUT, TimeUnit.SECONDS);
assertEquals(1, list.size());
assertEquals(java.util.Optional.of(-50.0), list.get(0).getDoubleValue());
}
@Test
public void testFindDeviceMaxAggregationOverAllNegativeDoubleTsData() throws Exception {
save(deviceId, 5000, -50.0);
save(deviceId, 15000, -100.0);
save(deviceId, 25000, -75.0);
List<TsKvEntry> list = tsService.findAll(tenantId, deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 0,
60000, 60000, 1, Aggregation.MAX))).get(MAX_TIMEOUT, TimeUnit.SECONDS);
assertEquals(1, list.size());
assertEquals(java.util.Optional.of(-50.0), list.get(0).getDoubleValue());
}
@Test
public void testSaveTs_RemoveTs_AndSaveTsAgain() throws Exception {
save(deviceId, 2000000L, 95);

Loading…
Cancel
Save