Commit 03f5375a02acb3bcca7039e872165768529c47db
Committed by
GitHub
1 parent
aa0fce86
JSON support (#2415)
* Created JsonDataEntry and added DataType JSON * Added json to ts and attributes, created sql schema-entities-hsql.sql (json_v varchar) * refactored * refactored * added json array support * Aggregation improvement * Changed in JsonDataEntry value type from JsonNode to String * fix AggregatePartitionsFunction Co-authored-by: Yevhen Bondarenko <56396344+YevhenBondarenko@users.noreply.github.com>
Showing
56 changed files
with
713 additions
and
324 deletions
@@ -567,6 +567,9 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { | @@ -567,6 +567,9 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { | ||
567 | case STRING_V: | 567 | case STRING_V: |
568 | json.addProperty(kv.getKey(), kv.getStringV()); | 568 | json.addProperty(kv.getKey(), kv.getStringV()); |
569 | break; | 569 | break; |
570 | + case JSON_V: | ||
571 | + json.add(kv.getKey(), jsonParser.parse(kv.getJsonV())); | ||
572 | + break; | ||
570 | } | 573 | } |
571 | } | 574 | } |
572 | return json; | 575 | return json; |
@@ -643,6 +646,10 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { | @@ -643,6 +646,10 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { | ||
643 | builder.setType(KeyValueType.STRING_V); | 646 | builder.setType(KeyValueType.STRING_V); |
644 | builder.setStringV(kvEntry.getStrValue().get()); | 647 | builder.setStringV(kvEntry.getStrValue().get()); |
645 | break; | 648 | break; |
649 | + case JSON: | ||
650 | + builder.setType(KeyValueType.JSON_V); | ||
651 | + builder.setJsonV(kvEntry.getJsonValue().get()); | ||
652 | + break; | ||
646 | } | 653 | } |
647 | return builder.build(); | 654 | return builder.build(); |
648 | } | 655 | } |
@@ -642,6 +642,8 @@ public abstract class BaseController { | @@ -642,6 +642,8 @@ public abstract class BaseController { | ||
642 | entityNode.put(attr.getKey(), attr.getDoubleValue().get()); | 642 | entityNode.put(attr.getKey(), attr.getDoubleValue().get()); |
643 | } else if (attr.getDataType() == DataType.LONG) { | 643 | } else if (attr.getDataType() == DataType.LONG) { |
644 | entityNode.put(attr.getKey(), attr.getLongValue().get()); | 644 | entityNode.put(attr.getKey(), attr.getLongValue().get()); |
645 | + } else if (attr.getDataType() == DataType.JSON) { | ||
646 | + entityNode.set(attr.getKey(), json.readTree(attr.getJsonValue().get())); | ||
645 | } else { | 647 | } else { |
646 | entityNode.put(attr.getKey(), attr.getValueAsString()); | 648 | entityNode.put(attr.getKey(), attr.getValueAsString()); |
647 | } | 649 | } |
@@ -15,12 +15,15 @@ | @@ -15,12 +15,15 @@ | ||
15 | */ | 15 | */ |
16 | package org.thingsboard.server.controller; | 16 | package org.thingsboard.server.controller; |
17 | 17 | ||
18 | +import com.fasterxml.jackson.core.JsonProcessingException; | ||
18 | import com.fasterxml.jackson.databind.JsonNode; | 19 | import com.fasterxml.jackson.databind.JsonNode; |
20 | +import com.fasterxml.jackson.databind.ObjectMapper; | ||
19 | import com.google.common.base.Function; | 21 | import com.google.common.base.Function; |
20 | import com.google.common.util.concurrent.FutureCallback; | 22 | import com.google.common.util.concurrent.FutureCallback; |
21 | import com.google.common.util.concurrent.Futures; | 23 | import com.google.common.util.concurrent.Futures; |
22 | import com.google.common.util.concurrent.ListenableFuture; | 24 | import com.google.common.util.concurrent.ListenableFuture; |
23 | import com.google.gson.JsonElement; | 25 | import com.google.gson.JsonElement; |
26 | +import com.google.gson.JsonParseException; | ||
24 | import com.google.gson.JsonParser; | 27 | import com.google.gson.JsonParser; |
25 | import lombok.extern.slf4j.Slf4j; | 28 | import lombok.extern.slf4j.Slf4j; |
26 | import org.springframework.beans.factory.annotation.Autowired; | 29 | import org.springframework.beans.factory.annotation.Autowired; |
@@ -56,8 +59,10 @@ import org.thingsboard.server.common.data.kv.BaseDeleteTsKvQuery; | @@ -56,8 +59,10 @@ import org.thingsboard.server.common.data.kv.BaseDeleteTsKvQuery; | ||
56 | import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery; | 59 | import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery; |
57 | import org.thingsboard.server.common.data.kv.BasicTsKvEntry; | 60 | import org.thingsboard.server.common.data.kv.BasicTsKvEntry; |
58 | import org.thingsboard.server.common.data.kv.BooleanDataEntry; | 61 | import org.thingsboard.server.common.data.kv.BooleanDataEntry; |
62 | +import org.thingsboard.server.common.data.kv.DataType; | ||
59 | import org.thingsboard.server.common.data.kv.DeleteTsKvQuery; | 63 | import org.thingsboard.server.common.data.kv.DeleteTsKvQuery; |
60 | import org.thingsboard.server.common.data.kv.DoubleDataEntry; | 64 | import org.thingsboard.server.common.data.kv.DoubleDataEntry; |
65 | +import org.thingsboard.server.common.data.kv.JsonDataEntry; | ||
61 | import org.thingsboard.server.common.data.kv.KvEntry; | 66 | import org.thingsboard.server.common.data.kv.KvEntry; |
62 | import org.thingsboard.server.common.data.kv.LongDataEntry; | 67 | import org.thingsboard.server.common.data.kv.LongDataEntry; |
63 | import org.thingsboard.server.common.data.kv.ReadTsKvQuery; | 68 | import org.thingsboard.server.common.data.kv.ReadTsKvQuery; |
@@ -77,6 +82,7 @@ import org.thingsboard.server.service.telemetry.exception.UncheckedApiException; | @@ -77,6 +82,7 @@ import org.thingsboard.server.service.telemetry.exception.UncheckedApiException; | ||
77 | import javax.annotation.Nullable; | 82 | import javax.annotation.Nullable; |
78 | import javax.annotation.PostConstruct; | 83 | import javax.annotation.PostConstruct; |
79 | import javax.annotation.PreDestroy; | 84 | import javax.annotation.PreDestroy; |
85 | +import java.io.IOException; | ||
80 | import java.util.ArrayList; | 86 | import java.util.ArrayList; |
81 | import java.util.Arrays; | 87 | import java.util.Arrays; |
82 | import java.util.HashSet; | 88 | import java.util.HashSet; |
@@ -107,6 +113,8 @@ public class TelemetryController extends BaseController { | @@ -107,6 +113,8 @@ public class TelemetryController extends BaseController { | ||
107 | 113 | ||
108 | private ExecutorService executor; | 114 | private ExecutorService executor; |
109 | 115 | ||
116 | + private static final ObjectMapper mapper = new ObjectMapper(); | ||
117 | + | ||
110 | @PostConstruct | 118 | @PostConstruct |
111 | public void initExecutor() { | 119 | public void initExecutor() { |
112 | executor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("telemetry-controller")); | 120 | executor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("telemetry-controller")); |
@@ -284,8 +292,7 @@ public class TelemetryController extends BaseController { | @@ -284,8 +292,7 @@ public class TelemetryController extends BaseController { | ||
284 | if (startTs == null || endTs == null) { | 292 | if (startTs == null || endTs == null) { |
285 | deleteToTs = endTs; | 293 | deleteToTs = endTs; |
286 | return getImmediateDeferredResult("When deleteAllDataForKeys is false, start and end timestamp values shouldn't be empty", HttpStatus.BAD_REQUEST); | 294 | return getImmediateDeferredResult("When deleteAllDataForKeys is false, start and end timestamp values shouldn't be empty", HttpStatus.BAD_REQUEST); |
287 | - } | ||
288 | - else{ | 295 | + } else { |
289 | deleteFromTs = startTs; | 296 | deleteFromTs = startTs; |
290 | deleteToTs = endTs; | 297 | deleteToTs = endTs; |
291 | } | 298 | } |
@@ -536,8 +543,9 @@ public class TelemetryController extends BaseController { | @@ -536,8 +543,9 @@ public class TelemetryController extends BaseController { | ||
536 | return new FutureCallback<List<AttributeKvEntry>>() { | 543 | return new FutureCallback<List<AttributeKvEntry>>() { |
537 | @Override | 544 | @Override |
538 | public void onSuccess(List<AttributeKvEntry> attributes) { | 545 | public void onSuccess(List<AttributeKvEntry> attributes) { |
539 | - List<AttributeData> values = attributes.stream().map(attribute -> new AttributeData(attribute.getLastUpdateTs(), | ||
540 | - attribute.getKey(), attribute.getValue())).collect(Collectors.toList()); | 546 | + List<AttributeData> values = attributes.stream().map(attribute -> |
547 | + new AttributeData(attribute.getLastUpdateTs(), attribute.getKey(), getKvValue(attribute)) | ||
548 | + ).collect(Collectors.toList()); | ||
541 | logAttributesRead(user, entityId, scope, keyList, null); | 549 | logAttributesRead(user, entityId, scope, keyList, null); |
542 | response.setResult(new ResponseEntity<>(values, HttpStatus.OK)); | 550 | response.setResult(new ResponseEntity<>(values, HttpStatus.OK)); |
543 | } | 551 | } |
@@ -639,7 +647,9 @@ public class TelemetryController extends BaseController { | @@ -639,7 +647,9 @@ public class TelemetryController extends BaseController { | ||
639 | jsonNode.fields().forEachRemaining(entry -> { | 647 | jsonNode.fields().forEachRemaining(entry -> { |
640 | String key = entry.getKey(); | 648 | String key = entry.getKey(); |
641 | JsonNode value = entry.getValue(); | 649 | JsonNode value = entry.getValue(); |
642 | - if (entry.getValue().isTextual()) { | 650 | + if (entry.getValue().isObject() || entry.getValue().isArray()) { |
651 | + attributes.add(new BaseAttributeKvEntry(new JsonDataEntry(key, toJsonStr(value)), ts)); | ||
652 | + } else if (entry.getValue().isTextual()) { | ||
643 | if (maxStringValueLength > 0 && entry.getValue().textValue().length() > maxStringValueLength) { | 653 | if (maxStringValueLength > 0 && entry.getValue().textValue().length() > maxStringValueLength) { |
644 | String message = String.format("String value length [%d] for key [%s] is greater than maximum allowed [%d]", entry.getValue().textValue().length(), key, maxStringValueLength); | 654 | String message = String.format("String value length [%d] for key [%s] is greater than maximum allowed [%d]", entry.getValue().textValue().length(), key, maxStringValueLength); |
645 | throw new UncheckedApiException(new InvalidParametersException(message)); | 655 | throw new UncheckedApiException(new InvalidParametersException(message)); |
@@ -659,4 +669,27 @@ public class TelemetryController extends BaseController { | @@ -659,4 +669,27 @@ public class TelemetryController extends BaseController { | ||
659 | }); | 669 | }); |
660 | return attributes; | 670 | return attributes; |
661 | } | 671 | } |
672 | + | ||
673 | + private String toJsonStr(JsonNode value) { | ||
674 | + try { | ||
675 | + return mapper.writeValueAsString(value); | ||
676 | + } catch (JsonProcessingException e) { | ||
677 | + throw new JsonParseException("Can't parse jsonValue: " + value, e); | ||
678 | + } | ||
679 | + } | ||
680 | + | ||
681 | + private JsonNode toJsonNode(String value) { | ||
682 | + try { | ||
683 | + return mapper.readTree(value); | ||
684 | + } catch (IOException e) { | ||
685 | + throw new JsonParseException("Can't parse jsonValue: " + value, e); | ||
686 | + } | ||
687 | + } | ||
688 | + | ||
689 | + private Object getKvValue(KvEntry entry) { | ||
690 | + if (entry.getDataType() == DataType.JSON) { | ||
691 | + return toJsonNode(entry.getJsonValue().get()); | ||
692 | + } | ||
693 | + return entry.getValue(); | ||
694 | + } | ||
662 | } | 695 | } |
@@ -24,9 +24,9 @@ import org.springframework.beans.factory.annotation.Autowired; | @@ -24,9 +24,9 @@ import org.springframework.beans.factory.annotation.Autowired; | ||
24 | import org.springframework.context.annotation.Lazy; | 24 | import org.springframework.context.annotation.Lazy; |
25 | import org.springframework.stereotype.Service; | 25 | import org.springframework.stereotype.Service; |
26 | import org.springframework.util.StringUtils; | 26 | import org.springframework.util.StringUtils; |
27 | +import org.thingsboard.common.util.DonAsynchron; | ||
27 | import org.thingsboard.common.util.ThingsBoardThreadFactory; | 28 | import org.thingsboard.common.util.ThingsBoardThreadFactory; |
28 | import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg; | 29 | import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg; |
29 | -import org.thingsboard.common.util.DonAsynchron; | ||
30 | import org.thingsboard.server.actors.service.ActorService; | 30 | import org.thingsboard.server.actors.service.ActorService; |
31 | import org.thingsboard.server.common.data.DataConstants; | 31 | import org.thingsboard.server.common.data.DataConstants; |
32 | import org.thingsboard.server.common.data.EntityType; | 32 | import org.thingsboard.server.common.data.EntityType; |
@@ -36,7 +36,20 @@ import org.thingsboard.server.common.data.id.EntityId; | @@ -36,7 +36,20 @@ import org.thingsboard.server.common.data.id.EntityId; | ||
36 | import org.thingsboard.server.common.data.id.EntityIdFactory; | 36 | import org.thingsboard.server.common.data.id.EntityIdFactory; |
37 | import org.thingsboard.server.common.data.id.EntityViewId; | 37 | import org.thingsboard.server.common.data.id.EntityViewId; |
38 | import org.thingsboard.server.common.data.id.TenantId; | 38 | import org.thingsboard.server.common.data.id.TenantId; |
39 | -import org.thingsboard.server.common.data.kv.*; | 39 | +import org.thingsboard.server.common.data.kv.Aggregation; |
40 | +import org.thingsboard.server.common.data.kv.AttributeKvEntry; | ||
41 | +import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; | ||
42 | +import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery; | ||
43 | +import org.thingsboard.server.common.data.kv.BasicTsKvEntry; | ||
44 | +import org.thingsboard.server.common.data.kv.BooleanDataEntry; | ||
45 | +import org.thingsboard.server.common.data.kv.DataType; | ||
46 | +import org.thingsboard.server.common.data.kv.DoubleDataEntry; | ||
47 | +import org.thingsboard.server.common.data.kv.JsonDataEntry; | ||
48 | +import org.thingsboard.server.common.data.kv.KvEntry; | ||
49 | +import org.thingsboard.server.common.data.kv.LongDataEntry; | ||
50 | +import org.thingsboard.server.common.data.kv.ReadTsKvQuery; | ||
51 | +import org.thingsboard.server.common.data.kv.StringDataEntry; | ||
52 | +import org.thingsboard.server.common.data.kv.TsKvEntry; | ||
40 | import org.thingsboard.server.common.msg.cluster.SendToClusterMsg; | 53 | import org.thingsboard.server.common.msg.cluster.SendToClusterMsg; |
41 | import org.thingsboard.server.common.msg.cluster.ServerAddress; | 54 | import org.thingsboard.server.common.msg.cluster.ServerAddress; |
42 | import org.thingsboard.server.dao.attributes.AttributesService; | 55 | import org.thingsboard.server.dao.attributes.AttributesService; |
@@ -105,7 +118,7 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio | @@ -105,7 +118,7 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio | ||
105 | @Autowired | 118 | @Autowired |
106 | @Lazy | 119 | @Lazy |
107 | private ActorService actorService; | 120 | private ActorService actorService; |
108 | - | 121 | + |
109 | private ExecutorService tsCallBackExecutor; | 122 | private ExecutorService tsCallBackExecutor; |
110 | private ExecutorService wsCallBackExecutor; | 123 | private ExecutorService wsCallBackExecutor; |
111 | 124 | ||
@@ -692,6 +705,10 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio | @@ -692,6 +705,10 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio | ||
692 | Optional<Double> doubleValue = attr.getDoubleValue(); | 705 | Optional<Double> doubleValue = attr.getDoubleValue(); |
693 | doubleValue.ifPresent(dataBuilder::setDoubleValue); | 706 | doubleValue.ifPresent(dataBuilder::setDoubleValue); |
694 | break; | 707 | break; |
708 | + case JSON: | ||
709 | + Optional<String> jsonValue = attr.getJsonValue(); | ||
710 | + jsonValue.ifPresent(dataBuilder::setJsonValue); | ||
711 | + break; | ||
695 | case STRING: | 712 | case STRING: |
696 | Optional<String> stringValue = attr.getStrValue(); | 713 | Optional<String> stringValue = attr.getStrValue(); |
697 | stringValue.ifPresent(dataBuilder::setStrValue); | 714 | stringValue.ifPresent(dataBuilder::setStrValue); |
@@ -724,6 +741,9 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio | @@ -724,6 +741,9 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio | ||
724 | case STRING: | 741 | case STRING: |
725 | entry = new StringDataEntry(proto.getKey(), proto.getStrValue()); | 742 | entry = new StringDataEntry(proto.getKey(), proto.getStrValue()); |
726 | break; | 743 | break; |
744 | + case JSON: | ||
745 | + entry = new JsonDataEntry(proto.getKey(), proto.getJsonValue()); | ||
746 | + break; | ||
727 | } | 747 | } |
728 | return entry; | 748 | return entry; |
729 | } | 749 | } |
@@ -125,6 +125,7 @@ message KeyValueProto { | @@ -125,6 +125,7 @@ message KeyValueProto { | ||
125 | int64 longValue = 5; | 125 | int64 longValue = 5; |
126 | double doubleValue = 6; | 126 | double doubleValue = 6; |
127 | bool boolValue = 7; | 127 | bool boolValue = 7; |
128 | + string jsonValue = 8; | ||
128 | } | 129 | } |
129 | 130 | ||
130 | message FromDeviceRPCResponseProto { | 131 | message FromDeviceRPCResponseProto { |
@@ -30,7 +30,7 @@ public class ControllerSqlTestSuite { | @@ -30,7 +30,7 @@ public class ControllerSqlTestSuite { | ||
30 | 30 | ||
31 | @ClassRule | 31 | @ClassRule |
32 | public static CustomSqlUnit sqlUnit = new CustomSqlUnit( | 32 | public static CustomSqlUnit sqlUnit = new CustomSqlUnit( |
33 | - Arrays.asList("sql/schema-ts-hsql.sql", "sql/schema-entities.sql", "sql/schema-entities-idx.sql", "sql/system-data.sql"), | 33 | + Arrays.asList("sql/schema-ts-hsql.sql", "sql/schema-entities-hsql.sql", "sql/schema-entities-idx.sql", "sql/system-data.sql"), |
34 | "sql/drop-all-tables.sql", | 34 | "sql/drop-all-tables.sql", |
35 | "sql-test.properties"); | 35 | "sql-test.properties"); |
36 | } | 36 | } |
@@ -29,7 +29,7 @@ public class MqttSqlTestSuite { | @@ -29,7 +29,7 @@ public class MqttSqlTestSuite { | ||
29 | 29 | ||
30 | @ClassRule | 30 | @ClassRule |
31 | public static CustomSqlUnit sqlUnit = new CustomSqlUnit( | 31 | public static CustomSqlUnit sqlUnit = new CustomSqlUnit( |
32 | - Arrays.asList("sql/schema-ts-hsql.sql", "sql/schema-entities.sql", "sql/system-data.sql"), | 32 | + Arrays.asList("sql/schema-ts-hsql.sql", "sql/schema-entities-hsql.sql", "sql/system-data.sql"), |
33 | "sql/drop-all-tables.sql", | 33 | "sql/drop-all-tables.sql", |
34 | "sql-test.properties"); | 34 | "sql-test.properties"); |
35 | } | 35 | } |
@@ -30,7 +30,7 @@ public class RuleEngineSqlTestSuite { | @@ -30,7 +30,7 @@ public class RuleEngineSqlTestSuite { | ||
30 | 30 | ||
31 | @ClassRule | 31 | @ClassRule |
32 | public static CustomSqlUnit sqlUnit = new CustomSqlUnit( | 32 | public static CustomSqlUnit sqlUnit = new CustomSqlUnit( |
33 | - Arrays.asList("sql/schema-ts-hsql.sql", "sql/schema-entities.sql", "sql/system-data.sql"), | 33 | + Arrays.asList("sql/schema-ts-hsql.sql", "sql/schema-entities-hsql.sql", "sql/system-data.sql"), |
34 | "sql/drop-all-tables.sql", | 34 | "sql/drop-all-tables.sql", |
35 | "sql-test.properties"); | 35 | "sql-test.properties"); |
36 | } | 36 | } |
@@ -31,7 +31,7 @@ public class SystemSqlTestSuite { | @@ -31,7 +31,7 @@ public class SystemSqlTestSuite { | ||
31 | 31 | ||
32 | @ClassRule | 32 | @ClassRule |
33 | public static CustomSqlUnit sqlUnit = new CustomSqlUnit( | 33 | public static CustomSqlUnit sqlUnit = new CustomSqlUnit( |
34 | - Arrays.asList("sql/schema-ts-hsql.sql", "sql/schema-entities.sql", "sql/system-data.sql"), | 34 | + Arrays.asList("sql/schema-ts-hsql.sql", "sql/schema-entities-hsql.sql", "sql/system-data.sql"), |
35 | "sql/drop-all-tables.sql", | 35 | "sql/drop-all-tables.sql", |
36 | "sql-test.properties"); | 36 | "sql-test.properties"); |
37 | 37 |
@@ -35,6 +35,7 @@ import java.util.function.Consumer; | @@ -35,6 +35,7 @@ import java.util.function.Consumer; | ||
35 | @Slf4j | 35 | @Slf4j |
36 | public abstract class SearchTextBasedWithAdditionalInfo<I extends UUIDBased> extends SearchTextBased<I> implements HasAdditionalInfo { | 36 | public abstract class SearchTextBasedWithAdditionalInfo<I extends UUIDBased> extends SearchTextBased<I> implements HasAdditionalInfo { |
37 | 37 | ||
38 | + private static final ObjectMapper mapper = new ObjectMapper(); | ||
38 | private transient JsonNode additionalInfo; | 39 | private transient JsonNode additionalInfo; |
39 | @JsonIgnore | 40 | @JsonIgnore |
40 | private byte[] additionalInfoBytes; | 41 | private byte[] additionalInfoBytes; |
@@ -97,7 +98,7 @@ public abstract class SearchTextBasedWithAdditionalInfo<I extends UUIDBased> ext | @@ -97,7 +98,7 @@ public abstract class SearchTextBasedWithAdditionalInfo<I extends UUIDBased> ext | ||
97 | public static void setJson(JsonNode json, Consumer<JsonNode> jsonConsumer, Consumer<byte[]> bytesConsumer) { | 98 | public static void setJson(JsonNode json, Consumer<JsonNode> jsonConsumer, Consumer<byte[]> bytesConsumer) { |
98 | jsonConsumer.accept(json); | 99 | jsonConsumer.accept(json); |
99 | try { | 100 | try { |
100 | - bytesConsumer.accept(new ObjectMapper().writeValueAsBytes(json)); | 101 | + bytesConsumer.accept(mapper.writeValueAsBytes(json)); |
101 | } catch (JsonProcessingException e) { | 102 | } catch (JsonProcessingException e) { |
102 | log.warn("Can't serialize json data: ", e); | 103 | log.warn("Can't serialize json data: ", e); |
103 | } | 104 | } |
@@ -15,6 +15,8 @@ | @@ -15,6 +15,8 @@ | ||
15 | */ | 15 | */ |
16 | package org.thingsboard.server.common.data.kv; | 16 | package org.thingsboard.server.common.data.kv; |
17 | 17 | ||
18 | +import com.fasterxml.jackson.databind.JsonNode; | ||
19 | + | ||
18 | import java.util.Optional; | 20 | import java.util.Optional; |
19 | 21 | ||
20 | /** | 22 | /** |
@@ -66,6 +68,11 @@ public class BaseAttributeKvEntry implements AttributeKvEntry { | @@ -66,6 +68,11 @@ public class BaseAttributeKvEntry implements AttributeKvEntry { | ||
66 | } | 68 | } |
67 | 69 | ||
68 | @Override | 70 | @Override |
71 | + public Optional<String> getJsonValue() { | ||
72 | + return kv.getJsonValue(); | ||
73 | + } | ||
74 | + | ||
75 | + @Override | ||
69 | public String getValueAsString() { | 76 | public String getValueAsString() { |
70 | return kv.getValueAsString(); | 77 | return kv.getValueAsString(); |
71 | } | 78 | } |
@@ -52,6 +52,11 @@ public abstract class BasicKvEntry implements KvEntry { | @@ -52,6 +52,11 @@ public abstract class BasicKvEntry implements KvEntry { | ||
52 | } | 52 | } |
53 | 53 | ||
54 | @Override | 54 | @Override |
55 | + public Optional<String> getJsonValue() { | ||
56 | + return Optional.ofNullable(null); | ||
57 | + } | ||
58 | + | ||
59 | + @Override | ||
55 | public boolean equals(Object o) { | 60 | public boolean equals(Object o) { |
56 | if (this == o) return true; | 61 | if (this == o) return true; |
57 | if (!(o instanceof BasicKvEntry)) return false; | 62 | if (!(o instanceof BasicKvEntry)) return false; |
@@ -59,6 +59,11 @@ public class BasicTsKvEntry implements TsKvEntry { | @@ -59,6 +59,11 @@ public class BasicTsKvEntry implements TsKvEntry { | ||
59 | } | 59 | } |
60 | 60 | ||
61 | @Override | 61 | @Override |
62 | + public Optional<String> getJsonValue() { | ||
63 | + return kv.getJsonValue(); | ||
64 | + } | ||
65 | + | ||
66 | + @Override | ||
62 | public Object getValue() { | 67 | public Object getValue() { |
63 | return kv.getValue(); | 68 | return kv.getValue(); |
64 | } | 69 | } |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.common.data.kv; | ||
17 | + | ||
18 | +import java.util.Objects; | ||
19 | +import java.util.Optional; | ||
20 | + | ||
21 | +public class JsonDataEntry extends BasicKvEntry { | ||
22 | + private final String value; | ||
23 | + | ||
24 | + public JsonDataEntry(String key, String value) { | ||
25 | + super(key); | ||
26 | + this.value = value; | ||
27 | + } | ||
28 | + | ||
29 | + @Override | ||
30 | + public DataType getDataType() { | ||
31 | + return DataType.JSON; | ||
32 | + } | ||
33 | + | ||
34 | + @Override | ||
35 | + public Optional<String> getJsonValue() { | ||
36 | + return Optional.ofNullable(value); | ||
37 | + } | ||
38 | + | ||
39 | + @Override | ||
40 | + public boolean equals(Object o) { | ||
41 | + if (this == o) return true; | ||
42 | + if (!(o instanceof JsonDataEntry)) return false; | ||
43 | + if (!super.equals(o)) return false; | ||
44 | + JsonDataEntry that = (JsonDataEntry) o; | ||
45 | + return Objects.equals(value, that.value); | ||
46 | + } | ||
47 | + | ||
48 | + @Override | ||
49 | + public Object getValue() { | ||
50 | + return value; | ||
51 | + } | ||
52 | + | ||
53 | + @Override | ||
54 | + public int hashCode() { | ||
55 | + return Objects.hash(super.hashCode(), value); | ||
56 | + } | ||
57 | + | ||
58 | + @Override | ||
59 | + public String toString() { | ||
60 | + return "JsonDataEntry{" + | ||
61 | + "value=" + value + | ||
62 | + "} " + super.toString(); | ||
63 | + } | ||
64 | + | ||
65 | + @Override | ||
66 | + public String getValueAsString() { | ||
67 | + return value; | ||
68 | + } | ||
69 | +} |
@@ -37,6 +37,8 @@ public interface KvEntry extends Serializable { | @@ -37,6 +37,8 @@ public interface KvEntry extends Serializable { | ||
37 | 37 | ||
38 | Optional<Double> getDoubleValue(); | 38 | Optional<Double> getDoubleValue(); |
39 | 39 | ||
40 | + Optional<String> getJsonValue(); | ||
41 | + | ||
40 | String getValueAsString(); | 42 | String getValueAsString(); |
41 | 43 | ||
42 | Object getValue(); | 44 | Object getValue(); |
@@ -31,6 +31,7 @@ import org.thingsboard.server.common.data.kv.AttributeKvEntry; | @@ -31,6 +31,7 @@ import org.thingsboard.server.common.data.kv.AttributeKvEntry; | ||
31 | import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; | 31 | import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; |
32 | import org.thingsboard.server.common.data.kv.BooleanDataEntry; | 32 | import org.thingsboard.server.common.data.kv.BooleanDataEntry; |
33 | import org.thingsboard.server.common.data.kv.DoubleDataEntry; | 33 | import org.thingsboard.server.common.data.kv.DoubleDataEntry; |
34 | +import org.thingsboard.server.common.data.kv.JsonDataEntry; | ||
34 | import org.thingsboard.server.common.data.kv.KvEntry; | 35 | import org.thingsboard.server.common.data.kv.KvEntry; |
35 | import org.thingsboard.server.common.data.kv.LongDataEntry; | 36 | import org.thingsboard.server.common.data.kv.LongDataEntry; |
36 | import org.thingsboard.server.common.data.kv.StringDataEntry; | 37 | import org.thingsboard.server.common.data.kv.StringDataEntry; |
@@ -59,6 +60,7 @@ import java.util.stream.Collectors; | @@ -59,6 +60,7 @@ import java.util.stream.Collectors; | ||
59 | public class JsonConverter { | 60 | public class JsonConverter { |
60 | 61 | ||
61 | private static final Gson GSON = new Gson(); | 62 | private static final Gson GSON = new Gson(); |
63 | + private static final JsonParser JSON_PARSER = new JsonParser(); | ||
62 | private static final String CAN_T_PARSE_VALUE = "Can't parse value: "; | 64 | private static final String CAN_T_PARSE_VALUE = "Can't parse value: "; |
63 | private static final String DEVICE_PROPERTY = "device"; | 65 | private static final String DEVICE_PROPERTY = "device"; |
64 | 66 | ||
@@ -204,6 +206,14 @@ public class JsonConverter { | @@ -204,6 +206,14 @@ public class JsonConverter { | ||
204 | } else if (!value.isJsonNull()) { | 206 | } else if (!value.isJsonNull()) { |
205 | throw new JsonSyntaxException(CAN_T_PARSE_VALUE + value); | 207 | throw new JsonSyntaxException(CAN_T_PARSE_VALUE + value); |
206 | } | 208 | } |
209 | + } else if (element.isJsonObject() || element.isJsonArray()) { | ||
210 | + result.add(KeyValueProto | ||
211 | + .newBuilder() | ||
212 | + .setKey(valueEntry | ||
213 | + .getKey()) | ||
214 | + .setType(KeyValueType.JSON_V) | ||
215 | + .setJsonV(element.toString()) | ||
216 | + .build()); | ||
207 | } else if (!element.isJsonNull()) { | 217 | } else if (!element.isJsonNull()) { |
208 | throw new JsonSyntaxException(CAN_T_PARSE_VALUE + element); | 218 | throw new JsonSyntaxException(CAN_T_PARSE_VALUE + element); |
209 | } | 219 | } |
@@ -354,6 +364,9 @@ public class JsonConverter { | @@ -354,6 +364,9 @@ public class JsonConverter { | ||
354 | case LONG_V: | 364 | case LONG_V: |
355 | json.addProperty(name, entry.getLongV()); | 365 | json.addProperty(name, entry.getLongV()); |
356 | break; | 366 | break; |
367 | + case JSON_V: | ||
368 | + json.add(name, JSON_PARSER.parse(entry.getJsonV())); | ||
369 | + break; | ||
357 | } | 370 | } |
358 | } | 371 | } |
359 | 372 | ||
@@ -363,47 +376,48 @@ public class JsonConverter { | @@ -363,47 +376,48 @@ public class JsonConverter { | ||
363 | 376 | ||
364 | private static Consumer<TsKvProto> addToObjectFromProto(JsonObject result) { | 377 | private static Consumer<TsKvProto> addToObjectFromProto(JsonObject result) { |
365 | return de -> { | 378 | return de -> { |
366 | - JsonPrimitive value; | ||
367 | switch (de.getKv().getType()) { | 379 | switch (de.getKv().getType()) { |
368 | case BOOLEAN_V: | 380 | case BOOLEAN_V: |
369 | - value = new JsonPrimitive(de.getKv().getBoolV()); | 381 | + result.add(de.getKv().getKey(), new JsonPrimitive(de.getKv().getBoolV())); |
370 | break; | 382 | break; |
371 | case DOUBLE_V: | 383 | case DOUBLE_V: |
372 | - value = new JsonPrimitive(de.getKv().getDoubleV()); | 384 | + result.add(de.getKv().getKey(), new JsonPrimitive(de.getKv().getDoubleV())); |
373 | break; | 385 | break; |
374 | case LONG_V: | 386 | case LONG_V: |
375 | - value = new JsonPrimitive(de.getKv().getLongV()); | 387 | + result.add(de.getKv().getKey(), new JsonPrimitive(de.getKv().getLongV())); |
376 | break; | 388 | break; |
377 | case STRING_V: | 389 | case STRING_V: |
378 | - value = new JsonPrimitive(de.getKv().getStringV()); | 390 | + result.add(de.getKv().getKey(), new JsonPrimitive(de.getKv().getStringV())); |
379 | break; | 391 | break; |
392 | + case JSON_V: | ||
393 | + result.add(de.getKv().getKey(), JSON_PARSER.parse(de.getKv().getJsonV())); | ||
380 | default: | 394 | default: |
381 | throw new IllegalArgumentException("Unsupported data type: " + de.getKv().getType()); | 395 | throw new IllegalArgumentException("Unsupported data type: " + de.getKv().getType()); |
382 | } | 396 | } |
383 | - result.add(de.getKv().getKey(), value); | ||
384 | }; | 397 | }; |
385 | } | 398 | } |
386 | 399 | ||
387 | private static Consumer<AttributeKvEntry> addToObject(JsonObject result) { | 400 | private static Consumer<AttributeKvEntry> addToObject(JsonObject result) { |
388 | return de -> { | 401 | return de -> { |
389 | - JsonPrimitive value; | ||
390 | switch (de.getDataType()) { | 402 | switch (de.getDataType()) { |
391 | case BOOLEAN: | 403 | case BOOLEAN: |
392 | - value = new JsonPrimitive(de.getBooleanValue().get()); | 404 | + result.add(de.getKey(), new JsonPrimitive(de.getBooleanValue().get())); |
393 | break; | 405 | break; |
394 | case DOUBLE: | 406 | case DOUBLE: |
395 | - value = new JsonPrimitive(de.getDoubleValue().get()); | 407 | + result.add(de.getKey(), new JsonPrimitive(de.getDoubleValue().get())); |
396 | break; | 408 | break; |
397 | case LONG: | 409 | case LONG: |
398 | - value = new JsonPrimitive(de.getLongValue().get()); | 410 | + result.add(de.getKey(), new JsonPrimitive(de.getLongValue().get())); |
399 | break; | 411 | break; |
400 | case STRING: | 412 | case STRING: |
401 | - value = new JsonPrimitive(de.getStrValue().get()); | 413 | + result.add(de.getKey(), new JsonPrimitive(de.getStrValue().get())); |
414 | + break; | ||
415 | + case JSON: | ||
416 | + result.add(de.getKey(), JSON_PARSER.parse(de.getJsonValue().get())); | ||
402 | break; | 417 | break; |
403 | default: | 418 | default: |
404 | throw new IllegalArgumentException("Unsupported data type: " + de.getDataType()); | 419 | throw new IllegalArgumentException("Unsupported data type: " + de.getDataType()); |
405 | } | 420 | } |
406 | - result.add(de.getKey(), value); | ||
407 | }; | 421 | }; |
408 | } | 422 | } |
409 | 423 | ||
@@ -464,6 +478,8 @@ public class JsonConverter { | @@ -464,6 +478,8 @@ public class JsonConverter { | ||
464 | } else { | 478 | } else { |
465 | throw new JsonSyntaxException(CAN_T_PARSE_VALUE + value); | 479 | throw new JsonSyntaxException(CAN_T_PARSE_VALUE + value); |
466 | } | 480 | } |
481 | + } else if (element.isJsonObject() || element.isJsonArray()) { | ||
482 | + result.add(new JsonDataEntry(valueEntry.getKey(), element.toString())); | ||
467 | } else { | 483 | } else { |
468 | throw new JsonSyntaxException(CAN_T_PARSE_VALUE + element); | 484 | throw new JsonSyntaxException(CAN_T_PARSE_VALUE + element); |
469 | } | 485 | } |
@@ -47,6 +47,7 @@ enum KeyValueType { | @@ -47,6 +47,7 @@ enum KeyValueType { | ||
47 | LONG_V = 1; | 47 | LONG_V = 1; |
48 | DOUBLE_V = 2; | 48 | DOUBLE_V = 2; |
49 | STRING_V = 3; | 49 | STRING_V = 3; |
50 | + JSON_V = 4; | ||
50 | } | 51 | } |
51 | 52 | ||
52 | message KeyValueProto { | 53 | message KeyValueProto { |
@@ -56,6 +57,7 @@ message KeyValueProto { | @@ -56,6 +57,7 @@ message KeyValueProto { | ||
56 | int64 long_v = 4; | 57 | int64 long_v = 4; |
57 | double double_v = 5; | 58 | double double_v = 5; |
58 | string string_v = 6; | 59 | string string_v = 6; |
60 | + string json_v = 7; | ||
59 | } | 61 | } |
60 | 62 | ||
61 | message TsKvProto { | 63 | message TsKvProto { |
@@ -112,31 +112,18 @@ public class CassandraBaseAttributesDao extends CassandraAbstractAsyncDao implem | @@ -112,31 +112,18 @@ public class CassandraBaseAttributesDao extends CassandraAbstractAsyncDao implem | ||
112 | 112 | ||
113 | @Override | 113 | @Override |
114 | public ListenableFuture<Void> save(TenantId tenantId, EntityId entityId, String attributeType, AttributeKvEntry attribute) { | 114 | public ListenableFuture<Void> save(TenantId tenantId, EntityId entityId, String attributeType, AttributeKvEntry attribute) { |
115 | - BoundStatement stmt = getSaveStmt().bind(); | ||
116 | - stmt.setString(0, entityId.getEntityType().name()); | ||
117 | - stmt.setUUID(1, entityId.getId()); | ||
118 | - stmt.setString(2, attributeType); | ||
119 | - stmt.setString(3, attribute.getKey()); | ||
120 | - stmt.setLong(4, attribute.getLastUpdateTs()); | ||
121 | - stmt.setString(5, attribute.getStrValue().orElse(null)); | ||
122 | - Optional<Boolean> booleanValue = attribute.getBooleanValue(); | ||
123 | - if (booleanValue.isPresent()) { | ||
124 | - stmt.setBool(6, booleanValue.get()); | ||
125 | - } else { | ||
126 | - stmt.setToNull(6); | ||
127 | - } | ||
128 | - Optional<Long> longValue = attribute.getLongValue(); | ||
129 | - if (longValue.isPresent()) { | ||
130 | - stmt.setLong(7, longValue.get()); | ||
131 | - } else { | ||
132 | - stmt.setToNull(7); | ||
133 | - } | ||
134 | - Optional<Double> doubleValue = attribute.getDoubleValue(); | ||
135 | - if (doubleValue.isPresent()) { | ||
136 | - stmt.setDouble(8, doubleValue.get()); | ||
137 | - } else { | ||
138 | - stmt.setToNull(8); | ||
139 | - } | 115 | + BoundStatement stmt = getSaveStmt().bind() |
116 | + .setString(0, entityId.getEntityType().name()) | ||
117 | + .setUUID(1, entityId.getId()) | ||
118 | + .setString(2, attributeType) | ||
119 | + .setString(3, attribute.getKey()) | ||
120 | + .setLong(4, attribute.getLastUpdateTs()) | ||
121 | + .set(5, attribute.getStrValue().orElse(null), String.class) | ||
122 | + .set(6, attribute.getBooleanValue().orElse(null), Boolean.class) | ||
123 | + .set(7, attribute.getLongValue().orElse(null), Long.class) | ||
124 | + .set(8, attribute.getDoubleValue().orElse(null), Double.class) | ||
125 | + .set(9, attribute.getJsonValue().orElse(null), String.class); | ||
126 | + | ||
140 | log.trace("Generated save stmt [{}] for entityId {} and attributeType {} and attribute", stmt, entityId, attributeType, attribute); | 127 | log.trace("Generated save stmt [{}] for entityId {} and attributeType {} and attribute", stmt, entityId, attributeType, attribute); |
141 | return getFuture(executeAsyncWrite(tenantId, stmt), rs -> null); | 128 | return getFuture(executeAsyncWrite(tenantId, stmt), rs -> null); |
142 | } | 129 | } |
@@ -172,8 +159,9 @@ public class CassandraBaseAttributesDao extends CassandraAbstractAsyncDao implem | @@ -172,8 +159,9 @@ public class CassandraBaseAttributesDao extends CassandraAbstractAsyncDao implem | ||
172 | "," + ModelConstants.BOOLEAN_VALUE_COLUMN + | 159 | "," + ModelConstants.BOOLEAN_VALUE_COLUMN + |
173 | "," + ModelConstants.LONG_VALUE_COLUMN + | 160 | "," + ModelConstants.LONG_VALUE_COLUMN + |
174 | "," + ModelConstants.DOUBLE_VALUE_COLUMN + | 161 | "," + ModelConstants.DOUBLE_VALUE_COLUMN + |
162 | + "," + ModelConstants.JSON_VALUE_COLUMN + | ||
175 | ")" + | 163 | ")" + |
176 | - " VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)"); | 164 | + " VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); |
177 | } | 165 | } |
178 | return saveStmt; | 166 | return saveStmt; |
179 | } | 167 | } |
@@ -369,17 +369,18 @@ public class ModelConstants { | @@ -369,17 +369,18 @@ public class ModelConstants { | ||
369 | public static final String STRING_VALUE_COLUMN = "str_v"; | 369 | public static final String STRING_VALUE_COLUMN = "str_v"; |
370 | public static final String LONG_VALUE_COLUMN = "long_v"; | 370 | public static final String LONG_VALUE_COLUMN = "long_v"; |
371 | public static final String DOUBLE_VALUE_COLUMN = "dbl_v"; | 371 | public static final String DOUBLE_VALUE_COLUMN = "dbl_v"; |
372 | + public static final String JSON_VALUE_COLUMN = "json_v"; | ||
372 | 373 | ||
373 | - protected static final String[] NONE_AGGREGATION_COLUMNS = new String[]{LONG_VALUE_COLUMN, DOUBLE_VALUE_COLUMN, BOOLEAN_VALUE_COLUMN, STRING_VALUE_COLUMN, KEY_COLUMN, TS_COLUMN}; | 374 | + protected static final String[] NONE_AGGREGATION_COLUMNS = new String[]{LONG_VALUE_COLUMN, DOUBLE_VALUE_COLUMN, BOOLEAN_VALUE_COLUMN, STRING_VALUE_COLUMN, JSON_VALUE_COLUMN, KEY_COLUMN, TS_COLUMN}; |
374 | 375 | ||
375 | - protected static final String[] COUNT_AGGREGATION_COLUMNS = new String[]{count(LONG_VALUE_COLUMN), count(DOUBLE_VALUE_COLUMN), count(BOOLEAN_VALUE_COLUMN), count(STRING_VALUE_COLUMN)}; | 376 | + protected static final String[] COUNT_AGGREGATION_COLUMNS = new String[]{count(LONG_VALUE_COLUMN), count(DOUBLE_VALUE_COLUMN), count(BOOLEAN_VALUE_COLUMN), count(STRING_VALUE_COLUMN), count(JSON_VALUE_COLUMN)}; |
376 | 377 | ||
377 | - protected static final String[] MIN_AGGREGATION_COLUMNS = ArrayUtils.addAll(COUNT_AGGREGATION_COLUMNS, | ||
378 | - new String[]{min(LONG_VALUE_COLUMN), min(DOUBLE_VALUE_COLUMN), min(BOOLEAN_VALUE_COLUMN), min(STRING_VALUE_COLUMN)}); | ||
379 | - protected static final String[] MAX_AGGREGATION_COLUMNS = ArrayUtils.addAll(COUNT_AGGREGATION_COLUMNS, | ||
380 | - new String[]{max(LONG_VALUE_COLUMN), max(DOUBLE_VALUE_COLUMN), max(BOOLEAN_VALUE_COLUMN), max(STRING_VALUE_COLUMN)}); | ||
381 | - protected static final String[] SUM_AGGREGATION_COLUMNS = ArrayUtils.addAll(COUNT_AGGREGATION_COLUMNS, | ||
382 | - new String[]{sum(LONG_VALUE_COLUMN), sum(DOUBLE_VALUE_COLUMN)}); | 378 | + protected static final String[] MIN_AGGREGATION_COLUMNS = |
379 | + ArrayUtils.addAll(COUNT_AGGREGATION_COLUMNS, new String[]{min(LONG_VALUE_COLUMN), min(DOUBLE_VALUE_COLUMN), min(BOOLEAN_VALUE_COLUMN), min(STRING_VALUE_COLUMN), min(JSON_VALUE_COLUMN)}); | ||
380 | + protected static final String[] MAX_AGGREGATION_COLUMNS = | ||
381 | + ArrayUtils.addAll(COUNT_AGGREGATION_COLUMNS, new String[]{max(LONG_VALUE_COLUMN), max(DOUBLE_VALUE_COLUMN), max(BOOLEAN_VALUE_COLUMN), max(STRING_VALUE_COLUMN), max(JSON_VALUE_COLUMN)}); | ||
382 | + protected static final String[] SUM_AGGREGATION_COLUMNS = | ||
383 | + ArrayUtils.addAll(COUNT_AGGREGATION_COLUMNS, new String[]{sum(LONG_VALUE_COLUMN), sum(DOUBLE_VALUE_COLUMN)}); | ||
383 | protected static final String[] AVG_AGGREGATION_COLUMNS = SUM_AGGREGATION_COLUMNS; | 384 | protected static final String[] AVG_AGGREGATION_COLUMNS = SUM_AGGREGATION_COLUMNS; |
384 | 385 | ||
385 | public static String min(String s) { | 386 | public static String min(String s) { |
@@ -19,6 +19,7 @@ import lombok.Data; | @@ -19,6 +19,7 @@ import lombok.Data; | ||
19 | import org.thingsboard.server.common.data.kv.BasicTsKvEntry; | 19 | import org.thingsboard.server.common.data.kv.BasicTsKvEntry; |
20 | import org.thingsboard.server.common.data.kv.BooleanDataEntry; | 20 | import org.thingsboard.server.common.data.kv.BooleanDataEntry; |
21 | import org.thingsboard.server.common.data.kv.DoubleDataEntry; | 21 | import org.thingsboard.server.common.data.kv.DoubleDataEntry; |
22 | +import org.thingsboard.server.common.data.kv.JsonDataEntry; | ||
22 | import org.thingsboard.server.common.data.kv.KvEntry; | 23 | import org.thingsboard.server.common.data.kv.KvEntry; |
23 | import org.thingsboard.server.common.data.kv.LongDataEntry; | 24 | import org.thingsboard.server.common.data.kv.LongDataEntry; |
24 | import org.thingsboard.server.common.data.kv.StringDataEntry; | 25 | import org.thingsboard.server.common.data.kv.StringDataEntry; |
@@ -29,12 +30,12 @@ import javax.persistence.Column; | @@ -29,12 +30,12 @@ import javax.persistence.Column; | ||
29 | import javax.persistence.Id; | 30 | import javax.persistence.Id; |
30 | import javax.persistence.MappedSuperclass; | 31 | import javax.persistence.MappedSuperclass; |
31 | import javax.persistence.Transient; | 32 | import javax.persistence.Transient; |
32 | - | ||
33 | import java.util.UUID; | 33 | import java.util.UUID; |
34 | 34 | ||
35 | import static org.thingsboard.server.dao.model.ModelConstants.BOOLEAN_VALUE_COLUMN; | 35 | import static org.thingsboard.server.dao.model.ModelConstants.BOOLEAN_VALUE_COLUMN; |
36 | import static org.thingsboard.server.dao.model.ModelConstants.DOUBLE_VALUE_COLUMN; | 36 | import static org.thingsboard.server.dao.model.ModelConstants.DOUBLE_VALUE_COLUMN; |
37 | import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_ID_COLUMN; | 37 | import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_ID_COLUMN; |
38 | +import static org.thingsboard.server.dao.model.ModelConstants.JSON_VALUE_COLUMN; | ||
38 | import static org.thingsboard.server.dao.model.ModelConstants.LONG_VALUE_COLUMN; | 39 | import static org.thingsboard.server.dao.model.ModelConstants.LONG_VALUE_COLUMN; |
39 | import static org.thingsboard.server.dao.model.ModelConstants.STRING_VALUE_COLUMN; | 40 | import static org.thingsboard.server.dao.model.ModelConstants.STRING_VALUE_COLUMN; |
40 | import static org.thingsboard.server.dao.model.ModelConstants.TS_COLUMN; | 41 | import static org.thingsboard.server.dao.model.ModelConstants.TS_COLUMN; |
@@ -68,6 +69,9 @@ public abstract class AbstractTsKvEntity implements ToData<TsKvEntry> { | @@ -68,6 +69,9 @@ public abstract class AbstractTsKvEntity implements ToData<TsKvEntry> { | ||
68 | @Column(name = DOUBLE_VALUE_COLUMN) | 69 | @Column(name = DOUBLE_VALUE_COLUMN) |
69 | protected Double doubleValue; | 70 | protected Double doubleValue; |
70 | 71 | ||
72 | + @Column(name = JSON_VALUE_COLUMN) | ||
73 | + protected String jsonValue; | ||
74 | + | ||
71 | @Transient | 75 | @Transient |
72 | protected String strKey; | 76 | protected String strKey; |
73 | 77 | ||
@@ -93,6 +97,8 @@ public abstract class AbstractTsKvEntity implements ToData<TsKvEntry> { | @@ -93,6 +97,8 @@ public abstract class AbstractTsKvEntity implements ToData<TsKvEntry> { | ||
93 | kvEntry = new DoubleDataEntry(strKey, doubleValue); | 97 | kvEntry = new DoubleDataEntry(strKey, doubleValue); |
94 | } else if (booleanValue != null) { | 98 | } else if (booleanValue != null) { |
95 | kvEntry = new BooleanDataEntry(strKey, booleanValue); | 99 | kvEntry = new BooleanDataEntry(strKey, booleanValue); |
100 | + } else if (jsonValue != null) { | ||
101 | + kvEntry = new JsonDataEntry(strKey, jsonValue); | ||
96 | } | 102 | } |
97 | return new BasicTsKvEntry(ts, kvEntry); | 103 | return new BasicTsKvEntry(ts, kvEntry); |
98 | } | 104 | } |
@@ -20,6 +20,7 @@ import org.thingsboard.server.common.data.kv.AttributeKvEntry; | @@ -20,6 +20,7 @@ import org.thingsboard.server.common.data.kv.AttributeKvEntry; | ||
20 | import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; | 20 | import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; |
21 | import org.thingsboard.server.common.data.kv.BooleanDataEntry; | 21 | import org.thingsboard.server.common.data.kv.BooleanDataEntry; |
22 | import org.thingsboard.server.common.data.kv.DoubleDataEntry; | 22 | import org.thingsboard.server.common.data.kv.DoubleDataEntry; |
23 | +import org.thingsboard.server.common.data.kv.JsonDataEntry; | ||
23 | import org.thingsboard.server.common.data.kv.KvEntry; | 24 | import org.thingsboard.server.common.data.kv.KvEntry; |
24 | import org.thingsboard.server.common.data.kv.LongDataEntry; | 25 | import org.thingsboard.server.common.data.kv.LongDataEntry; |
25 | import org.thingsboard.server.common.data.kv.StringDataEntry; | 26 | import org.thingsboard.server.common.data.kv.StringDataEntry; |
@@ -33,6 +34,7 @@ import java.io.Serializable; | @@ -33,6 +34,7 @@ import java.io.Serializable; | ||
33 | 34 | ||
34 | import static org.thingsboard.server.dao.model.ModelConstants.BOOLEAN_VALUE_COLUMN; | 35 | import static org.thingsboard.server.dao.model.ModelConstants.BOOLEAN_VALUE_COLUMN; |
35 | import static org.thingsboard.server.dao.model.ModelConstants.DOUBLE_VALUE_COLUMN; | 36 | import static org.thingsboard.server.dao.model.ModelConstants.DOUBLE_VALUE_COLUMN; |
37 | +import static org.thingsboard.server.dao.model.ModelConstants.JSON_VALUE_COLUMN; | ||
36 | import static org.thingsboard.server.dao.model.ModelConstants.LAST_UPDATE_TS_COLUMN; | 38 | import static org.thingsboard.server.dao.model.ModelConstants.LAST_UPDATE_TS_COLUMN; |
37 | import static org.thingsboard.server.dao.model.ModelConstants.LONG_VALUE_COLUMN; | 39 | import static org.thingsboard.server.dao.model.ModelConstants.LONG_VALUE_COLUMN; |
38 | import static org.thingsboard.server.dao.model.ModelConstants.STRING_VALUE_COLUMN; | 40 | import static org.thingsboard.server.dao.model.ModelConstants.STRING_VALUE_COLUMN; |
@@ -57,6 +59,9 @@ public class AttributeKvEntity implements ToData<AttributeKvEntry>, Serializable | @@ -57,6 +59,9 @@ public class AttributeKvEntity implements ToData<AttributeKvEntry>, Serializable | ||
57 | @Column(name = DOUBLE_VALUE_COLUMN) | 59 | @Column(name = DOUBLE_VALUE_COLUMN) |
58 | private Double doubleValue; | 60 | private Double doubleValue; |
59 | 61 | ||
62 | + @Column(name = JSON_VALUE_COLUMN) | ||
63 | + private String jsonValue; | ||
64 | + | ||
60 | @Column(name = LAST_UPDATE_TS_COLUMN) | 65 | @Column(name = LAST_UPDATE_TS_COLUMN) |
61 | private Long lastUpdateTs; | 66 | private Long lastUpdateTs; |
62 | 67 | ||
@@ -71,7 +76,10 @@ public class AttributeKvEntity implements ToData<AttributeKvEntry>, Serializable | @@ -71,7 +76,10 @@ public class AttributeKvEntity implements ToData<AttributeKvEntry>, Serializable | ||
71 | kvEntry = new DoubleDataEntry(id.getAttributeKey(), doubleValue); | 76 | kvEntry = new DoubleDataEntry(id.getAttributeKey(), doubleValue); |
72 | } else if (longValue != null) { | 77 | } else if (longValue != null) { |
73 | kvEntry = new LongDataEntry(id.getAttributeKey(), longValue); | 78 | kvEntry = new LongDataEntry(id.getAttributeKey(), longValue); |
79 | + } else if (jsonValue != null) { | ||
80 | + kvEntry = new JsonDataEntry(id.getAttributeKey(), jsonValue); | ||
74 | } | 81 | } |
82 | + | ||
75 | return new BaseAttributeKvEntry(kvEntry, lastUpdateTs); | 83 | return new BaseAttributeKvEntry(kvEntry, lastUpdateTs); |
76 | } | 84 | } |
77 | } | 85 | } |
@@ -16,30 +16,16 @@ | @@ -16,30 +16,16 @@ | ||
16 | package org.thingsboard.server.dao.model.sqlts.hsql; | 16 | package org.thingsboard.server.dao.model.sqlts.hsql; |
17 | 17 | ||
18 | import lombok.Data; | 18 | import lombok.Data; |
19 | -import org.thingsboard.server.common.data.EntityType; | ||
20 | -import org.thingsboard.server.common.data.kv.BasicTsKvEntry; | ||
21 | -import org.thingsboard.server.common.data.kv.BooleanDataEntry; | ||
22 | -import org.thingsboard.server.common.data.kv.DoubleDataEntry; | ||
23 | -import org.thingsboard.server.common.data.kv.KvEntry; | ||
24 | -import org.thingsboard.server.common.data.kv.LongDataEntry; | ||
25 | -import org.thingsboard.server.common.data.kv.StringDataEntry; | ||
26 | import org.thingsboard.server.common.data.kv.TsKvEntry; | 19 | import org.thingsboard.server.common.data.kv.TsKvEntry; |
27 | import org.thingsboard.server.dao.model.ToData; | 20 | import org.thingsboard.server.dao.model.ToData; |
28 | import org.thingsboard.server.dao.model.sql.AbstractTsKvEntity; | 21 | import org.thingsboard.server.dao.model.sql.AbstractTsKvEntity; |
29 | 22 | ||
30 | import javax.persistence.Column; | 23 | import javax.persistence.Column; |
31 | import javax.persistence.Entity; | 24 | import javax.persistence.Entity; |
32 | -import javax.persistence.EnumType; | ||
33 | -import javax.persistence.Enumerated; | ||
34 | import javax.persistence.Id; | 25 | import javax.persistence.Id; |
35 | import javax.persistence.IdClass; | 26 | import javax.persistence.IdClass; |
36 | import javax.persistence.Table; | 27 | import javax.persistence.Table; |
37 | -import javax.persistence.Transient; | ||
38 | 28 | ||
39 | -import java.util.UUID; | ||
40 | - | ||
41 | -import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_ID_COLUMN; | ||
42 | -import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_TYPE_COLUMN; | ||
43 | import static org.thingsboard.server.dao.model.ModelConstants.KEY_COLUMN; | 29 | import static org.thingsboard.server.dao.model.ModelConstants.KEY_COLUMN; |
44 | 30 | ||
45 | @Data | 31 | @Data |
@@ -98,12 +84,14 @@ public final class TsKvEntity extends AbstractTsKvEntity implements ToData<TsKvE | @@ -98,12 +84,14 @@ public final class TsKvEntity extends AbstractTsKvEntity implements ToData<TsKvE | ||
98 | } | 84 | } |
99 | } | 85 | } |
100 | 86 | ||
101 | - public TsKvEntity(Long booleanValueCount, Long strValueCount, Long longValueCount, Long doubleValueCount) { | 87 | + public TsKvEntity(Long booleanValueCount, Long strValueCount, Long longValueCount, Long doubleValueCount, Long jsonValueCount) { |
102 | if (!isAllNull(booleanValueCount, strValueCount, longValueCount, doubleValueCount)) { | 88 | if (!isAllNull(booleanValueCount, strValueCount, longValueCount, doubleValueCount)) { |
103 | if (booleanValueCount != 0) { | 89 | if (booleanValueCount != 0) { |
104 | this.longValue = booleanValueCount; | 90 | this.longValue = booleanValueCount; |
105 | } else if (strValueCount != 0) { | 91 | } else if (strValueCount != 0) { |
106 | this.longValue = strValueCount; | 92 | this.longValue = strValueCount; |
93 | + } else if (jsonValueCount != 0) { | ||
94 | + this.longValue = jsonValueCount; | ||
107 | } else { | 95 | } else { |
108 | this.longValue = longValueCount + doubleValueCount; | 96 | this.longValue = longValueCount + doubleValueCount; |
109 | } | 97 | } |
@@ -52,6 +52,7 @@ import static org.thingsboard.server.dao.model.ModelConstants.KEY_COLUMN; | @@ -52,6 +52,7 @@ import static org.thingsboard.server.dao.model.ModelConstants.KEY_COLUMN; | ||
52 | @ColumnResult(name = "boolValue", type = Boolean.class), | 52 | @ColumnResult(name = "boolValue", type = Boolean.class), |
53 | @ColumnResult(name = "longValue", type = Long.class), | 53 | @ColumnResult(name = "longValue", type = Long.class), |
54 | @ColumnResult(name = "doubleValue", type = Double.class), | 54 | @ColumnResult(name = "doubleValue", type = Double.class), |
55 | + @ColumnResult(name = "jsonValue", type = String.class), | ||
55 | @ColumnResult(name = "ts", type = Long.class), | 56 | @ColumnResult(name = "ts", type = Long.class), |
56 | 57 | ||
57 | } | 58 | } |
@@ -74,13 +75,13 @@ public final class TsKvLatestEntity extends AbstractTsKvEntity { | @@ -74,13 +75,13 @@ public final class TsKvLatestEntity extends AbstractTsKvEntity { | ||
74 | 75 | ||
75 | @Override | 76 | @Override |
76 | public boolean isNotEmpty() { | 77 | public boolean isNotEmpty() { |
77 | - return strValue != null || longValue != null || doubleValue != null || booleanValue != null; | 78 | + return strValue != null || longValue != null || doubleValue != null || booleanValue != null || jsonValue != null; |
78 | } | 79 | } |
79 | 80 | ||
80 | public TsKvLatestEntity() { | 81 | public TsKvLatestEntity() { |
81 | } | 82 | } |
82 | 83 | ||
83 | - public TsKvLatestEntity(UUID entityId, Integer key, String strKey, String strValue, Boolean boolValue, Long longValue, Double doubleValue, Long ts) { | 84 | + public TsKvLatestEntity(UUID entityId, Integer key, String strKey, String strValue, Boolean boolValue, Long longValue, Double doubleValue, String jsonValue, Long ts) { |
84 | this.entityId = entityId; | 85 | this.entityId = entityId; |
85 | this.key = key; | 86 | this.key = key; |
86 | this.ts = ts; | 87 | this.ts = ts; |
@@ -88,6 +89,7 @@ public final class TsKvLatestEntity extends AbstractTsKvEntity { | @@ -88,6 +89,7 @@ public final class TsKvLatestEntity extends AbstractTsKvEntity { | ||
88 | this.doubleValue = doubleValue; | 89 | this.doubleValue = doubleValue; |
89 | this.strValue = strValue; | 90 | this.strValue = strValue; |
90 | this.booleanValue = boolValue; | 91 | this.booleanValue = boolValue; |
92 | + this.jsonValue = jsonValue; | ||
91 | this.strKey = strKey; | 93 | this.strKey = strKey; |
92 | } | 94 | } |
93 | } | 95 | } |
@@ -16,14 +16,6 @@ | @@ -16,14 +16,6 @@ | ||
16 | package org.thingsboard.server.dao.model.sqlts.psql; | 16 | package org.thingsboard.server.dao.model.sqlts.psql; |
17 | 17 | ||
18 | import lombok.Data; | 18 | import lombok.Data; |
19 | -import org.thingsboard.server.common.data.kv.BasicTsKvEntry; | ||
20 | -import org.thingsboard.server.common.data.kv.BooleanDataEntry; | ||
21 | -import org.thingsboard.server.common.data.kv.DoubleDataEntry; | ||
22 | -import org.thingsboard.server.common.data.kv.KvEntry; | ||
23 | -import org.thingsboard.server.common.data.kv.LongDataEntry; | ||
24 | -import org.thingsboard.server.common.data.kv.StringDataEntry; | ||
25 | -import org.thingsboard.server.common.data.kv.TsKvEntry; | ||
26 | -import org.thingsboard.server.dao.model.ToData; | ||
27 | import org.thingsboard.server.dao.model.sql.AbstractTsKvEntity; | 19 | import org.thingsboard.server.dao.model.sql.AbstractTsKvEntity; |
28 | 20 | ||
29 | import javax.persistence.Column; | 21 | import javax.persistence.Column; |
@@ -31,10 +23,7 @@ import javax.persistence.Entity; | @@ -31,10 +23,7 @@ import javax.persistence.Entity; | ||
31 | import javax.persistence.Id; | 23 | import javax.persistence.Id; |
32 | import javax.persistence.IdClass; | 24 | import javax.persistence.IdClass; |
33 | import javax.persistence.Table; | 25 | import javax.persistence.Table; |
34 | -import javax.persistence.Transient; | ||
35 | -import java.util.UUID; | ||
36 | 26 | ||
37 | -import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_ID_COLUMN; | ||
38 | import static org.thingsboard.server.dao.model.ModelConstants.KEY_COLUMN; | 27 | import static org.thingsboard.server.dao.model.ModelConstants.KEY_COLUMN; |
39 | 28 | ||
40 | @Data | 29 | @Data |
@@ -93,12 +82,14 @@ public final class TsKvEntity extends AbstractTsKvEntity { | @@ -93,12 +82,14 @@ public final class TsKvEntity extends AbstractTsKvEntity { | ||
93 | } | 82 | } |
94 | } | 83 | } |
95 | 84 | ||
96 | - public TsKvEntity(Long booleanValueCount, Long strValueCount, Long longValueCount, Long doubleValueCount) { | 85 | + public TsKvEntity(Long booleanValueCount, Long strValueCount, Long longValueCount, Long doubleValueCount, Long jsonValueCount) { |
97 | if (!isAllNull(booleanValueCount, strValueCount, longValueCount, doubleValueCount)) { | 86 | if (!isAllNull(booleanValueCount, strValueCount, longValueCount, doubleValueCount)) { |
98 | if (booleanValueCount != 0) { | 87 | if (booleanValueCount != 0) { |
99 | this.longValue = booleanValueCount; | 88 | this.longValue = booleanValueCount; |
100 | } else if (strValueCount != 0) { | 89 | } else if (strValueCount != 0) { |
101 | this.longValue = strValueCount; | 90 | this.longValue = strValueCount; |
91 | + } else if (jsonValueCount != 0) { | ||
92 | + this.longValue = jsonValueCount; | ||
102 | } else { | 93 | } else { |
103 | this.longValue = longValueCount + doubleValueCount; | 94 | this.longValue = longValueCount + doubleValueCount; |
104 | } | 95 | } |
@@ -18,12 +18,6 @@ package org.thingsboard.server.dao.model.sqlts.timescale; | @@ -18,12 +18,6 @@ package org.thingsboard.server.dao.model.sqlts.timescale; | ||
18 | import lombok.Data; | 18 | import lombok.Data; |
19 | import lombok.EqualsAndHashCode; | 19 | import lombok.EqualsAndHashCode; |
20 | import org.springframework.util.StringUtils; | 20 | import org.springframework.util.StringUtils; |
21 | -import org.thingsboard.server.common.data.kv.BasicTsKvEntry; | ||
22 | -import org.thingsboard.server.common.data.kv.BooleanDataEntry; | ||
23 | -import org.thingsboard.server.common.data.kv.DoubleDataEntry; | ||
24 | -import org.thingsboard.server.common.data.kv.KvEntry; | ||
25 | -import org.thingsboard.server.common.data.kv.LongDataEntry; | ||
26 | -import org.thingsboard.server.common.data.kv.StringDataEntry; | ||
27 | import org.thingsboard.server.common.data.kv.TsKvEntry; | 21 | import org.thingsboard.server.common.data.kv.TsKvEntry; |
28 | import org.thingsboard.server.dao.model.ToData; | 22 | import org.thingsboard.server.dao.model.ToData; |
29 | import org.thingsboard.server.dao.model.sql.AbstractTsKvEntity; | 23 | import org.thingsboard.server.dao.model.sql.AbstractTsKvEntity; |
@@ -39,10 +33,8 @@ import javax.persistence.NamedNativeQuery; | @@ -39,10 +33,8 @@ import javax.persistence.NamedNativeQuery; | ||
39 | import javax.persistence.SqlResultSetMapping; | 33 | import javax.persistence.SqlResultSetMapping; |
40 | import javax.persistence.SqlResultSetMappings; | 34 | import javax.persistence.SqlResultSetMappings; |
41 | import javax.persistence.Table; | 35 | import javax.persistence.Table; |
42 | -import javax.persistence.Transient; | ||
43 | import java.util.UUID; | 36 | import java.util.UUID; |
44 | 37 | ||
45 | -import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_ID_COLUMN; | ||
46 | import static org.thingsboard.server.dao.model.ModelConstants.KEY_COLUMN; | 38 | import static org.thingsboard.server.dao.model.ModelConstants.KEY_COLUMN; |
47 | import static org.thingsboard.server.dao.model.ModelConstants.TENANT_ID_COLUMN; | 39 | import static org.thingsboard.server.dao.model.ModelConstants.TENANT_ID_COLUMN; |
48 | import static org.thingsboard.server.dao.sqlts.timescale.AggregationRepository.FIND_AVG; | 40 | import static org.thingsboard.server.dao.sqlts.timescale.AggregationRepository.FIND_AVG; |
@@ -92,6 +84,7 @@ import static org.thingsboard.server.dao.sqlts.timescale.AggregationRepository.F | @@ -92,6 +84,7 @@ import static org.thingsboard.server.dao.sqlts.timescale.AggregationRepository.F | ||
92 | @ColumnResult(name = "strValueCount", type = Long.class), | 84 | @ColumnResult(name = "strValueCount", type = Long.class), |
93 | @ColumnResult(name = "longValueCount", type = Long.class), | 85 | @ColumnResult(name = "longValueCount", type = Long.class), |
94 | @ColumnResult(name = "doubleValueCount", type = Long.class), | 86 | @ColumnResult(name = "doubleValueCount", type = Long.class), |
87 | + @ColumnResult(name = "jsonValueCount", type = Long.class), | ||
95 | } | 88 | } |
96 | ) | 89 | ) |
97 | }), | 90 | }), |
@@ -179,13 +172,15 @@ public final class TimescaleTsKvEntity extends AbstractTsKvEntity implements ToD | @@ -179,13 +172,15 @@ public final class TimescaleTsKvEntity extends AbstractTsKvEntity implements ToD | ||
179 | } | 172 | } |
180 | } | 173 | } |
181 | 174 | ||
182 | - public TimescaleTsKvEntity(Long tsBucket, Long interval, Long booleanValueCount, Long strValueCount, Long longValueCount, Long doubleValueCount) { | ||
183 | - if (!isAllNull(tsBucket, interval, booleanValueCount, strValueCount, longValueCount, doubleValueCount)) { | 175 | + public TimescaleTsKvEntity(Long tsBucket, Long interval, Long booleanValueCount, Long strValueCount, Long longValueCount, Long doubleValueCount, Long jsonValueCount) { |
176 | + if (!isAllNull(tsBucket, interval, booleanValueCount, strValueCount, longValueCount, doubleValueCount, jsonValueCount)) { | ||
184 | this.ts = tsBucket + interval / 2; | 177 | this.ts = tsBucket + interval / 2; |
185 | if (booleanValueCount != 0) { | 178 | if (booleanValueCount != 0) { |
186 | this.longValue = booleanValueCount; | 179 | this.longValue = booleanValueCount; |
187 | } else if (strValueCount != 0) { | 180 | } else if (strValueCount != 0) { |
188 | this.longValue = strValueCount; | 181 | this.longValue = strValueCount; |
182 | + } else if (jsonValueCount != 0) { | ||
183 | + this.longValue = jsonValueCount; | ||
189 | } else { | 184 | } else { |
190 | this.longValue = longValueCount + doubleValueCount; | 185 | this.longValue = longValueCount + doubleValueCount; |
191 | } | 186 | } |
@@ -194,6 +189,6 @@ public final class TimescaleTsKvEntity extends AbstractTsKvEntity implements ToD | @@ -194,6 +189,6 @@ public final class TimescaleTsKvEntity extends AbstractTsKvEntity implements ToD | ||
194 | 189 | ||
195 | @Override | 190 | @Override |
196 | public boolean isNotEmpty() { | 191 | public boolean isNotEmpty() { |
197 | - return ts != null && (strValue != null || longValue != null || doubleValue != null || booleanValue != null); | 192 | + return ts != null && (strValue != null || longValue != null || doubleValue != null || booleanValue != null || jsonValue != null); |
198 | } | 193 | } |
199 | } | 194 | } |
@@ -18,7 +18,6 @@ package org.thingsboard.server.dao.sql.attributes; | @@ -18,7 +18,6 @@ package org.thingsboard.server.dao.sql.attributes; | ||
18 | import lombok.extern.slf4j.Slf4j; | 18 | import lombok.extern.slf4j.Slf4j; |
19 | import org.springframework.beans.factory.annotation.Autowired; | 19 | import org.springframework.beans.factory.annotation.Autowired; |
20 | import org.springframework.beans.factory.annotation.Value; | 20 | import org.springframework.beans.factory.annotation.Value; |
21 | -import org.springframework.data.jpa.repository.Modifying; | ||
22 | import org.springframework.jdbc.core.BatchPreparedStatementSetter; | 21 | import org.springframework.jdbc.core.BatchPreparedStatementSetter; |
23 | import org.springframework.jdbc.core.JdbcTemplate; | 22 | import org.springframework.jdbc.core.JdbcTemplate; |
24 | import org.springframework.stereotype.Repository; | 23 | import org.springframework.stereotype.Repository; |
@@ -28,8 +27,6 @@ import org.springframework.transaction.support.TransactionTemplate; | @@ -28,8 +27,6 @@ import org.springframework.transaction.support.TransactionTemplate; | ||
28 | import org.thingsboard.server.dao.model.sql.AttributeKvEntity; | 27 | import org.thingsboard.server.dao.model.sql.AttributeKvEntity; |
29 | import org.thingsboard.server.dao.util.SqlDao; | 28 | import org.thingsboard.server.dao.util.SqlDao; |
30 | 29 | ||
31 | -import javax.persistence.EntityManager; | ||
32 | -import javax.persistence.PersistenceContext; | ||
33 | import java.sql.PreparedStatement; | 30 | import java.sql.PreparedStatement; |
34 | import java.sql.SQLException; | 31 | import java.sql.SQLException; |
35 | import java.sql.Types; | 32 | import java.sql.Types; |
@@ -45,19 +42,14 @@ public abstract class AttributeKvInsertRepository { | @@ -45,19 +42,14 @@ public abstract class AttributeKvInsertRepository { | ||
45 | private static final ThreadLocal<Pattern> PATTERN_THREAD_LOCAL = ThreadLocal.withInitial(() -> Pattern.compile(String.valueOf(Character.MIN_VALUE))); | 42 | private static final ThreadLocal<Pattern> PATTERN_THREAD_LOCAL = ThreadLocal.withInitial(() -> Pattern.compile(String.valueOf(Character.MIN_VALUE))); |
46 | private static final String EMPTY_STR = ""; | 43 | private static final String EMPTY_STR = ""; |
47 | 44 | ||
48 | - private static final String BATCH_UPDATE = "UPDATE attribute_kv SET str_v = ?, long_v = ?, dbl_v = ?, bool_v = ?, last_update_ts = ? " + | 45 | + private static final String BATCH_UPDATE = "UPDATE attribute_kv SET str_v = ?, long_v = ?, dbl_v = ?, bool_v = ?, json_v = cast(? AS json), last_update_ts = ? " + |
49 | "WHERE entity_type = ? and entity_id = ? and attribute_type =? and attribute_key = ?;"; | 46 | "WHERE entity_type = ? and entity_id = ? and attribute_type =? and attribute_key = ?;"; |
50 | 47 | ||
51 | private static final String INSERT_OR_UPDATE = | 48 | private static final String INSERT_OR_UPDATE = |
52 | - "INSERT INTO attribute_kv (entity_type, entity_id, attribute_type, attribute_key, str_v, long_v, dbl_v, bool_v, last_update_ts) " + | ||
53 | - "VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?) " + | 49 | + "INSERT INTO attribute_kv (entity_type, entity_id, attribute_type, attribute_key, str_v, long_v, dbl_v, bool_v, json_v, last_update_ts) " + |
50 | + "VALUES(?, ?, ?, ?, ?, ?, ?, ?, cast(? AS json), ?) " + | ||
54 | "ON CONFLICT (entity_type, entity_id, attribute_type, attribute_key) " + | 51 | "ON CONFLICT (entity_type, entity_id, attribute_type, attribute_key) " + |
55 | - "DO UPDATE SET str_v = ?, long_v = ?, dbl_v = ?, bool_v = ?, last_update_ts = ?;"; | ||
56 | - | ||
57 | - protected static final String BOOL_V = "bool_v"; | ||
58 | - protected static final String STR_V = "str_v"; | ||
59 | - protected static final String LONG_V = "long_v"; | ||
60 | - protected static final String DBL_V = "dbl_v"; | 52 | + "DO UPDATE SET str_v = ?, long_v = ?, dbl_v = ?, bool_v = ?, json_v = cast(? AS json), last_update_ts = ?;"; |
61 | 53 | ||
62 | @Autowired | 54 | @Autowired |
63 | protected JdbcTemplate jdbcTemplate; | 55 | protected JdbcTemplate jdbcTemplate; |
@@ -68,74 +60,6 @@ public abstract class AttributeKvInsertRepository { | @@ -68,74 +60,6 @@ public abstract class AttributeKvInsertRepository { | ||
68 | @Value("${sql.remove_null_chars}") | 60 | @Value("${sql.remove_null_chars}") |
69 | private boolean removeNullChars; | 61 | private boolean removeNullChars; |
70 | 62 | ||
71 | - @PersistenceContext | ||
72 | - protected EntityManager entityManager; | ||
73 | - | ||
74 | - public abstract void saveOrUpdate(AttributeKvEntity entity); | ||
75 | - | ||
76 | - protected void processSaveOrUpdate(AttributeKvEntity entity, String requestBoolValue, String requestStrValue, String requestLongValue, String requestDblValue) { | ||
77 | - if (entity.getBooleanValue() != null) { | ||
78 | - saveOrUpdateBoolean(entity, requestBoolValue); | ||
79 | - } | ||
80 | - if (entity.getStrValue() != null) { | ||
81 | - saveOrUpdateString(entity, requestStrValue); | ||
82 | - } | ||
83 | - if (entity.getLongValue() != null) { | ||
84 | - saveOrUpdateLong(entity, requestLongValue); | ||
85 | - } | ||
86 | - if (entity.getDoubleValue() != null) { | ||
87 | - saveOrUpdateDouble(entity, requestDblValue); | ||
88 | - } | ||
89 | - } | ||
90 | - | ||
91 | - @Modifying | ||
92 | - private void saveOrUpdateBoolean(AttributeKvEntity entity, String query) { | ||
93 | - entityManager.createNativeQuery(query) | ||
94 | - .setParameter("entity_type", entity.getId().getEntityType().name()) | ||
95 | - .setParameter("entity_id", entity.getId().getEntityId()) | ||
96 | - .setParameter("attribute_type", entity.getId().getAttributeType()) | ||
97 | - .setParameter("attribute_key", entity.getId().getAttributeKey()) | ||
98 | - .setParameter("bool_v", entity.getBooleanValue()) | ||
99 | - .setParameter("last_update_ts", entity.getLastUpdateTs()) | ||
100 | - .executeUpdate(); | ||
101 | - } | ||
102 | - | ||
103 | - @Modifying | ||
104 | - private void saveOrUpdateString(AttributeKvEntity entity, String query) { | ||
105 | - entityManager.createNativeQuery(query) | ||
106 | - .setParameter("entity_type", entity.getId().getEntityType().name()) | ||
107 | - .setParameter("entity_id", entity.getId().getEntityId()) | ||
108 | - .setParameter("attribute_type", entity.getId().getAttributeType()) | ||
109 | - .setParameter("attribute_key", entity.getId().getAttributeKey()) | ||
110 | - .setParameter("str_v", replaceNullChars(entity.getStrValue())) | ||
111 | - .setParameter("last_update_ts", entity.getLastUpdateTs()) | ||
112 | - .executeUpdate(); | ||
113 | - } | ||
114 | - | ||
115 | - @Modifying | ||
116 | - private void saveOrUpdateLong(AttributeKvEntity entity, String query) { | ||
117 | - entityManager.createNativeQuery(query) | ||
118 | - .setParameter("entity_type", entity.getId().getEntityType().name()) | ||
119 | - .setParameter("entity_id", entity.getId().getEntityId()) | ||
120 | - .setParameter("attribute_type", entity.getId().getAttributeType()) | ||
121 | - .setParameter("attribute_key", entity.getId().getAttributeKey()) | ||
122 | - .setParameter("long_v", entity.getLongValue()) | ||
123 | - .setParameter("last_update_ts", entity.getLastUpdateTs()) | ||
124 | - .executeUpdate(); | ||
125 | - } | ||
126 | - | ||
127 | - @Modifying | ||
128 | - private void saveOrUpdateDouble(AttributeKvEntity entity, String query) { | ||
129 | - entityManager.createNativeQuery(query) | ||
130 | - .setParameter("entity_type", entity.getId().getEntityType().name()) | ||
131 | - .setParameter("entity_id", entity.getId().getEntityId()) | ||
132 | - .setParameter("attribute_type", entity.getId().getAttributeType()) | ||
133 | - .setParameter("attribute_key", entity.getId().getAttributeKey()) | ||
134 | - .setParameter("dbl_v", entity.getDoubleValue()) | ||
135 | - .setParameter("last_update_ts", entity.getLastUpdateTs()) | ||
136 | - .executeUpdate(); | ||
137 | - } | ||
138 | - | ||
139 | protected void saveOrUpdate(List<AttributeKvEntity> entities) { | 63 | protected void saveOrUpdate(List<AttributeKvEntity> entities) { |
140 | transactionTemplate.execute(new TransactionCallbackWithoutResult() { | 64 | transactionTemplate.execute(new TransactionCallbackWithoutResult() { |
141 | @Override | 65 | @Override |
@@ -164,11 +88,13 @@ public abstract class AttributeKvInsertRepository { | @@ -164,11 +88,13 @@ public abstract class AttributeKvInsertRepository { | ||
164 | ps.setNull(4, Types.BOOLEAN); | 88 | ps.setNull(4, Types.BOOLEAN); |
165 | } | 89 | } |
166 | 90 | ||
167 | - ps.setLong(5, kvEntity.getLastUpdateTs()); | ||
168 | - ps.setString(6, kvEntity.getId().getEntityType().name()); | ||
169 | - ps.setString(7, kvEntity.getId().getEntityId()); | ||
170 | - ps.setString(8, kvEntity.getId().getAttributeType()); | ||
171 | - ps.setString(9, kvEntity.getId().getAttributeKey()); | 91 | + ps.setString(5, replaceNullChars(kvEntity.getJsonValue())); |
92 | + | ||
93 | + ps.setLong(6, kvEntity.getLastUpdateTs()); | ||
94 | + ps.setString(7, kvEntity.getId().getEntityType().name()); | ||
95 | + ps.setString(8, kvEntity.getId().getEntityId()); | ||
96 | + ps.setString(9, kvEntity.getId().getAttributeType()); | ||
97 | + ps.setString(10, kvEntity.getId().getAttributeKey()); | ||
172 | } | 98 | } |
173 | 99 | ||
174 | @Override | 100 | @Override |
@@ -199,35 +125,39 @@ public abstract class AttributeKvInsertRepository { | @@ -199,35 +125,39 @@ public abstract class AttributeKvInsertRepository { | ||
199 | ps.setString(2, kvEntity.getId().getEntityId()); | 125 | ps.setString(2, kvEntity.getId().getEntityId()); |
200 | ps.setString(3, kvEntity.getId().getAttributeType()); | 126 | ps.setString(3, kvEntity.getId().getAttributeType()); |
201 | ps.setString(4, kvEntity.getId().getAttributeKey()); | 127 | ps.setString(4, kvEntity.getId().getAttributeKey()); |
128 | + | ||
202 | ps.setString(5, replaceNullChars(kvEntity.getStrValue())); | 129 | ps.setString(5, replaceNullChars(kvEntity.getStrValue())); |
203 | - ps.setString(10, replaceNullChars(kvEntity.getStrValue())); | 130 | + ps.setString(11, replaceNullChars(kvEntity.getStrValue())); |
204 | 131 | ||
205 | if (kvEntity.getLongValue() != null) { | 132 | if (kvEntity.getLongValue() != null) { |
206 | ps.setLong(6, kvEntity.getLongValue()); | 133 | ps.setLong(6, kvEntity.getLongValue()); |
207 | - ps.setLong(11, kvEntity.getLongValue()); | 134 | + ps.setLong(12, kvEntity.getLongValue()); |
208 | } else { | 135 | } else { |
209 | ps.setNull(6, Types.BIGINT); | 136 | ps.setNull(6, Types.BIGINT); |
210 | - ps.setNull(11, Types.BIGINT); | 137 | + ps.setNull(12, Types.BIGINT); |
211 | } | 138 | } |
212 | 139 | ||
213 | if (kvEntity.getDoubleValue() != null) { | 140 | if (kvEntity.getDoubleValue() != null) { |
214 | ps.setDouble(7, kvEntity.getDoubleValue()); | 141 | ps.setDouble(7, kvEntity.getDoubleValue()); |
215 | - ps.setDouble(12, kvEntity.getDoubleValue()); | 142 | + ps.setDouble(13, kvEntity.getDoubleValue()); |
216 | } else { | 143 | } else { |
217 | ps.setNull(7, Types.DOUBLE); | 144 | ps.setNull(7, Types.DOUBLE); |
218 | - ps.setNull(12, Types.DOUBLE); | 145 | + ps.setNull(13, Types.DOUBLE); |
219 | } | 146 | } |
220 | 147 | ||
221 | if (kvEntity.getBooleanValue() != null) { | 148 | if (kvEntity.getBooleanValue() != null) { |
222 | ps.setBoolean(8, kvEntity.getBooleanValue()); | 149 | ps.setBoolean(8, kvEntity.getBooleanValue()); |
223 | - ps.setBoolean(13, kvEntity.getBooleanValue()); | 150 | + ps.setBoolean(14, kvEntity.getBooleanValue()); |
224 | } else { | 151 | } else { |
225 | ps.setNull(8, Types.BOOLEAN); | 152 | ps.setNull(8, Types.BOOLEAN); |
226 | - ps.setNull(13, Types.BOOLEAN); | 153 | + ps.setNull(14, Types.BOOLEAN); |
227 | } | 154 | } |
228 | 155 | ||
229 | - ps.setLong(9, kvEntity.getLastUpdateTs()); | ||
230 | - ps.setLong(14, kvEntity.getLastUpdateTs()); | 156 | + ps.setString(9, replaceNullChars(kvEntity.getJsonValue())); |
157 | + ps.setString(15, replaceNullChars(kvEntity.getJsonValue())); | ||
158 | + | ||
159 | + ps.setLong(10, kvEntity.getLastUpdateTs()); | ||
160 | + ps.setLong(16, kvEntity.getLastUpdateTs()); | ||
231 | } | 161 | } |
232 | 162 | ||
233 | @Override | 163 | @Override |
@@ -30,36 +30,16 @@ import java.util.List; | @@ -30,36 +30,16 @@ import java.util.List; | ||
30 | @Transactional | 30 | @Transactional |
31 | public class HsqlAttributesInsertRepository extends AttributeKvInsertRepository { | 31 | public class HsqlAttributesInsertRepository extends AttributeKvInsertRepository { |
32 | 32 | ||
33 | - private static final String ON_BOOL_VALUE_UPDATE_SET_NULLS = " attribute_kv.str_v = null, attribute_kv.long_v = null, attribute_kv.dbl_v = null "; | ||
34 | - private static final String ON_STR_VALUE_UPDATE_SET_NULLS = " attribute_kv.bool_v = null, attribute_kv.long_v = null, attribute_kv.dbl_v = null "; | ||
35 | - private static final String ON_LONG_VALUE_UPDATE_SET_NULLS = " attribute_kv.str_v = null, attribute_kv.bool_v = null, attribute_kv.dbl_v = null "; | ||
36 | - private static final String ON_DBL_VALUE_UPDATE_SET_NULLS = " attribute_kv.str_v = null, attribute_kv.long_v = null, attribute_kv.bool_v = null "; | ||
37 | - | ||
38 | - private static final String INSERT_BOOL_STATEMENT = getInsertOrUpdateString(BOOL_V, ON_BOOL_VALUE_UPDATE_SET_NULLS); | ||
39 | - private static final String INSERT_STR_STATEMENT = getInsertOrUpdateString(STR_V, ON_STR_VALUE_UPDATE_SET_NULLS); | ||
40 | - private static final String INSERT_LONG_STATEMENT = getInsertOrUpdateString(LONG_V, ON_LONG_VALUE_UPDATE_SET_NULLS); | ||
41 | - private static final String INSERT_DBL_STATEMENT = getInsertOrUpdateString(DBL_V, ON_DBL_VALUE_UPDATE_SET_NULLS); | ||
42 | - | ||
43 | private static final String INSERT_OR_UPDATE = | 33 | private static final String INSERT_OR_UPDATE = |
44 | - "MERGE INTO attribute_kv USING(VALUES ?, ?, ?, ?, ?, ?, ?, ?, ?) " + | ||
45 | - "A (entity_type, entity_id, attribute_type, attribute_key, str_v, long_v, dbl_v, bool_v, last_update_ts) " + | 34 | + "MERGE INTO attribute_kv USING(VALUES ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) " + |
35 | + "A (entity_type, entity_id, attribute_type, attribute_key, str_v, long_v, dbl_v, bool_v, json_v, last_update_ts) " + | ||
46 | "ON (attribute_kv.entity_type=A.entity_type " + | 36 | "ON (attribute_kv.entity_type=A.entity_type " + |
47 | "AND attribute_kv.entity_id=A.entity_id " + | 37 | "AND attribute_kv.entity_id=A.entity_id " + |
48 | "AND attribute_kv.attribute_type=A.attribute_type " + | 38 | "AND attribute_kv.attribute_type=A.attribute_type " + |
49 | "AND attribute_kv.attribute_key=A.attribute_key) " + | 39 | "AND attribute_kv.attribute_key=A.attribute_key) " + |
50 | - "WHEN MATCHED THEN UPDATE SET attribute_kv.str_v = A.str_v, attribute_kv.long_v = A.long_v, attribute_kv.dbl_v = A.dbl_v, attribute_kv.bool_v = A.bool_v, attribute_kv.last_update_ts = A.last_update_ts " + | ||
51 | - "WHEN NOT MATCHED THEN INSERT (entity_type, entity_id, attribute_type, attribute_key, str_v, long_v, dbl_v, bool_v, last_update_ts) " + | ||
52 | - "VALUES (A.entity_type, A.entity_id, A.attribute_type, A.attribute_key, A.str_v, A.long_v, A.dbl_v, A.bool_v, A.last_update_ts)"; | ||
53 | - | ||
54 | - @Override | ||
55 | - public void saveOrUpdate(AttributeKvEntity entity) { | ||
56 | - processSaveOrUpdate(entity, INSERT_BOOL_STATEMENT, INSERT_STR_STATEMENT, INSERT_LONG_STATEMENT, INSERT_DBL_STATEMENT); | ||
57 | - } | ||
58 | - | ||
59 | - private static String getInsertOrUpdateString(String value, String nullValues) { | ||
60 | - return "MERGE INTO attribute_kv USING(VALUES :entity_type, :entity_id, :attribute_type, :attribute_key, :" + value + ", :last_update_ts) A (entity_type, entity_id, attribute_type, attribute_key, " + value + ", last_update_ts) ON (attribute_kv.entity_type=A.entity_type AND attribute_kv.entity_id=A.entity_id AND attribute_kv.attribute_type=A.attribute_type AND attribute_kv.attribute_key=A.attribute_key) WHEN MATCHED THEN UPDATE SET attribute_kv." + value + " = A." + value + ", attribute_kv.last_update_ts = A.last_update_ts," + nullValues + "WHEN NOT MATCHED THEN INSERT (entity_type, entity_id, attribute_type, attribute_key, " + value + ", last_update_ts) VALUES (A.entity_type, A.entity_id, A.attribute_type, A.attribute_key, A." + value + ", A.last_update_ts)"; | ||
61 | - } | ||
62 | - | 40 | + "WHEN MATCHED THEN UPDATE SET attribute_kv.str_v = A.str_v, attribute_kv.long_v = A.long_v, attribute_kv.dbl_v = A.dbl_v, attribute_kv.bool_v = A.bool_v, attribute_kv.json_v = A.json_v, attribute_kv.last_update_ts = A.last_update_ts " + |
41 | + "WHEN NOT MATCHED THEN INSERT (entity_type, entity_id, attribute_type, attribute_key, str_v, long_v, dbl_v, bool_v, json_v, last_update_ts) " + | ||
42 | + "VALUES (A.entity_type, A.entity_id, A.attribute_type, A.attribute_key, A.str_v, A.long_v, A.dbl_v, A.bool_v, A.json_v, A.last_update_ts)"; | ||
63 | 43 | ||
64 | @Override | 44 | @Override |
65 | protected void saveOrUpdate(List<AttributeKvEntity> entities) { | 45 | protected void saveOrUpdate(List<AttributeKvEntity> entities) { |
@@ -89,7 +69,9 @@ public class HsqlAttributesInsertRepository extends AttributeKvInsertRepository | @@ -89,7 +69,9 @@ public class HsqlAttributesInsertRepository extends AttributeKvInsertRepository | ||
89 | ps.setNull(8, Types.BOOLEAN); | 69 | ps.setNull(8, Types.BOOLEAN); |
90 | } | 70 | } |
91 | 71 | ||
92 | - ps.setLong(9, entity.getLastUpdateTs()); | 72 | + ps.setString(9, entity.getJsonValue()); |
73 | + | ||
74 | + ps.setLong(10, entity.getLastUpdateTs()); | ||
93 | }); | 75 | }); |
94 | }); | 76 | }); |
95 | } | 77 | } |
@@ -128,6 +128,7 @@ public class JpaAttributeDao extends JpaAbstractDaoListeningExecutorService impl | @@ -128,6 +128,7 @@ public class JpaAttributeDao extends JpaAbstractDaoListeningExecutorService impl | ||
128 | entity.setDoubleValue(attribute.getDoubleValue().orElse(null)); | 128 | entity.setDoubleValue(attribute.getDoubleValue().orElse(null)); |
129 | entity.setLongValue(attribute.getLongValue().orElse(null)); | 129 | entity.setLongValue(attribute.getLongValue().orElse(null)); |
130 | entity.setBooleanValue(attribute.getBooleanValue().orElse(null)); | 130 | entity.setBooleanValue(attribute.getBooleanValue().orElse(null)); |
131 | + entity.setJsonValue(attribute.getJsonValue().orElse(null)); | ||
131 | return addToQueue(entity); | 132 | return addToQueue(entity); |
132 | } | 133 | } |
133 | 134 |
@@ -17,7 +17,6 @@ package org.thingsboard.server.dao.sql.attributes; | @@ -17,7 +17,6 @@ package org.thingsboard.server.dao.sql.attributes; | ||
17 | 17 | ||
18 | import org.springframework.stereotype.Repository; | 18 | import org.springframework.stereotype.Repository; |
19 | import org.springframework.transaction.annotation.Transactional; | 19 | import org.springframework.transaction.annotation.Transactional; |
20 | -import org.thingsboard.server.dao.model.sql.AttributeKvEntity; | ||
21 | import org.thingsboard.server.dao.util.PsqlDao; | 20 | import org.thingsboard.server.dao.util.PsqlDao; |
22 | import org.thingsboard.server.dao.util.SqlDao; | 21 | import org.thingsboard.server.dao.util.SqlDao; |
23 | 22 | ||
@@ -27,22 +26,4 @@ import org.thingsboard.server.dao.util.SqlDao; | @@ -27,22 +26,4 @@ import org.thingsboard.server.dao.util.SqlDao; | ||
27 | @Transactional | 26 | @Transactional |
28 | public class PsqlAttributesInsertRepository extends AttributeKvInsertRepository { | 27 | public class PsqlAttributesInsertRepository extends AttributeKvInsertRepository { |
29 | 28 | ||
30 | - private static final String ON_BOOL_VALUE_UPDATE_SET_NULLS = "str_v = null, long_v = null, dbl_v = null"; | ||
31 | - private static final String ON_STR_VALUE_UPDATE_SET_NULLS = "bool_v = null, long_v = null, dbl_v = null"; | ||
32 | - private static final String ON_LONG_VALUE_UPDATE_SET_NULLS = "str_v = null, bool_v = null, dbl_v = null"; | ||
33 | - private static final String ON_DBL_VALUE_UPDATE_SET_NULLS = "str_v = null, long_v = null, bool_v = null"; | ||
34 | - | ||
35 | - private static final String INSERT_OR_UPDATE_BOOL_STATEMENT = getInsertOrUpdateString(BOOL_V, ON_BOOL_VALUE_UPDATE_SET_NULLS); | ||
36 | - private static final String INSERT_OR_UPDATE_STR_STATEMENT = getInsertOrUpdateString(STR_V, ON_STR_VALUE_UPDATE_SET_NULLS); | ||
37 | - private static final String INSERT_OR_UPDATE_LONG_STATEMENT = getInsertOrUpdateString(LONG_V , ON_LONG_VALUE_UPDATE_SET_NULLS); | ||
38 | - private static final String INSERT_OR_UPDATE_DBL_STATEMENT = getInsertOrUpdateString(DBL_V, ON_DBL_VALUE_UPDATE_SET_NULLS); | ||
39 | - | ||
40 | - @Override | ||
41 | - public void saveOrUpdate(AttributeKvEntity entity) { | ||
42 | - processSaveOrUpdate(entity, INSERT_OR_UPDATE_BOOL_STATEMENT, INSERT_OR_UPDATE_STR_STATEMENT, INSERT_OR_UPDATE_LONG_STATEMENT, INSERT_OR_UPDATE_DBL_STATEMENT); | ||
43 | - } | ||
44 | - | ||
45 | - private static String getInsertOrUpdateString(String value, String nullValues) { | ||
46 | - return "INSERT INTO attribute_kv (entity_type, entity_id, attribute_type, attribute_key, " + value + ", last_update_ts) VALUES (:entity_type, :entity_id, :attribute_type, :attribute_key, :" + value + ", :last_update_ts) ON CONFLICT (entity_type, entity_id, attribute_type, attribute_key) DO UPDATE SET " + value + " = :" + value + ", last_update_ts = :last_update_ts," + nullValues; | ||
47 | - } | ||
48 | } | 29 | } |
@@ -253,6 +253,8 @@ public abstract class AbstractSqlTimeseriesDao extends JpaAbstractDaoListeningEx | @@ -253,6 +253,8 @@ public abstract class AbstractSqlTimeseriesDao extends JpaAbstractDaoListeningEx | ||
253 | latestEntity.setDoubleValue(tsKvEntry.getDoubleValue().orElse(null)); | 253 | latestEntity.setDoubleValue(tsKvEntry.getDoubleValue().orElse(null)); |
254 | latestEntity.setLongValue(tsKvEntry.getLongValue().orElse(null)); | 254 | latestEntity.setLongValue(tsKvEntry.getLongValue().orElse(null)); |
255 | latestEntity.setBooleanValue(tsKvEntry.getBooleanValue().orElse(null)); | 255 | latestEntity.setBooleanValue(tsKvEntry.getBooleanValue().orElse(null)); |
256 | + latestEntity.setJsonValue(tsKvEntry.getJsonValue().orElse(null)); | ||
257 | + | ||
256 | return tsLatestQueue.add(latestEntity); | 258 | return tsLatestQueue.add(latestEntity); |
257 | } | 259 | } |
258 | 260 |
@@ -37,14 +37,14 @@ import java.util.List; | @@ -37,14 +37,14 @@ import java.util.List; | ||
37 | public class HsqlInsertTsRepository extends AbstractInsertRepository implements InsertTsRepository<TsKvEntity> { | 37 | public class HsqlInsertTsRepository extends AbstractInsertRepository implements InsertTsRepository<TsKvEntity> { |
38 | 38 | ||
39 | private static final String INSERT_OR_UPDATE = | 39 | private static final String INSERT_OR_UPDATE = |
40 | - "MERGE INTO ts_kv USING(VALUES ?, ?, ?, ?, ?, ?, ?) " + | ||
41 | - "T (entity_id, key, ts, bool_v, str_v, long_v, dbl_v) " + | 40 | + "MERGE INTO ts_kv USING(VALUES ?, ?, ?, ?, ?, ?, ?, ?) " + |
41 | + "T (entity_id, key, ts, bool_v, str_v, long_v, dbl_v, json_v) " + | ||
42 | "ON (ts_kv.entity_id=T.entity_id " + | 42 | "ON (ts_kv.entity_id=T.entity_id " + |
43 | "AND ts_kv.key=T.key " + | 43 | "AND ts_kv.key=T.key " + |
44 | "AND ts_kv.ts=T.ts) " + | 44 | "AND ts_kv.ts=T.ts) " + |
45 | - "WHEN MATCHED THEN UPDATE SET ts_kv.bool_v = T.bool_v, ts_kv.str_v = T.str_v, ts_kv.long_v = T.long_v, ts_kv.dbl_v = T.dbl_v " + | ||
46 | - "WHEN NOT MATCHED THEN INSERT (entity_id, key, ts, bool_v, str_v, long_v, dbl_v) " + | ||
47 | - "VALUES (T.entity_id, T.key, T.ts, T.bool_v, T.str_v, T.long_v, T.dbl_v);"; | 45 | + "WHEN MATCHED THEN UPDATE SET ts_kv.bool_v = T.bool_v, ts_kv.str_v = T.str_v, ts_kv.long_v = T.long_v, ts_kv.dbl_v = T.dbl_v ,ts_kv.json_v = T.json_v " + |
46 | + "WHEN NOT MATCHED THEN INSERT (entity_id, key, ts, bool_v, str_v, long_v, dbl_v, json_v) " + | ||
47 | + "VALUES (T.entity_id, T.key, T.ts, T.bool_v, T.str_v, T.long_v, T.dbl_v, T.json_v);"; | ||
48 | 48 | ||
49 | @Override | 49 | @Override |
50 | public void saveOrUpdate(List<EntityContainer<TsKvEntity>> entities) { | 50 | public void saveOrUpdate(List<EntityContainer<TsKvEntity>> entities) { |
@@ -76,6 +76,8 @@ public class HsqlInsertTsRepository extends AbstractInsertRepository implements | @@ -76,6 +76,8 @@ public class HsqlInsertTsRepository extends AbstractInsertRepository implements | ||
76 | } else { | 76 | } else { |
77 | ps.setNull(7, Types.DOUBLE); | 77 | ps.setNull(7, Types.DOUBLE); |
78 | } | 78 | } |
79 | + | ||
80 | + ps.setString(8, tsKvEntity.getJsonValue()); | ||
79 | } | 81 | } |
80 | 82 | ||
81 | @Override | 83 | @Override |
@@ -97,7 +97,8 @@ public interface TsKvHsqlRepository extends CrudRepository<TsKvEntity, TsKvCompo | @@ -97,7 +97,8 @@ public interface TsKvHsqlRepository extends CrudRepository<TsKvEntity, TsKvCompo | ||
97 | @Query("SELECT new TsKvEntity(SUM(CASE WHEN tskv.booleanValue IS NULL THEN 0 ELSE 1 END), " + | 97 | @Query("SELECT new TsKvEntity(SUM(CASE WHEN tskv.booleanValue IS NULL THEN 0 ELSE 1 END), " + |
98 | "SUM(CASE WHEN tskv.strValue IS NULL THEN 0 ELSE 1 END), " + | 98 | "SUM(CASE WHEN tskv.strValue IS NULL THEN 0 ELSE 1 END), " + |
99 | "SUM(CASE WHEN tskv.longValue IS NULL THEN 0 ELSE 1 END), " + | 99 | "SUM(CASE WHEN tskv.longValue IS NULL THEN 0 ELSE 1 END), " + |
100 | - "SUM(CASE WHEN tskv.doubleValue IS NULL THEN 0 ELSE 1 END)) FROM TsKvEntity tskv " + | 100 | + "SUM(CASE WHEN tskv.doubleValue IS NULL THEN 0 ELSE 1 END), " + |
101 | + "SUM(CASE WHEN tskv.jsonValue IS NULL THEN 0 ELSE 1 END)) FROM TsKvEntity tskv " + | ||
101 | "WHERE tskv.entityId = :entityId AND tskv.key = :entityKey AND tskv.ts > :startTs AND tskv.ts <= :endTs") | 102 | "WHERE tskv.entityId = :entityId AND tskv.key = :entityKey AND tskv.ts > :startTs AND tskv.ts <= :endTs") |
102 | CompletableFuture<TsKvEntity> findCount(@Param("entityId") UUID entityId, | 103 | CompletableFuture<TsKvEntity> findCount(@Param("entityId") UUID entityId, |
103 | @Param("entityKey") int entityKey, | 104 | @Param("entityKey") int entityKey, |
@@ -36,11 +36,11 @@ import java.util.List; | @@ -36,11 +36,11 @@ import java.util.List; | ||
36 | public class HsqlLatestInsertTsRepository extends AbstractInsertRepository implements InsertLatestTsRepository { | 36 | public class HsqlLatestInsertTsRepository extends AbstractInsertRepository implements InsertLatestTsRepository { |
37 | 37 | ||
38 | private static final String INSERT_OR_UPDATE = | 38 | private static final String INSERT_OR_UPDATE = |
39 | - "MERGE INTO ts_kv_latest USING(VALUES ?, ?, ?, ?, ?, ?, ?) " + | ||
40 | - "T (entity_id, key, ts, bool_v, str_v, long_v, dbl_v) " + | 39 | + "MERGE INTO ts_kv_latest USING(VALUES ?, ?, ?, ?, ?, ?, ?, ?) " + |
40 | + "T (entity_id, key, ts, bool_v, str_v, long_v, dbl_v, json_v) " + | ||
41 | "ON (ts_kv_latest.entity_id=T.entity_id " + | 41 | "ON (ts_kv_latest.entity_id=T.entity_id " + |
42 | "AND ts_kv_latest.key=T.key) " + | 42 | "AND ts_kv_latest.key=T.key) " + |
43 | - "WHEN MATCHED THEN UPDATE SET ts_kv_latest.ts = T.ts, ts_kv_latest.bool_v = T.bool_v, ts_kv_latest.str_v = T.str_v, ts_kv_latest.long_v = T.long_v, ts_kv_latest.dbl_v = T.dbl_v " + | 43 | + "WHEN MATCHED THEN UPDATE SET ts_kv_latest.ts = T.ts, ts_kv_latest.bool_v = T.bool_v, ts_kv_latest.str_v = T.str_v, ts_kv_latest.long_v = T.long_v, ts_kv_latest.dbl_v = T.dbl_v, ts_kv_latest.json_v = T.json_v " + |
44 | "WHEN NOT MATCHED THEN INSERT (entity_id, key, ts, bool_v, str_v, long_v, dbl_v) " + | 44 | "WHEN NOT MATCHED THEN INSERT (entity_id, key, ts, bool_v, str_v, long_v, dbl_v) " + |
45 | "VALUES (T.entity_id, T.key, T.ts, T.bool_v, T.str_v, T.long_v, T.dbl_v);"; | 45 | "VALUES (T.entity_id, T.key, T.ts, T.bool_v, T.str_v, T.long_v, T.dbl_v);"; |
46 | 46 | ||
@@ -72,6 +72,8 @@ public class HsqlLatestInsertTsRepository extends AbstractInsertRepository imple | @@ -72,6 +72,8 @@ public class HsqlLatestInsertTsRepository extends AbstractInsertRepository imple | ||
72 | } else { | 72 | } else { |
73 | ps.setNull(7, Types.DOUBLE); | 73 | ps.setNull(7, Types.DOUBLE); |
74 | } | 74 | } |
75 | + | ||
76 | + ps.setString(8, entities.get(i).getJsonValue()); | ||
75 | } | 77 | } |
76 | 78 | ||
77 | @Override | 79 | @Override |
@@ -38,12 +38,12 @@ import java.util.List; | @@ -38,12 +38,12 @@ import java.util.List; | ||
38 | public class PsqlLatestInsertTsRepository extends AbstractInsertRepository implements InsertLatestTsRepository { | 38 | public class PsqlLatestInsertTsRepository extends AbstractInsertRepository implements InsertLatestTsRepository { |
39 | 39 | ||
40 | private static final String BATCH_UPDATE = | 40 | private static final String BATCH_UPDATE = |
41 | - "UPDATE ts_kv_latest SET ts = ?, bool_v = ?, str_v = ?, long_v = ?, dbl_v = ? WHERE entity_id = ? and key = ?"; | 41 | + "UPDATE ts_kv_latest SET ts = ?, bool_v = ?, str_v = ?, long_v = ?, dbl_v = ?, json_v = cast(? AS json) WHERE entity_id = ? and key = ?"; |
42 | 42 | ||
43 | 43 | ||
44 | private static final String INSERT_OR_UPDATE = | 44 | private static final String INSERT_OR_UPDATE = |
45 | - "INSERT INTO ts_kv_latest (entity_id, key, ts, bool_v, str_v, long_v, dbl_v) VALUES(?, ?, ?, ?, ?, ?, ?) " + | ||
46 | - "ON CONFLICT (entity_id, key) DO UPDATE SET ts = ?, bool_v = ?, str_v = ?, long_v = ?, dbl_v = ?;"; | 45 | + "INSERT INTO ts_kv_latest (entity_id, key, ts, bool_v, str_v, long_v, dbl_v, json_v) VALUES(?, ?, ?, ?, ?, ?, ?, cast(? AS json)) " + |
46 | + "ON CONFLICT (entity_id, key) DO UPDATE SET ts = ?, bool_v = ?, str_v = ?, long_v = ?, dbl_v = ?, json_v = cast(? AS json);"; | ||
47 | 47 | ||
48 | @Override | 48 | @Override |
49 | public void saveOrUpdate(List<TsKvLatestEntity> entities) { | 49 | public void saveOrUpdate(List<TsKvLatestEntity> entities) { |
@@ -76,8 +76,10 @@ public class PsqlLatestInsertTsRepository extends AbstractInsertRepository imple | @@ -76,8 +76,10 @@ public class PsqlLatestInsertTsRepository extends AbstractInsertRepository imple | ||
76 | ps.setNull(5, Types.DOUBLE); | 76 | ps.setNull(5, Types.DOUBLE); |
77 | } | 77 | } |
78 | 78 | ||
79 | - ps.setObject(6, tsKvLatestEntity.getEntityId()); | ||
80 | - ps.setInt(7, tsKvLatestEntity.getKey()); | 79 | + ps.setString(6, replaceNullChars(tsKvLatestEntity.getJsonValue())); |
80 | + | ||
81 | + ps.setObject(7, tsKvLatestEntity.getEntityId()); | ||
82 | + ps.setInt(8, tsKvLatestEntity.getKey()); | ||
81 | } | 83 | } |
82 | 84 | ||
83 | @Override | 85 | @Override |
@@ -106,36 +108,39 @@ public class PsqlLatestInsertTsRepository extends AbstractInsertRepository imple | @@ -106,36 +108,39 @@ public class PsqlLatestInsertTsRepository extends AbstractInsertRepository imple | ||
106 | TsKvLatestEntity tsKvLatestEntity = insertEntities.get(i); | 108 | TsKvLatestEntity tsKvLatestEntity = insertEntities.get(i); |
107 | ps.setObject(1, tsKvLatestEntity.getEntityId()); | 109 | ps.setObject(1, tsKvLatestEntity.getEntityId()); |
108 | ps.setInt(2, tsKvLatestEntity.getKey()); | 110 | ps.setInt(2, tsKvLatestEntity.getKey()); |
111 | + | ||
109 | ps.setLong(3, tsKvLatestEntity.getTs()); | 112 | ps.setLong(3, tsKvLatestEntity.getTs()); |
110 | - ps.setLong(8, tsKvLatestEntity.getTs()); | 113 | + ps.setLong(9, tsKvLatestEntity.getTs()); |
111 | 114 | ||
112 | if (tsKvLatestEntity.getBooleanValue() != null) { | 115 | if (tsKvLatestEntity.getBooleanValue() != null) { |
113 | ps.setBoolean(4, tsKvLatestEntity.getBooleanValue()); | 116 | ps.setBoolean(4, tsKvLatestEntity.getBooleanValue()); |
114 | - ps.setBoolean(9, tsKvLatestEntity.getBooleanValue()); | 117 | + ps.setBoolean(10, tsKvLatestEntity.getBooleanValue()); |
115 | } else { | 118 | } else { |
116 | ps.setNull(4, Types.BOOLEAN); | 119 | ps.setNull(4, Types.BOOLEAN); |
117 | - ps.setNull(9, Types.BOOLEAN); | 120 | + ps.setNull(10, Types.BOOLEAN); |
118 | } | 121 | } |
119 | 122 | ||
120 | ps.setString(5, replaceNullChars(tsKvLatestEntity.getStrValue())); | 123 | ps.setString(5, replaceNullChars(tsKvLatestEntity.getStrValue())); |
121 | - ps.setString(10, replaceNullChars(tsKvLatestEntity.getStrValue())); | ||
122 | - | 124 | + ps.setString(11, replaceNullChars(tsKvLatestEntity.getStrValue())); |
123 | 125 | ||
124 | if (tsKvLatestEntity.getLongValue() != null) { | 126 | if (tsKvLatestEntity.getLongValue() != null) { |
125 | ps.setLong(6, tsKvLatestEntity.getLongValue()); | 127 | ps.setLong(6, tsKvLatestEntity.getLongValue()); |
126 | - ps.setLong(11, tsKvLatestEntity.getLongValue()); | 128 | + ps.setLong(12, tsKvLatestEntity.getLongValue()); |
127 | } else { | 129 | } else { |
128 | ps.setNull(6, Types.BIGINT); | 130 | ps.setNull(6, Types.BIGINT); |
129 | - ps.setNull(11, Types.BIGINT); | 131 | + ps.setNull(12, Types.BIGINT); |
130 | } | 132 | } |
131 | 133 | ||
132 | if (tsKvLatestEntity.getDoubleValue() != null) { | 134 | if (tsKvLatestEntity.getDoubleValue() != null) { |
133 | ps.setDouble(7, tsKvLatestEntity.getDoubleValue()); | 135 | ps.setDouble(7, tsKvLatestEntity.getDoubleValue()); |
134 | - ps.setDouble(12, tsKvLatestEntity.getDoubleValue()); | 136 | + ps.setDouble(13, tsKvLatestEntity.getDoubleValue()); |
135 | } else { | 137 | } else { |
136 | ps.setNull(7, Types.DOUBLE); | 138 | ps.setNull(7, Types.DOUBLE); |
137 | - ps.setNull(12, Types.DOUBLE); | 139 | + ps.setNull(13, Types.DOUBLE); |
138 | } | 140 | } |
141 | + | ||
142 | + ps.setString(8, replaceNullChars(tsKvLatestEntity.getJsonValue())); | ||
143 | + ps.setString(14, replaceNullChars(tsKvLatestEntity.getJsonValue())); | ||
139 | } | 144 | } |
140 | 145 | ||
141 | @Override | 146 | @Override |
@@ -29,8 +29,9 @@ import java.util.UUID; | @@ -29,8 +29,9 @@ import java.util.UUID; | ||
29 | public class SearchTsKvLatestRepository { | 29 | public class SearchTsKvLatestRepository { |
30 | 30 | ||
31 | public static final String FIND_ALL_BY_ENTITY_ID = "findAllByEntityId"; | 31 | public static final String FIND_ALL_BY_ENTITY_ID = "findAllByEntityId"; |
32 | + | ||
32 | public static final String FIND_ALL_BY_ENTITY_ID_QUERY = "SELECT ts_kv_latest.entity_id AS entityId, ts_kv_latest.key AS key, ts_kv_dictionary.key AS strKey, ts_kv_latest.str_v AS strValue," + | 33 | public static final String FIND_ALL_BY_ENTITY_ID_QUERY = "SELECT ts_kv_latest.entity_id AS entityId, ts_kv_latest.key AS key, ts_kv_dictionary.key AS strKey, ts_kv_latest.str_v AS strValue," + |
33 | - " ts_kv_latest.bool_v AS boolValue, ts_kv_latest.long_v AS longValue, ts_kv_latest.dbl_v AS doubleValue, ts_kv_latest.ts AS ts FROM ts_kv_latest " + | 34 | + " ts_kv_latest.bool_v AS boolValue, ts_kv_latest.long_v AS longValue, ts_kv_latest.dbl_v AS doubleValue, ts_kv_latest.json_v AS jsonValue, ts_kv_latest.ts AS ts FROM ts_kv_latest " + |
34 | "INNER JOIN ts_kv_dictionary ON ts_kv_latest.key = ts_kv_dictionary.key_id WHERE ts_kv_latest.entity_id = cast(:id AS uuid)"; | 35 | "INNER JOIN ts_kv_dictionary ON ts_kv_latest.key = ts_kv_dictionary.key_id WHERE ts_kv_latest.entity_id = cast(:id AS uuid)"; |
35 | 36 | ||
36 | @PersistenceContext | 37 | @PersistenceContext |
@@ -20,11 +20,7 @@ import org.thingsboard.server.dao.model.sqlts.latest.TsKvLatestCompositeKey; | @@ -20,11 +20,7 @@ import org.thingsboard.server.dao.model.sqlts.latest.TsKvLatestCompositeKey; | ||
20 | import org.thingsboard.server.dao.model.sqlts.latest.TsKvLatestEntity; | 20 | import org.thingsboard.server.dao.model.sqlts.latest.TsKvLatestEntity; |
21 | import org.thingsboard.server.dao.util.SqlDao; | 21 | import org.thingsboard.server.dao.util.SqlDao; |
22 | 22 | ||
23 | -import java.util.List; | ||
24 | -import java.util.UUID; | ||
25 | - | ||
26 | @SqlDao | 23 | @SqlDao |
27 | public interface TsKvLatestRepository extends CrudRepository<TsKvLatestEntity, TsKvLatestCompositeKey> { | 24 | public interface TsKvLatestRepository extends CrudRepository<TsKvLatestEntity, TsKvLatestCompositeKey> { |
28 | 25 | ||
29 | - List<TsKvLatestEntity> findAllByEntityId(UUID entityId); | ||
30 | } | 26 | } |
@@ -104,6 +104,7 @@ public class JpaPsqlTimeseriesDao extends AbstractChunkedAggregationTimeseriesDa | @@ -104,6 +104,7 @@ public class JpaPsqlTimeseriesDao extends AbstractChunkedAggregationTimeseriesDa | ||
104 | entity.setDoubleValue(tsKvEntry.getDoubleValue().orElse(null)); | 104 | entity.setDoubleValue(tsKvEntry.getDoubleValue().orElse(null)); |
105 | entity.setLongValue(tsKvEntry.getLongValue().orElse(null)); | 105 | entity.setLongValue(tsKvEntry.getLongValue().orElse(null)); |
106 | entity.setBooleanValue(tsKvEntry.getBooleanValue().orElse(null)); | 106 | entity.setBooleanValue(tsKvEntry.getBooleanValue().orElse(null)); |
107 | + entity.setJsonValue(tsKvEntry.getJsonValue().orElse(null)); | ||
107 | PsqlPartition psqlPartition = toPartition(tsKvEntry.getTs()); | 108 | PsqlPartition psqlPartition = toPartition(tsKvEntry.getTs()); |
108 | log.trace("Saving entity: {}", entity); | 109 | log.trace("Saving entity: {}", entity); |
109 | return tsQueue.add(new EntityContainer(entity, psqlPartition.getPartitionDate())); | 110 | return tsQueue.add(new EntityContainer(entity, psqlPartition.getPartitionDate())); |
@@ -41,8 +41,8 @@ public class PsqlInsertTsRepository extends AbstractInsertRepository implements | @@ -41,8 +41,8 @@ public class PsqlInsertTsRepository extends AbstractInsertRepository implements | ||
41 | 41 | ||
42 | private static final String INSERT_INTO_TS_KV = "INSERT INTO ts_kv_"; | 42 | private static final String INSERT_INTO_TS_KV = "INSERT INTO ts_kv_"; |
43 | 43 | ||
44 | - private static final String VALUES_ON_CONFLICT_DO_UPDATE = " (entity_id, key, ts, bool_v, str_v, long_v, dbl_v) VALUES (?, ?, ?, ?, ?, ?, ?) " + | ||
45 | - "ON CONFLICT (entity_id, key, ts) DO UPDATE SET bool_v = ?, str_v = ?, long_v = ?, dbl_v = ?;"; | 44 | + private static final String VALUES_ON_CONFLICT_DO_UPDATE = " (entity_id, key, ts, bool_v, str_v, long_v, dbl_v, json_v) VALUES (?, ?, ?, ?, ?, ?, ?, cast(? AS json)) " + |
45 | + "ON CONFLICT (entity_id, key, ts) DO UPDATE SET bool_v = ?, str_v = ?, long_v = ?, dbl_v = ?, json_v = cast(? AS json);"; | ||
46 | 46 | ||
47 | @Override | 47 | @Override |
48 | public void saveOrUpdate(List<EntityContainer<TsKvEntity>> entities) { | 48 | public void saveOrUpdate(List<EntityContainer<TsKvEntity>> entities) { |
@@ -61,30 +61,33 @@ public class PsqlInsertTsRepository extends AbstractInsertRepository implements | @@ -61,30 +61,33 @@ public class PsqlInsertTsRepository extends AbstractInsertRepository implements | ||
61 | 61 | ||
62 | if (tsKvEntity.getBooleanValue() != null) { | 62 | if (tsKvEntity.getBooleanValue() != null) { |
63 | ps.setBoolean(4, tsKvEntity.getBooleanValue()); | 63 | ps.setBoolean(4, tsKvEntity.getBooleanValue()); |
64 | - ps.setBoolean(8, tsKvEntity.getBooleanValue()); | 64 | + ps.setBoolean(9, tsKvEntity.getBooleanValue()); |
65 | } else { | 65 | } else { |
66 | ps.setNull(4, Types.BOOLEAN); | 66 | ps.setNull(4, Types.BOOLEAN); |
67 | - ps.setNull(8, Types.BOOLEAN); | 67 | + ps.setNull(9, Types.BOOLEAN); |
68 | } | 68 | } |
69 | 69 | ||
70 | ps.setString(5, replaceNullChars(tsKvEntity.getStrValue())); | 70 | ps.setString(5, replaceNullChars(tsKvEntity.getStrValue())); |
71 | - ps.setString(9, replaceNullChars(tsKvEntity.getStrValue())); | 71 | + ps.setString(10, replaceNullChars(tsKvEntity.getStrValue())); |
72 | 72 | ||
73 | 73 | ||
74 | if (tsKvEntity.getLongValue() != null) { | 74 | if (tsKvEntity.getLongValue() != null) { |
75 | ps.setLong(6, tsKvEntity.getLongValue()); | 75 | ps.setLong(6, tsKvEntity.getLongValue()); |
76 | - ps.setLong(10, tsKvEntity.getLongValue()); | 76 | + ps.setLong(11, tsKvEntity.getLongValue()); |
77 | } else { | 77 | } else { |
78 | ps.setNull(6, Types.BIGINT); | 78 | ps.setNull(6, Types.BIGINT); |
79 | - ps.setNull(10, Types.BIGINT); | 79 | + ps.setNull(11, Types.BIGINT); |
80 | } | 80 | } |
81 | 81 | ||
82 | if (tsKvEntity.getDoubleValue() != null) { | 82 | if (tsKvEntity.getDoubleValue() != null) { |
83 | ps.setDouble(7, tsKvEntity.getDoubleValue()); | 83 | ps.setDouble(7, tsKvEntity.getDoubleValue()); |
84 | - ps.setDouble(11, tsKvEntity.getDoubleValue()); | 84 | + ps.setDouble(12, tsKvEntity.getDoubleValue()); |
85 | } else { | 85 | } else { |
86 | ps.setNull(7, Types.DOUBLE); | 86 | ps.setNull(7, Types.DOUBLE); |
87 | - ps.setNull(11, Types.DOUBLE); | 87 | + ps.setNull(12, Types.DOUBLE); |
88 | + | ||
89 | + ps.setString(8, replaceNullChars(tsKvEntity.getJsonValue())); | ||
90 | + ps.setString(13, replaceNullChars(tsKvEntity.getJsonValue())); | ||
88 | } | 91 | } |
89 | } | 92 | } |
90 | 93 |
@@ -98,7 +98,8 @@ public interface TsKvPsqlRepository extends CrudRepository<TsKvEntity, TsKvCompo | @@ -98,7 +98,8 @@ public interface TsKvPsqlRepository extends CrudRepository<TsKvEntity, TsKvCompo | ||
98 | @Query("SELECT new TsKvEntity(SUM(CASE WHEN tskv.booleanValue IS NULL THEN 0 ELSE 1 END), " + | 98 | @Query("SELECT new TsKvEntity(SUM(CASE WHEN tskv.booleanValue IS NULL THEN 0 ELSE 1 END), " + |
99 | "SUM(CASE WHEN tskv.strValue IS NULL THEN 0 ELSE 1 END), " + | 99 | "SUM(CASE WHEN tskv.strValue IS NULL THEN 0 ELSE 1 END), " + |
100 | "SUM(CASE WHEN tskv.longValue IS NULL THEN 0 ELSE 1 END), " + | 100 | "SUM(CASE WHEN tskv.longValue IS NULL THEN 0 ELSE 1 END), " + |
101 | - "SUM(CASE WHEN tskv.doubleValue IS NULL THEN 0 ELSE 1 END)) FROM TsKvEntity tskv " + | 101 | + "SUM(CASE WHEN tskv.doubleValue IS NULL THEN 0 ELSE 1 END), " + |
102 | + "SUM(CASE WHEN tskv.jsonValue IS NULL THEN 0 ELSE 1 END)) FROM TsKvEntity tskv " + | ||
102 | "WHERE tskv.entityId = :entityId AND tskv.key = :entityKey AND tskv.ts > :startTs AND tskv.ts <= :endTs") | 103 | "WHERE tskv.entityId = :entityId AND tskv.key = :entityKey AND tskv.ts > :startTs AND tskv.ts <= :endTs") |
103 | CompletableFuture<TsKvEntity> findCount(@Param("entityId") UUID entityId, | 104 | CompletableFuture<TsKvEntity> findCount(@Param("entityId") UUID entityId, |
104 | @Param("entityKey") int entityKey, | 105 | @Param("entityKey") int entityKey, |
@@ -36,18 +36,17 @@ public class AggregationRepository { | @@ -36,18 +36,17 @@ public class AggregationRepository { | ||
36 | public static final String FIND_SUM = "findSum"; | 36 | public static final String FIND_SUM = "findSum"; |
37 | public static final String FIND_COUNT = "findCount"; | 37 | public static final String FIND_COUNT = "findCount"; |
38 | 38 | ||
39 | - | ||
40 | public static final String FROM_WHERE_CLAUSE = "FROM tenant_ts_kv tskv WHERE tskv.tenant_id = cast(:tenantId AS uuid) AND tskv.entity_id = cast(:entityId AS uuid) AND tskv.key= cast(:entityKey AS int) AND tskv.ts > :startTs AND tskv.ts <= :endTs GROUP BY tskv.tenant_id, tskv.entity_id, tskv.key, tsBucket ORDER BY tskv.tenant_id, tskv.entity_id, tskv.key, tsBucket"; | 39 | public static final String FROM_WHERE_CLAUSE = "FROM tenant_ts_kv tskv WHERE tskv.tenant_id = cast(:tenantId AS uuid) AND tskv.entity_id = cast(:entityId AS uuid) AND tskv.key= cast(:entityKey AS int) AND tskv.ts > :startTs AND tskv.ts <= :endTs GROUP BY tskv.tenant_id, tskv.entity_id, tskv.key, tsBucket ORDER BY tskv.tenant_id, tskv.entity_id, tskv.key, tsBucket"; |
41 | 40 | ||
42 | public static final String FIND_AVG_QUERY = "SELECT time_bucket(:timeBucket, tskv.ts) AS tsBucket, :timeBucket AS interval, SUM(COALESCE(tskv.long_v, 0)) AS longValue, SUM(COALESCE(tskv.dbl_v, 0.0)) AS doubleValue, SUM(CASE WHEN tskv.long_v IS NULL THEN 0 ELSE 1 END) AS longCountValue, SUM(CASE WHEN tskv.dbl_v IS NULL THEN 0 ELSE 1 END) AS doubleCountValue, null AS strValue, 'AVG' AS aggType "; | 41 | public static final String FIND_AVG_QUERY = "SELECT time_bucket(:timeBucket, tskv.ts) AS tsBucket, :timeBucket AS interval, SUM(COALESCE(tskv.long_v, 0)) AS longValue, SUM(COALESCE(tskv.dbl_v, 0.0)) AS doubleValue, SUM(CASE WHEN tskv.long_v IS NULL THEN 0 ELSE 1 END) AS longCountValue, SUM(CASE WHEN tskv.dbl_v IS NULL THEN 0 ELSE 1 END) AS doubleCountValue, null AS strValue, 'AVG' AS aggType "; |
43 | 42 | ||
44 | - public static final String FIND_MAX_QUERY = "SELECT time_bucket(:timeBucket, tskv.ts) AS tsBucket, :timeBucket AS interval, MAX(COALESCE(tskv.long_v, -9223372036854775807)) AS longValue, MAX(COALESCE(tskv.dbl_v, -1.79769E+308)) as doubleValue, SUM(CASE WHEN tskv.long_v IS NULL THEN 0 ELSE 1 END) AS longCountValue, SUM(CASE WHEN tskv.dbl_v IS NULL THEN 0 ELSE 1 END) AS doubleCountValue, MAX(tskv.str_v) AS strValue, 'MAX' AS aggType "; | 43 | + public static final String FIND_MAX_QUERY = "SELECT time_bucket(:timeBucket, tskv.ts) AS tsBucket, :timeBucket AS interval, MAX(COALESCE(tskv.long_v, -9223372036854775807)) AS longValue, MAX(COALESCE(tskv.dbl_v, -1.79769E+308)) as doubleValue, SUM(CASE WHEN tskv.long_v IS NULL THEN 0 ELSE 1 END) AS longCountValue, SUM(CASE WHEN tskv.dbl_v IS NULL THEN 0 ELSE 1 END) AS doubleCountValue, MAX(tskv.str_v) AS strValue, MAX(tskv.json_v) AS jsonValue, 'MAX' AS aggType "; |
45 | 44 | ||
46 | - public static final String FIND_MIN_QUERY = "SELECT time_bucket(:timeBucket, tskv.ts) AS tsBucket, :timeBucket AS interval, MIN(COALESCE(tskv.long_v, 9223372036854775807)) AS longValue, MIN(COALESCE(tskv.dbl_v, 1.79769E+308)) as doubleValue, SUM(CASE WHEN tskv.long_v IS NULL THEN 0 ELSE 1 END) AS longCountValue, SUM(CASE WHEN tskv.dbl_v IS NULL THEN 0 ELSE 1 END) AS doubleCountValue, MIN(tskv.str_v) AS strValue, 'MIN' AS aggType "; | 45 | + public static final String FIND_MIN_QUERY = "SELECT time_bucket(:timeBucket, tskv.ts) AS tsBucket, :timeBucket AS interval, MIN(COALESCE(tskv.long_v, 9223372036854775807)) AS longValue, MIN(COALESCE(tskv.dbl_v, 1.79769E+308)) as doubleValue, SUM(CASE WHEN tskv.long_v IS NULL THEN 0 ELSE 1 END) AS longCountValue, SUM(CASE WHEN tskv.dbl_v IS NULL THEN 0 ELSE 1 END) AS doubleCountValue, MIN(tskv.str_v) AS strValue, MIN(tskv.json_v) AS jsonValue,'MIN' AS aggType "; |
47 | 46 | ||
48 | - public static final String FIND_SUM_QUERY = "SELECT time_bucket(:timeBucket, tskv.ts) AS tsBucket, :timeBucket AS interval, SUM(COALESCE(tskv.long_v, 0)) AS longValue, SUM(COALESCE(tskv.dbl_v, 0.0)) AS doubleValue, SUM(CASE WHEN tskv.long_v IS NULL THEN 0 ELSE 1 END) AS longCountValue, SUM(CASE WHEN tskv.dbl_v IS NULL THEN 0 ELSE 1 END) AS doubleCountValue, null AS strValue, 'SUM' AS aggType "; | 47 | + public static final String FIND_SUM_QUERY = "SELECT time_bucket(:timeBucket, tskv.ts) AS tsBucket, :timeBucket AS interval, SUM(COALESCE(tskv.long_v, 0)) AS longValue, SUM(COALESCE(tskv.dbl_v, 0.0)) AS doubleValue, SUM(CASE WHEN tskv.long_v IS NULL THEN 0 ELSE 1 END) AS longCountValue, SUM(CASE WHEN tskv.dbl_v IS NULL THEN 0 ELSE 1 END) AS doubleCountValue, null AS strValue, null AS jsonValue, 'SUM' AS aggType "; |
49 | 48 | ||
50 | - public static final String FIND_COUNT_QUERY = "SELECT time_bucket(:timeBucket, tskv.ts) AS tsBucket, :timeBucket AS interval, SUM(CASE WHEN tskv.bool_v IS NULL THEN 0 ELSE 1 END) AS booleanValueCount, SUM(CASE WHEN tskv.str_v IS NULL THEN 0 ELSE 1 END) AS strValueCount, SUM(CASE WHEN tskv.long_v IS NULL THEN 0 ELSE 1 END) AS longValueCount, SUM(CASE WHEN tskv.dbl_v IS NULL THEN 0 ELSE 1 END) AS doubleValueCount "; | 49 | + public static final String FIND_COUNT_QUERY = "SELECT time_bucket(:timeBucket, tskv.ts) AS tsBucket, :timeBucket AS interval, SUM(CASE WHEN tskv.bool_v IS NULL THEN 0 ELSE 1 END) AS booleanValueCount, SUM(CASE WHEN tskv.str_v IS NULL THEN 0 ELSE 1 END) AS strValueCount, SUM(CASE WHEN tskv.long_v IS NULL THEN 0 ELSE 1 END) AS longValueCount, SUM(CASE WHEN tskv.dbl_v IS NULL THEN 0 ELSE 1 END) AS doubleValueCount, SUM(CASE WHEN tskv.json_v IS NULL THEN 0 ELSE 1 END) AS jsonValueCount "; |
51 | 50 | ||
52 | @PersistenceContext | 51 | @PersistenceContext |
53 | private EntityManager entityManager; | 52 | private EntityManager entityManager; |
@@ -37,8 +37,8 @@ import java.util.List; | @@ -37,8 +37,8 @@ import java.util.List; | ||
37 | public class TimescaleInsertTsRepository extends AbstractInsertRepository implements InsertTsRepository<TimescaleTsKvEntity> { | 37 | public class TimescaleInsertTsRepository extends AbstractInsertRepository implements InsertTsRepository<TimescaleTsKvEntity> { |
38 | 38 | ||
39 | private static final String INSERT_OR_UPDATE = | 39 | private static final String INSERT_OR_UPDATE = |
40 | - "INSERT INTO tenant_ts_kv (tenant_id, entity_id, key, ts, bool_v, str_v, long_v, dbl_v) VALUES(?, ?, ?, ?, ?, ?, ?, ?) " + | ||
41 | - "ON CONFLICT (tenant_id, entity_id, key, ts) DO UPDATE SET bool_v = ?, str_v = ?, long_v = ?, dbl_v = ?;"; | 40 | + "INSERT INTO tenant_ts_kv (tenant_id, entity_id, key, ts, bool_v, str_v, long_v, dbl_v, json_v) VALUES(?, ?, ?, ?, ?, ?, ?, ?, cast(? AS json)) " + |
41 | + "ON CONFLICT (tenant_id, entity_id, key, ts) DO UPDATE SET bool_v = ?, str_v = ?, long_v = ?, dbl_v = ?, json_v = cast(? AS json);"; | ||
42 | 42 | ||
43 | @Override | 43 | @Override |
44 | public void saveOrUpdate(List<EntityContainer<TimescaleTsKvEntity>> entities) { | 44 | public void saveOrUpdate(List<EntityContainer<TimescaleTsKvEntity>> entities) { |
@@ -56,28 +56,31 @@ public class TimescaleInsertTsRepository extends AbstractInsertRepository implem | @@ -56,28 +56,31 @@ public class TimescaleInsertTsRepository extends AbstractInsertRepository implem | ||
56 | ps.setBoolean(9, tsKvEntity.getBooleanValue()); | 56 | ps.setBoolean(9, tsKvEntity.getBooleanValue()); |
57 | } else { | 57 | } else { |
58 | ps.setNull(5, Types.BOOLEAN); | 58 | ps.setNull(5, Types.BOOLEAN); |
59 | - ps.setNull(9, Types.BOOLEAN); | 59 | + ps.setNull(10, Types.BOOLEAN); |
60 | } | 60 | } |
61 | 61 | ||
62 | ps.setString(6, replaceNullChars(tsKvEntity.getStrValue())); | 62 | ps.setString(6, replaceNullChars(tsKvEntity.getStrValue())); |
63 | - ps.setString(10, replaceNullChars(tsKvEntity.getStrValue())); | 63 | + ps.setString(11, replaceNullChars(tsKvEntity.getStrValue())); |
64 | 64 | ||
65 | 65 | ||
66 | if (tsKvEntity.getLongValue() != null) { | 66 | if (tsKvEntity.getLongValue() != null) { |
67 | ps.setLong(7, tsKvEntity.getLongValue()); | 67 | ps.setLong(7, tsKvEntity.getLongValue()); |
68 | - ps.setLong(11, tsKvEntity.getLongValue()); | 68 | + ps.setLong(12, tsKvEntity.getLongValue()); |
69 | } else { | 69 | } else { |
70 | ps.setNull(7, Types.BIGINT); | 70 | ps.setNull(7, Types.BIGINT); |
71 | - ps.setNull(11, Types.BIGINT); | 71 | + ps.setNull(12, Types.BIGINT); |
72 | } | 72 | } |
73 | 73 | ||
74 | if (tsKvEntity.getDoubleValue() != null) { | 74 | if (tsKvEntity.getDoubleValue() != null) { |
75 | ps.setDouble(8, tsKvEntity.getDoubleValue()); | 75 | ps.setDouble(8, tsKvEntity.getDoubleValue()); |
76 | - ps.setDouble(12, tsKvEntity.getDoubleValue()); | 76 | + ps.setDouble(13, tsKvEntity.getDoubleValue()); |
77 | } else { | 77 | } else { |
78 | ps.setNull(8, Types.DOUBLE); | 78 | ps.setNull(8, Types.DOUBLE); |
79 | - ps.setNull(12, Types.DOUBLE); | 79 | + ps.setNull(13, Types.DOUBLE); |
80 | } | 80 | } |
81 | + | ||
82 | + ps.setString(9, replaceNullChars(tsKvEntity.getJsonValue())); | ||
83 | + ps.setString(14, replaceNullChars(tsKvEntity.getJsonValue())); | ||
81 | } | 84 | } |
82 | 85 | ||
83 | @Override | 86 | @Override |
@@ -174,6 +174,8 @@ public class TimescaleTimeseriesDao extends AbstractSqlTimeseriesDao implements | @@ -174,6 +174,8 @@ public class TimescaleTimeseriesDao extends AbstractSqlTimeseriesDao implements | ||
174 | entity.setDoubleValue(tsKvEntry.getDoubleValue().orElse(null)); | 174 | entity.setDoubleValue(tsKvEntry.getDoubleValue().orElse(null)); |
175 | entity.setLongValue(tsKvEntry.getLongValue().orElse(null)); | 175 | entity.setLongValue(tsKvEntry.getLongValue().orElse(null)); |
176 | entity.setBooleanValue(tsKvEntry.getBooleanValue().orElse(null)); | 176 | entity.setBooleanValue(tsKvEntry.getBooleanValue().orElse(null)); |
177 | + entity.setJsonValue(tsKvEntry.getJsonValue().orElse(null)); | ||
178 | + | ||
177 | log.trace("Saving entity to timescale db: {}", entity); | 179 | log.trace("Saving entity to timescale db: {}", entity); |
178 | return tsQueue.add(new EntityContainer(entity, null)); | 180 | return tsQueue.add(new EntityContainer(entity, null)); |
179 | } | 181 | } |
@@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.kv.BasicTsKvEntry; | @@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.kv.BasicTsKvEntry; | ||
23 | import org.thingsboard.server.common.data.kv.BooleanDataEntry; | 23 | import org.thingsboard.server.common.data.kv.BooleanDataEntry; |
24 | import org.thingsboard.server.common.data.kv.DataType; | 24 | import org.thingsboard.server.common.data.kv.DataType; |
25 | import org.thingsboard.server.common.data.kv.DoubleDataEntry; | 25 | import org.thingsboard.server.common.data.kv.DoubleDataEntry; |
26 | +import org.thingsboard.server.common.data.kv.JsonDataEntry; | ||
26 | import org.thingsboard.server.common.data.kv.LongDataEntry; | 27 | import org.thingsboard.server.common.data.kv.LongDataEntry; |
27 | import org.thingsboard.server.common.data.kv.StringDataEntry; | 28 | import org.thingsboard.server.common.data.kv.StringDataEntry; |
28 | import org.thingsboard.server.common.data.kv.TsKvEntry; | 29 | import org.thingsboard.server.common.data.kv.TsKvEntry; |
@@ -41,10 +42,12 @@ public class AggregatePartitionsFunction implements com.google.common.base.Funct | @@ -41,10 +42,12 @@ public class AggregatePartitionsFunction implements com.google.common.base.Funct | ||
41 | private static final int DOUBLE_CNT_POS = 1; | 42 | private static final int DOUBLE_CNT_POS = 1; |
42 | private static final int BOOL_CNT_POS = 2; | 43 | private static final int BOOL_CNT_POS = 2; |
43 | private static final int STR_CNT_POS = 3; | 44 | private static final int STR_CNT_POS = 3; |
44 | - private static final int LONG_POS = 4; | ||
45 | - private static final int DOUBLE_POS = 5; | ||
46 | - private static final int BOOL_POS = 6; | ||
47 | - private static final int STR_POS = 7; | 45 | + private static final int JSON_CNT_POS = 4; |
46 | + private static final int LONG_POS = 5; | ||
47 | + private static final int DOUBLE_POS = 6; | ||
48 | + private static final int BOOL_POS = 7; | ||
49 | + private static final int STR_POS = 8; | ||
50 | + private static final int JSON_POS = 9; | ||
48 | 51 | ||
49 | private final Aggregation aggregation; | 52 | private final Aggregation aggregation; |
50 | private final String key; | 53 | private final String key; |
@@ -72,7 +75,7 @@ public class AggregatePartitionsFunction implements com.google.common.base.Funct | @@ -72,7 +75,7 @@ public class AggregatePartitionsFunction implements com.google.common.base.Funct | ||
72 | } | 75 | } |
73 | } | 76 | } |
74 | return processAggregationResult(aggResult); | 77 | return processAggregationResult(aggResult); |
75 | - }catch (Exception e){ | 78 | + } catch (Exception e) { |
76 | log.error("[{}][{}][{}] Failed to aggregate data", key, ts, aggregation, e); | 79 | log.error("[{}][{}][{}] Failed to aggregate data", key, ts, aggregation, e); |
77 | return Optional.empty(); | 80 | return Optional.empty(); |
78 | } | 81 | } |
@@ -85,11 +88,13 @@ public class AggregatePartitionsFunction implements com.google.common.base.Funct | @@ -85,11 +88,13 @@ public class AggregatePartitionsFunction implements com.google.common.base.Funct | ||
85 | Double curDValue = null; | 88 | Double curDValue = null; |
86 | Boolean curBValue = null; | 89 | Boolean curBValue = null; |
87 | String curSValue = null; | 90 | String curSValue = null; |
91 | + String curJValue = null; | ||
88 | 92 | ||
89 | long longCount = row.getLong(LONG_CNT_POS); | 93 | long longCount = row.getLong(LONG_CNT_POS); |
90 | long doubleCount = row.getLong(DOUBLE_CNT_POS); | 94 | long doubleCount = row.getLong(DOUBLE_CNT_POS); |
91 | long boolCount = row.getLong(BOOL_CNT_POS); | 95 | long boolCount = row.getLong(BOOL_CNT_POS); |
92 | long strCount = row.getLong(STR_CNT_POS); | 96 | long strCount = row.getLong(STR_CNT_POS); |
97 | + long jsonCount = row.getLong(JSON_CNT_POS); | ||
93 | 98 | ||
94 | if (longCount > 0 || doubleCount > 0) { | 99 | if (longCount > 0 || doubleCount > 0) { |
95 | if (longCount > 0) { | 100 | if (longCount > 0) { |
@@ -111,6 +116,10 @@ public class AggregatePartitionsFunction implements com.google.common.base.Funct | @@ -111,6 +116,10 @@ public class AggregatePartitionsFunction implements com.google.common.base.Funct | ||
111 | aggResult.dataType = DataType.STRING; | 116 | aggResult.dataType = DataType.STRING; |
112 | curCount = strCount; | 117 | curCount = strCount; |
113 | curSValue = getStringValue(row); | 118 | curSValue = getStringValue(row); |
119 | + } else if (jsonCount > 0) { | ||
120 | + aggResult.dataType = DataType.JSON; | ||
121 | + curCount = jsonCount; | ||
122 | + curJValue = getJsonValue(row); | ||
114 | } else { | 123 | } else { |
115 | return; | 124 | return; |
116 | } | 125 | } |
@@ -120,9 +129,9 @@ public class AggregatePartitionsFunction implements com.google.common.base.Funct | @@ -120,9 +129,9 @@ public class AggregatePartitionsFunction implements com.google.common.base.Funct | ||
120 | } else if (aggregation == Aggregation.AVG || aggregation == Aggregation.SUM) { | 129 | } else if (aggregation == Aggregation.AVG || aggregation == Aggregation.SUM) { |
121 | processAvgOrSumAggregation(aggResult, curCount, curLValue, curDValue); | 130 | processAvgOrSumAggregation(aggResult, curCount, curLValue, curDValue); |
122 | } else if (aggregation == Aggregation.MIN) { | 131 | } else if (aggregation == Aggregation.MIN) { |
123 | - processMinAggregation(aggResult, curLValue, curDValue, curBValue, curSValue); | 132 | + processMinAggregation(aggResult, curLValue, curDValue, curBValue, curSValue, curJValue); |
124 | } else if (aggregation == Aggregation.MAX) { | 133 | } else if (aggregation == Aggregation.MAX) { |
125 | - processMaxAggregation(aggResult, curLValue, curDValue, curBValue, curSValue); | 134 | + processMaxAggregation(aggResult, curLValue, curDValue, curBValue, curSValue, curJValue); |
126 | } | 135 | } |
127 | } | 136 | } |
128 | 137 | ||
@@ -136,7 +145,7 @@ public class AggregatePartitionsFunction implements com.google.common.base.Funct | @@ -136,7 +145,7 @@ public class AggregatePartitionsFunction implements com.google.common.base.Funct | ||
136 | } | 145 | } |
137 | } | 146 | } |
138 | 147 | ||
139 | - private void processMinAggregation(AggregationResult aggResult, Long curLValue, Double curDValue, Boolean curBValue, String curSValue) { | 148 | + private void processMinAggregation(AggregationResult aggResult, Long curLValue, Double curDValue, Boolean curBValue, String curSValue, String curJValue) { |
140 | if (curDValue != null || curLValue != null) { | 149 | if (curDValue != null || curLValue != null) { |
141 | if (curDValue != null) { | 150 | if (curDValue != null) { |
142 | aggResult.dValue = aggResult.dValue == null ? curDValue : Math.min(aggResult.dValue, curDValue); | 151 | aggResult.dValue = aggResult.dValue == null ? curDValue : Math.min(aggResult.dValue, curDValue); |
@@ -148,10 +157,12 @@ public class AggregatePartitionsFunction implements com.google.common.base.Funct | @@ -148,10 +157,12 @@ public class AggregatePartitionsFunction implements com.google.common.base.Funct | ||
148 | aggResult.bValue = aggResult.bValue == null ? curBValue : aggResult.bValue && curBValue; | 157 | aggResult.bValue = aggResult.bValue == null ? curBValue : aggResult.bValue && curBValue; |
149 | } else if (curSValue != null && (aggResult.sValue == null || curSValue.compareTo(aggResult.sValue) < 0)) { | 158 | } else if (curSValue != null && (aggResult.sValue == null || curSValue.compareTo(aggResult.sValue) < 0)) { |
150 | aggResult.sValue = curSValue; | 159 | aggResult.sValue = curSValue; |
160 | + } else if (curJValue != null && (aggResult.jValue == null || curJValue.compareTo(aggResult.jValue) < 0)) { | ||
161 | + aggResult.jValue = curJValue; | ||
151 | } | 162 | } |
152 | } | 163 | } |
153 | 164 | ||
154 | - private void processMaxAggregation(AggregationResult aggResult, Long curLValue, Double curDValue, Boolean curBValue, String curSValue) { | 165 | + private void processMaxAggregation(AggregationResult aggResult, Long curLValue, Double curDValue, Boolean curBValue, String curSValue, String curJValue) { |
155 | if (curDValue != null || curLValue != null) { | 166 | if (curDValue != null || curLValue != null) { |
156 | if (curDValue != null) { | 167 | if (curDValue != null) { |
157 | aggResult.dValue = aggResult.dValue == null ? curDValue : Math.max(aggResult.dValue, curDValue); | 168 | aggResult.dValue = aggResult.dValue == null ? curDValue : Math.max(aggResult.dValue, curDValue); |
@@ -163,6 +174,8 @@ public class AggregatePartitionsFunction implements com.google.common.base.Funct | @@ -163,6 +174,8 @@ public class AggregatePartitionsFunction implements com.google.common.base.Funct | ||
163 | aggResult.bValue = aggResult.bValue == null ? curBValue : aggResult.bValue || curBValue; | 174 | aggResult.bValue = aggResult.bValue == null ? curBValue : aggResult.bValue || curBValue; |
164 | } else if (curSValue != null && (aggResult.sValue == null || curSValue.compareTo(aggResult.sValue) > 0)) { | 175 | } else if (curSValue != null && (aggResult.sValue == null || curSValue.compareTo(aggResult.sValue) > 0)) { |
165 | aggResult.sValue = curSValue; | 176 | aggResult.sValue = curSValue; |
177 | + } else if (curJValue != null && (aggResult.jValue == null || curJValue.compareTo(aggResult.jValue) > 0)) { | ||
178 | + aggResult.jValue = curJValue; | ||
166 | } | 179 | } |
167 | } | 180 | } |
168 | 181 | ||
@@ -182,6 +195,14 @@ public class AggregatePartitionsFunction implements com.google.common.base.Funct | @@ -182,6 +195,14 @@ public class AggregatePartitionsFunction implements com.google.common.base.Funct | ||
182 | } | 195 | } |
183 | } | 196 | } |
184 | 197 | ||
198 | + private String getJsonValue(Row row) { | ||
199 | + if (aggregation == Aggregation.MIN || aggregation == Aggregation.MAX) { | ||
200 | + return row.getString(JSON_POS); | ||
201 | + } else { | ||
202 | + return null; | ||
203 | + } | ||
204 | + } | ||
205 | + | ||
185 | private Long getLongValue(Row row) { | 206 | private Long getLongValue(Row row) { |
186 | if (aggregation == Aggregation.MIN || aggregation == Aggregation.MAX | 207 | if (aggregation == Aggregation.MIN || aggregation == Aggregation.MAX |
187 | || aggregation == Aggregation.SUM || aggregation == Aggregation.AVG) { | 208 | || aggregation == Aggregation.SUM || aggregation == Aggregation.AVG) { |
@@ -223,7 +244,7 @@ public class AggregatePartitionsFunction implements com.google.common.base.Funct | @@ -223,7 +244,7 @@ public class AggregatePartitionsFunction implements com.google.common.base.Funct | ||
223 | if (aggResult.count == 0 || (aggResult.dataType == DataType.DOUBLE && aggResult.dValue == null) || (aggResult.dataType == DataType.LONG && aggResult.lValue == null)) { | 244 | if (aggResult.count == 0 || (aggResult.dataType == DataType.DOUBLE && aggResult.dValue == null) || (aggResult.dataType == DataType.LONG && aggResult.lValue == null)) { |
224 | return Optional.empty(); | 245 | return Optional.empty(); |
225 | } else if (aggResult.dataType == DataType.DOUBLE || aggResult.dataType == DataType.LONG) { | 246 | } else if (aggResult.dataType == DataType.DOUBLE || aggResult.dataType == DataType.LONG) { |
226 | - if(aggregation == Aggregation.AVG || aggResult.hasDouble) { | 247 | + if (aggregation == Aggregation.AVG || aggResult.hasDouble) { |
227 | double sum = Optional.ofNullable(aggResult.dValue).orElse(0.0d) + Optional.ofNullable(aggResult.lValue).orElse(0L); | 248 | double sum = Optional.ofNullable(aggResult.dValue).orElse(0.0d) + Optional.ofNullable(aggResult.lValue).orElse(0L); |
228 | return Optional.of(new BasicTsKvEntry(ts, new DoubleDataEntry(key, aggregation == Aggregation.SUM ? sum : (sum / aggResult.count)))); | 249 | return Optional.of(new BasicTsKvEntry(ts, new DoubleDataEntry(key, aggregation == Aggregation.SUM ? sum : (sum / aggResult.count)))); |
229 | } else { | 250 | } else { |
@@ -235,15 +256,17 @@ public class AggregatePartitionsFunction implements com.google.common.base.Funct | @@ -235,15 +256,17 @@ public class AggregatePartitionsFunction implements com.google.common.base.Funct | ||
235 | 256 | ||
236 | private Optional<TsKvEntry> processMinOrMaxResult(AggregationResult aggResult) { | 257 | private Optional<TsKvEntry> processMinOrMaxResult(AggregationResult aggResult) { |
237 | if (aggResult.dataType == DataType.DOUBLE || aggResult.dataType == DataType.LONG) { | 258 | if (aggResult.dataType == DataType.DOUBLE || aggResult.dataType == DataType.LONG) { |
238 | - if(aggResult.hasDouble) { | 259 | + if (aggResult.hasDouble) { |
239 | double currentD = aggregation == Aggregation.MIN ? Optional.ofNullable(aggResult.dValue).orElse(Double.MAX_VALUE) : Optional.ofNullable(aggResult.dValue).orElse(Double.MIN_VALUE); | 260 | double currentD = aggregation == Aggregation.MIN ? Optional.ofNullable(aggResult.dValue).orElse(Double.MAX_VALUE) : Optional.ofNullable(aggResult.dValue).orElse(Double.MIN_VALUE); |
240 | double currentL = aggregation == Aggregation.MIN ? Optional.ofNullable(aggResult.lValue).orElse(Long.MAX_VALUE) : Optional.ofNullable(aggResult.lValue).orElse(Long.MIN_VALUE); | 261 | double currentL = aggregation == Aggregation.MIN ? Optional.ofNullable(aggResult.lValue).orElse(Long.MAX_VALUE) : Optional.ofNullable(aggResult.lValue).orElse(Long.MIN_VALUE); |
241 | return Optional.of(new BasicTsKvEntry(ts, new DoubleDataEntry(key, aggregation == Aggregation.MIN ? Math.min(currentD, currentL) : Math.max(currentD, currentL)))); | 262 | return Optional.of(new BasicTsKvEntry(ts, new DoubleDataEntry(key, aggregation == Aggregation.MIN ? Math.min(currentD, currentL) : Math.max(currentD, currentL)))); |
242 | } else { | 263 | } else { |
243 | return Optional.of(new BasicTsKvEntry(ts, new LongDataEntry(key, aggResult.lValue))); | 264 | return Optional.of(new BasicTsKvEntry(ts, new LongDataEntry(key, aggResult.lValue))); |
244 | } | 265 | } |
245 | - } else if (aggResult.dataType == DataType.STRING) { | 266 | + } else if (aggResult.dataType == DataType.STRING) { |
246 | return Optional.of(new BasicTsKvEntry(ts, new StringDataEntry(key, aggResult.sValue))); | 267 | return Optional.of(new BasicTsKvEntry(ts, new StringDataEntry(key, aggResult.sValue))); |
268 | + } else if (aggResult.dataType == DataType.JSON) { | ||
269 | + return Optional.of(new BasicTsKvEntry(ts, new JsonDataEntry(key, aggResult.jValue))); | ||
247 | } else { | 270 | } else { |
248 | return Optional.of(new BasicTsKvEntry(ts, new BooleanDataEntry(key, aggResult.bValue))); | 271 | return Optional.of(new BasicTsKvEntry(ts, new BooleanDataEntry(key, aggResult.bValue))); |
249 | } | 272 | } |
@@ -253,6 +276,7 @@ public class AggregatePartitionsFunction implements com.google.common.base.Funct | @@ -253,6 +276,7 @@ public class AggregatePartitionsFunction implements com.google.common.base.Funct | ||
253 | DataType dataType = null; | 276 | DataType dataType = null; |
254 | Boolean bValue = null; | 277 | Boolean bValue = null; |
255 | String sValue = null; | 278 | String sValue = null; |
279 | + String jValue = null; | ||
256 | Double dValue = null; | 280 | Double dValue = null; |
257 | Long lValue = null; | 281 | Long lValue = null; |
258 | long count = 0; | 282 | long count = 0; |
@@ -29,6 +29,7 @@ import com.google.common.util.concurrent.FutureCallback; | @@ -29,6 +29,7 @@ import com.google.common.util.concurrent.FutureCallback; | ||
29 | import com.google.common.util.concurrent.Futures; | 29 | import com.google.common.util.concurrent.Futures; |
30 | import com.google.common.util.concurrent.ListenableFuture; | 30 | import com.google.common.util.concurrent.ListenableFuture; |
31 | import lombok.extern.slf4j.Slf4j; | 31 | import lombok.extern.slf4j.Slf4j; |
32 | +import org.apache.commons.lang3.StringUtils; | ||
32 | import org.springframework.beans.factory.annotation.Autowired; | 33 | import org.springframework.beans.factory.annotation.Autowired; |
33 | import org.springframework.beans.factory.annotation.Value; | 34 | import org.springframework.beans.factory.annotation.Value; |
34 | import org.springframework.core.env.Environment; | 35 | import org.springframework.core.env.Environment; |
@@ -42,6 +43,7 @@ import org.thingsboard.server.common.data.kv.BooleanDataEntry; | @@ -42,6 +43,7 @@ import org.thingsboard.server.common.data.kv.BooleanDataEntry; | ||
42 | import org.thingsboard.server.common.data.kv.DataType; | 43 | import org.thingsboard.server.common.data.kv.DataType; |
43 | import org.thingsboard.server.common.data.kv.DeleteTsKvQuery; | 44 | import org.thingsboard.server.common.data.kv.DeleteTsKvQuery; |
44 | import org.thingsboard.server.common.data.kv.DoubleDataEntry; | 45 | import org.thingsboard.server.common.data.kv.DoubleDataEntry; |
46 | +import org.thingsboard.server.common.data.kv.JsonDataEntry; | ||
45 | import org.thingsboard.server.common.data.kv.KvEntry; | 47 | import org.thingsboard.server.common.data.kv.KvEntry; |
46 | import org.thingsboard.server.common.data.kv.LongDataEntry; | 48 | import org.thingsboard.server.common.data.kv.LongDataEntry; |
47 | import org.thingsboard.server.common.data.kv.ReadTsKvQuery; | 49 | import org.thingsboard.server.common.data.kv.ReadTsKvQuery; |
@@ -337,21 +339,31 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem | @@ -337,21 +339,31 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem | ||
337 | futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.BOOLEAN)); | 339 | futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.BOOLEAN)); |
338 | futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.DOUBLE)); | 340 | futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.DOUBLE)); |
339 | futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.STRING)); | 341 | futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.STRING)); |
342 | + futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.JSON)); | ||
340 | break; | 343 | break; |
341 | case BOOLEAN: | 344 | case BOOLEAN: |
342 | futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.DOUBLE)); | 345 | futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.DOUBLE)); |
343 | futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.LONG)); | 346 | futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.LONG)); |
344 | futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.STRING)); | 347 | futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.STRING)); |
348 | + futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.JSON)); | ||
345 | break; | 349 | break; |
346 | case DOUBLE: | 350 | case DOUBLE: |
347 | futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.BOOLEAN)); | 351 | futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.BOOLEAN)); |
348 | futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.LONG)); | 352 | futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.LONG)); |
349 | futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.STRING)); | 353 | futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.STRING)); |
354 | + futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.JSON)); | ||
350 | break; | 355 | break; |
351 | case STRING: | 356 | case STRING: |
352 | futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.BOOLEAN)); | 357 | futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.BOOLEAN)); |
353 | futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.DOUBLE)); | 358 | futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.DOUBLE)); |
354 | futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.LONG)); | 359 | futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.LONG)); |
360 | + futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.JSON)); | ||
361 | + break; | ||
362 | + case JSON: | ||
363 | + futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.BOOLEAN)); | ||
364 | + futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.DOUBLE)); | ||
365 | + futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.LONG)); | ||
366 | + futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.STRING)); | ||
355 | break; | 367 | break; |
356 | } | 368 | } |
357 | } | 369 | } |
@@ -411,6 +423,13 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem | @@ -411,6 +423,13 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem | ||
411 | .set(5, tsKvEntry.getStrValue().orElse(null), String.class) | 423 | .set(5, tsKvEntry.getStrValue().orElse(null), String.class) |
412 | .set(6, tsKvEntry.getLongValue().orElse(null), Long.class) | 424 | .set(6, tsKvEntry.getLongValue().orElse(null), Long.class) |
413 | .set(7, tsKvEntry.getDoubleValue().orElse(null), Double.class); | 425 | .set(7, tsKvEntry.getDoubleValue().orElse(null), Double.class); |
426 | + Optional<String> jsonV = tsKvEntry.getJsonValue(); | ||
427 | + if (jsonV.isPresent()) { | ||
428 | + stmt.setString(8, tsKvEntry.getJsonValue().get()); | ||
429 | + } else { | ||
430 | + stmt.setToNull(8); | ||
431 | + } | ||
432 | + | ||
414 | return getFuture(executeAsyncWrite(tenantId, stmt), rs -> null); | 433 | return getFuture(executeAsyncWrite(tenantId, stmt), rs -> null); |
415 | } | 434 | } |
416 | 435 | ||
@@ -669,7 +688,12 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem | @@ -669,7 +688,12 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem | ||
669 | if (boolV != null) { | 688 | if (boolV != null) { |
670 | kvEntry = new BooleanDataEntry(key, boolV); | 689 | kvEntry = new BooleanDataEntry(key, boolV); |
671 | } else { | 690 | } else { |
672 | - log.warn("All values in key-value row are nullable "); | 691 | + String jsonV = row.get(ModelConstants.JSON_VALUE_COLUMN, String.class); |
692 | + if (StringUtils.isNoneEmpty(jsonV)) { | ||
693 | + kvEntry = new JsonDataEntry(key, jsonV); | ||
694 | + } else { | ||
695 | + log.warn("All values in key-value row are nullable "); | ||
696 | + } | ||
673 | } | 697 | } |
674 | } | 698 | } |
675 | } | 699 | } |
@@ -772,8 +796,9 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem | @@ -772,8 +796,9 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem | ||
772 | "," + ModelConstants.BOOLEAN_VALUE_COLUMN + | 796 | "," + ModelConstants.BOOLEAN_VALUE_COLUMN + |
773 | "," + ModelConstants.STRING_VALUE_COLUMN + | 797 | "," + ModelConstants.STRING_VALUE_COLUMN + |
774 | "," + ModelConstants.LONG_VALUE_COLUMN + | 798 | "," + ModelConstants.LONG_VALUE_COLUMN + |
775 | - "," + ModelConstants.DOUBLE_VALUE_COLUMN + ")" + | ||
776 | - " VALUES(?, ?, ?, ?, ?, ?, ?, ?)"); | 799 | + "," + ModelConstants.DOUBLE_VALUE_COLUMN + |
800 | + "," + ModelConstants.JSON_VALUE_COLUMN + ")" + | ||
801 | + " VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)"); | ||
777 | } | 802 | } |
778 | return latestInsertStmt; | 803 | return latestInsertStmt; |
779 | } | 804 | } |
@@ -812,7 +837,8 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem | @@ -812,7 +837,8 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem | ||
812 | ModelConstants.STRING_VALUE_COLUMN + "," + | 837 | ModelConstants.STRING_VALUE_COLUMN + "," + |
813 | ModelConstants.BOOLEAN_VALUE_COLUMN + "," + | 838 | ModelConstants.BOOLEAN_VALUE_COLUMN + "," + |
814 | ModelConstants.LONG_VALUE_COLUMN + "," + | 839 | ModelConstants.LONG_VALUE_COLUMN + "," + |
815 | - ModelConstants.DOUBLE_VALUE_COLUMN + " " + | 840 | + ModelConstants.DOUBLE_VALUE_COLUMN + "," + |
841 | + ModelConstants.JSON_VALUE_COLUMN + " " + | ||
816 | "FROM " + ModelConstants.TS_KV_LATEST_CF + " " + | 842 | "FROM " + ModelConstants.TS_KV_LATEST_CF + " " + |
817 | "WHERE " + ModelConstants.ENTITY_TYPE_COLUMN + EQUALS_PARAM + | 843 | "WHERE " + ModelConstants.ENTITY_TYPE_COLUMN + EQUALS_PARAM + |
818 | "AND " + ModelConstants.ENTITY_ID_COLUMN + EQUALS_PARAM + | 844 | "AND " + ModelConstants.ENTITY_ID_COLUMN + EQUALS_PARAM + |
@@ -829,7 +855,8 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem | @@ -829,7 +855,8 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem | ||
829 | ModelConstants.STRING_VALUE_COLUMN + "," + | 855 | ModelConstants.STRING_VALUE_COLUMN + "," + |
830 | ModelConstants.BOOLEAN_VALUE_COLUMN + "," + | 856 | ModelConstants.BOOLEAN_VALUE_COLUMN + "," + |
831 | ModelConstants.LONG_VALUE_COLUMN + "," + | 857 | ModelConstants.LONG_VALUE_COLUMN + "," + |
832 | - ModelConstants.DOUBLE_VALUE_COLUMN + " " + | 858 | + ModelConstants.DOUBLE_VALUE_COLUMN + "," + |
859 | + ModelConstants.JSON_VALUE_COLUMN + " " + | ||
833 | "FROM " + ModelConstants.TS_KV_LATEST_CF + " " + | 860 | "FROM " + ModelConstants.TS_KV_LATEST_CF + " " + |
834 | "WHERE " + ModelConstants.ENTITY_TYPE_COLUMN + EQUALS_PARAM + | 861 | "WHERE " + ModelConstants.ENTITY_TYPE_COLUMN + EQUALS_PARAM + |
835 | "AND " + ModelConstants.ENTITY_ID_COLUMN + EQUALS_PARAM); | 862 | "AND " + ModelConstants.ENTITY_ID_COLUMN + EQUALS_PARAM); |
@@ -847,6 +874,8 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem | @@ -847,6 +874,8 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem | ||
847 | return ModelConstants.LONG_VALUE_COLUMN; | 874 | return ModelConstants.LONG_VALUE_COLUMN; |
848 | case DOUBLE: | 875 | case DOUBLE: |
849 | return ModelConstants.DOUBLE_VALUE_COLUMN; | 876 | return ModelConstants.DOUBLE_VALUE_COLUMN; |
877 | + case JSON: | ||
878 | + return ModelConstants.JSON_VALUE_COLUMN; | ||
850 | default: | 879 | default: |
851 | throw new RuntimeException("Not implemented!"); | 880 | throw new RuntimeException("Not implemented!"); |
852 | } | 881 | } |
@@ -856,27 +885,23 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem | @@ -856,27 +885,23 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem | ||
856 | switch (kvEntry.getDataType()) { | 885 | switch (kvEntry.getDataType()) { |
857 | case BOOLEAN: | 886 | case BOOLEAN: |
858 | Optional<Boolean> booleanValue = kvEntry.getBooleanValue(); | 887 | Optional<Boolean> booleanValue = kvEntry.getBooleanValue(); |
859 | - if (booleanValue.isPresent()) { | ||
860 | - stmt.setBool(column, booleanValue.get().booleanValue()); | ||
861 | - } | 888 | + booleanValue.ifPresent(b -> stmt.setBool(column, b)); |
862 | break; | 889 | break; |
863 | case STRING: | 890 | case STRING: |
864 | Optional<String> stringValue = kvEntry.getStrValue(); | 891 | Optional<String> stringValue = kvEntry.getStrValue(); |
865 | - if (stringValue.isPresent()) { | ||
866 | - stmt.setString(column, stringValue.get()); | ||
867 | - } | 892 | + stringValue.ifPresent(s -> stmt.setString(column, s)); |
868 | break; | 893 | break; |
869 | case LONG: | 894 | case LONG: |
870 | Optional<Long> longValue = kvEntry.getLongValue(); | 895 | Optional<Long> longValue = kvEntry.getLongValue(); |
871 | - if (longValue.isPresent()) { | ||
872 | - stmt.setLong(column, longValue.get().longValue()); | ||
873 | - } | 896 | + longValue.ifPresent(l -> stmt.setLong(column, l)); |
874 | break; | 897 | break; |
875 | case DOUBLE: | 898 | case DOUBLE: |
876 | Optional<Double> doubleValue = kvEntry.getDoubleValue(); | 899 | Optional<Double> doubleValue = kvEntry.getDoubleValue(); |
877 | - if (doubleValue.isPresent()) { | ||
878 | - stmt.setDouble(column, doubleValue.get().doubleValue()); | ||
879 | - } | 900 | + doubleValue.ifPresent(d -> stmt.setDouble(column, d)); |
901 | + break; | ||
902 | + case JSON: | ||
903 | + Optional<String> jsonValue = kvEntry.getJsonValue(); | ||
904 | + jsonValue.ifPresent(jsonObject -> stmt.setString(column, jsonObject)); | ||
880 | break; | 905 | break; |
881 | } | 906 | } |
882 | } | 907 | } |
@@ -410,6 +410,7 @@ CREATE TABLE IF NOT EXISTS thingsboard.attributes_kv_cf ( | @@ -410,6 +410,7 @@ CREATE TABLE IF NOT EXISTS thingsboard.attributes_kv_cf ( | ||
410 | str_v text, | 410 | str_v text, |
411 | long_v bigint, | 411 | long_v bigint, |
412 | dbl_v double, | 412 | dbl_v double, |
413 | + json_v text, | ||
413 | last_update_ts bigint, | 414 | last_update_ts bigint, |
414 | PRIMARY KEY ((entity_type, entity_id, attribute_type), attribute_key) | 415 | PRIMARY KEY ((entity_type, entity_id, attribute_type), attribute_key) |
415 | ) WITH compaction = { 'class' : 'LeveledCompactionStrategy' }; | 416 | ) WITH compaction = { 'class' : 'LeveledCompactionStrategy' }; |
@@ -30,6 +30,7 @@ CREATE TABLE IF NOT EXISTS thingsboard.ts_kv_cf ( | @@ -30,6 +30,7 @@ CREATE TABLE IF NOT EXISTS thingsboard.ts_kv_cf ( | ||
30 | str_v text, | 30 | str_v text, |
31 | long_v bigint, | 31 | long_v bigint, |
32 | dbl_v double, | 32 | dbl_v double, |
33 | + json_v text, | ||
33 | PRIMARY KEY (( entity_type, entity_id, key, partition ), ts) | 34 | PRIMARY KEY (( entity_type, entity_id, key, partition ), ts) |
34 | ); | 35 | ); |
35 | 36 | ||
@@ -51,5 +52,6 @@ CREATE TABLE IF NOT EXISTS thingsboard.ts_kv_latest_cf ( | @@ -51,5 +52,6 @@ CREATE TABLE IF NOT EXISTS thingsboard.ts_kv_latest_cf ( | ||
51 | str_v text, | 52 | str_v text, |
52 | long_v bigint, | 53 | long_v bigint, |
53 | dbl_v double, | 54 | dbl_v double, |
55 | + json_v text, | ||
54 | PRIMARY KEY (( entity_type, entity_id ), key) | 56 | PRIMARY KEY (( entity_type, entity_id ), key) |
55 | ) WITH compaction = { 'class' : 'LeveledCompactionStrategy' }; | 57 | ) WITH compaction = { 'class' : 'LeveledCompactionStrategy' }; |
1 | +-- | ||
2 | +-- Copyright © 2016-2020 The Thingsboard Authors | ||
3 | +-- | ||
4 | +-- Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | +-- you may not use this file except in compliance with the License. | ||
6 | +-- You may obtain a copy of the License at | ||
7 | +-- | ||
8 | +-- http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | +-- | ||
10 | +-- Unless required by applicable law or agreed to in writing, software | ||
11 | +-- distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | +-- See the License for the specific language governing permissions and | ||
14 | +-- limitations under the License. | ||
15 | +-- | ||
16 | + | ||
17 | + | ||
18 | +CREATE TABLE IF NOT EXISTS admin_settings ( | ||
19 | + id varchar(31) NOT NULL CONSTRAINT admin_settings_pkey PRIMARY KEY, | ||
20 | + json_value varchar, | ||
21 | + key varchar(255) | ||
22 | +); | ||
23 | + | ||
24 | +CREATE TABLE IF NOT EXISTS alarm ( | ||
25 | + id varchar(31) NOT NULL CONSTRAINT alarm_pkey PRIMARY KEY, | ||
26 | + ack_ts bigint, | ||
27 | + clear_ts bigint, | ||
28 | + additional_info varchar, | ||
29 | + end_ts bigint, | ||
30 | + originator_id varchar(31), | ||
31 | + originator_type integer, | ||
32 | + propagate boolean, | ||
33 | + severity varchar(255), | ||
34 | + start_ts bigint, | ||
35 | + status varchar(255), | ||
36 | + tenant_id varchar(31), | ||
37 | + propagate_relation_types varchar, | ||
38 | + type varchar(255) | ||
39 | +); | ||
40 | + | ||
41 | +CREATE TABLE IF NOT EXISTS asset ( | ||
42 | + id varchar(31) NOT NULL CONSTRAINT asset_pkey PRIMARY KEY, | ||
43 | + additional_info varchar, | ||
44 | + customer_id varchar(31), | ||
45 | + name varchar(255), | ||
46 | + label varchar(255), | ||
47 | + search_text varchar(255), | ||
48 | + tenant_id varchar(31), | ||
49 | + type varchar(255), | ||
50 | + CONSTRAINT asset_name_unq_key UNIQUE (tenant_id, name) | ||
51 | +); | ||
52 | + | ||
53 | +CREATE TABLE IF NOT EXISTS audit_log ( | ||
54 | + id varchar(31) NOT NULL CONSTRAINT audit_log_pkey PRIMARY KEY, | ||
55 | + tenant_id varchar(31), | ||
56 | + customer_id varchar(31), | ||
57 | + entity_id varchar(31), | ||
58 | + entity_type varchar(255), | ||
59 | + entity_name varchar(255), | ||
60 | + user_id varchar(31), | ||
61 | + user_name varchar(255), | ||
62 | + action_type varchar(255), | ||
63 | + action_data varchar(1000000), | ||
64 | + action_status varchar(255), | ||
65 | + action_failure_details varchar(1000000) | ||
66 | +); | ||
67 | + | ||
68 | +CREATE TABLE IF NOT EXISTS attribute_kv ( | ||
69 | + entity_type varchar(255), | ||
70 | + entity_id varchar(31), | ||
71 | + attribute_type varchar(255), | ||
72 | + attribute_key varchar(255), | ||
73 | + bool_v boolean, | ||
74 | + str_v varchar(10000000), | ||
75 | + long_v bigint, | ||
76 | + dbl_v double precision, | ||
77 | + json_v varchar(10000000), | ||
78 | + last_update_ts bigint, | ||
79 | + CONSTRAINT attribute_kv_pkey PRIMARY KEY (entity_type, entity_id, attribute_type, attribute_key) | ||
80 | +); | ||
81 | + | ||
82 | +CREATE TABLE IF NOT EXISTS component_descriptor ( | ||
83 | + id varchar(31) NOT NULL CONSTRAINT component_descriptor_pkey PRIMARY KEY, | ||
84 | + actions varchar(255), | ||
85 | + clazz varchar UNIQUE, | ||
86 | + configuration_descriptor varchar, | ||
87 | + name varchar(255), | ||
88 | + scope varchar(255), | ||
89 | + search_text varchar(255), | ||
90 | + type varchar(255) | ||
91 | +); | ||
92 | + | ||
93 | +CREATE TABLE IF NOT EXISTS customer ( | ||
94 | + id varchar(31) NOT NULL CONSTRAINT customer_pkey PRIMARY KEY, | ||
95 | + additional_info varchar, | ||
96 | + address varchar, | ||
97 | + address2 varchar, | ||
98 | + city varchar(255), | ||
99 | + country varchar(255), | ||
100 | + email varchar(255), | ||
101 | + phone varchar(255), | ||
102 | + search_text varchar(255), | ||
103 | + state varchar(255), | ||
104 | + tenant_id varchar(31), | ||
105 | + title varchar(255), | ||
106 | + zip varchar(255) | ||
107 | +); | ||
108 | + | ||
109 | +CREATE TABLE IF NOT EXISTS dashboard ( | ||
110 | + id varchar(31) NOT NULL CONSTRAINT dashboard_pkey PRIMARY KEY, | ||
111 | + configuration varchar(10000000), | ||
112 | + assigned_customers varchar(1000000), | ||
113 | + search_text varchar(255), | ||
114 | + tenant_id varchar(31), | ||
115 | + title varchar(255) | ||
116 | +); | ||
117 | + | ||
118 | +CREATE TABLE IF NOT EXISTS device ( | ||
119 | + id varchar(31) NOT NULL CONSTRAINT device_pkey PRIMARY KEY, | ||
120 | + additional_info varchar, | ||
121 | + customer_id varchar(31), | ||
122 | + type varchar(255), | ||
123 | + name varchar(255), | ||
124 | + label varchar(255), | ||
125 | + search_text varchar(255), | ||
126 | + tenant_id varchar(31), | ||
127 | + CONSTRAINT device_name_unq_key UNIQUE (tenant_id, name) | ||
128 | +); | ||
129 | + | ||
130 | +CREATE TABLE IF NOT EXISTS device_credentials ( | ||
131 | + id varchar(31) NOT NULL CONSTRAINT device_credentials_pkey PRIMARY KEY, | ||
132 | + credentials_id varchar, | ||
133 | + credentials_type varchar(255), | ||
134 | + credentials_value varchar, | ||
135 | + device_id varchar(31), | ||
136 | + CONSTRAINT device_credentials_id_unq_key UNIQUE (credentials_id) | ||
137 | +); | ||
138 | + | ||
139 | +CREATE TABLE IF NOT EXISTS event ( | ||
140 | + id varchar(31) NOT NULL CONSTRAINT event_pkey PRIMARY KEY, | ||
141 | + body varchar(10000000), | ||
142 | + entity_id varchar(31), | ||
143 | + entity_type varchar(255), | ||
144 | + event_type varchar(255), | ||
145 | + event_uid varchar(255), | ||
146 | + tenant_id varchar(31), | ||
147 | + CONSTRAINT event_unq_key UNIQUE (tenant_id, entity_type, entity_id, event_type, event_uid) | ||
148 | +); | ||
149 | + | ||
150 | +CREATE TABLE IF NOT EXISTS relation ( | ||
151 | + from_id varchar(31), | ||
152 | + from_type varchar(255), | ||
153 | + to_id varchar(31), | ||
154 | + to_type varchar(255), | ||
155 | + relation_type_group varchar(255), | ||
156 | + relation_type varchar(255), | ||
157 | + additional_info varchar, | ||
158 | + CONSTRAINT relation_pkey PRIMARY KEY (from_id, from_type, relation_type_group, relation_type, to_id, to_type) | ||
159 | +); | ||
160 | + | ||
161 | +CREATE TABLE IF NOT EXISTS tb_user ( | ||
162 | + id varchar(31) NOT NULL CONSTRAINT tb_user_pkey PRIMARY KEY, | ||
163 | + additional_info varchar, | ||
164 | + authority varchar(255), | ||
165 | + customer_id varchar(31), | ||
166 | + email varchar(255) UNIQUE, | ||
167 | + first_name varchar(255), | ||
168 | + last_name varchar(255), | ||
169 | + search_text varchar(255), | ||
170 | + tenant_id varchar(31) | ||
171 | +); | ||
172 | + | ||
173 | +CREATE TABLE IF NOT EXISTS tenant ( | ||
174 | + id varchar(31) NOT NULL CONSTRAINT tenant_pkey PRIMARY KEY, | ||
175 | + additional_info varchar, | ||
176 | + address varchar, | ||
177 | + address2 varchar, | ||
178 | + city varchar(255), | ||
179 | + country varchar(255), | ||
180 | + email varchar(255), | ||
181 | + phone varchar(255), | ||
182 | + region varchar(255), | ||
183 | + search_text varchar(255), | ||
184 | + state varchar(255), | ||
185 | + title varchar(255), | ||
186 | + zip varchar(255) | ||
187 | +); | ||
188 | + | ||
189 | +CREATE TABLE IF NOT EXISTS user_credentials ( | ||
190 | + id varchar(31) NOT NULL CONSTRAINT user_credentials_pkey PRIMARY KEY, | ||
191 | + activate_token varchar(255) UNIQUE, | ||
192 | + enabled boolean, | ||
193 | + password varchar(255), | ||
194 | + reset_token varchar(255) UNIQUE, | ||
195 | + user_id varchar(31) UNIQUE | ||
196 | +); | ||
197 | + | ||
198 | +CREATE TABLE IF NOT EXISTS widget_type ( | ||
199 | + id varchar(31) NOT NULL CONSTRAINT widget_type_pkey PRIMARY KEY, | ||
200 | + alias varchar(255), | ||
201 | + bundle_alias varchar(255), | ||
202 | + descriptor varchar(1000000), | ||
203 | + name varchar(255), | ||
204 | + tenant_id varchar(31) | ||
205 | +); | ||
206 | + | ||
207 | +CREATE TABLE IF NOT EXISTS widgets_bundle ( | ||
208 | + id varchar(31) NOT NULL CONSTRAINT widgets_bundle_pkey PRIMARY KEY, | ||
209 | + alias varchar(255), | ||
210 | + search_text varchar(255), | ||
211 | + tenant_id varchar(31), | ||
212 | + title varchar(255) | ||
213 | +); | ||
214 | + | ||
215 | +CREATE TABLE IF NOT EXISTS rule_chain ( | ||
216 | + id varchar(31) NOT NULL CONSTRAINT rule_chain_pkey PRIMARY KEY, | ||
217 | + additional_info varchar, | ||
218 | + configuration varchar(10000000), | ||
219 | + name varchar(255), | ||
220 | + first_rule_node_id varchar(31), | ||
221 | + root boolean, | ||
222 | + debug_mode boolean, | ||
223 | + search_text varchar(255), | ||
224 | + tenant_id varchar(31) | ||
225 | +); | ||
226 | + | ||
227 | +CREATE TABLE IF NOT EXISTS rule_node ( | ||
228 | + id varchar(31) NOT NULL CONSTRAINT rule_node_pkey PRIMARY KEY, | ||
229 | + rule_chain_id varchar(31), | ||
230 | + additional_info varchar, | ||
231 | + configuration varchar(10000000), | ||
232 | + type varchar(255), | ||
233 | + name varchar(255), | ||
234 | + debug_mode boolean, | ||
235 | + search_text varchar(255) | ||
236 | +); | ||
237 | + | ||
238 | +CREATE TABLE IF NOT EXISTS entity_view ( | ||
239 | + id varchar(31) NOT NULL CONSTRAINT entity_view_pkey PRIMARY KEY, | ||
240 | + entity_id varchar(31), | ||
241 | + entity_type varchar(255), | ||
242 | + tenant_id varchar(31), | ||
243 | + customer_id varchar(31), | ||
244 | + type varchar(255), | ||
245 | + name varchar(255), | ||
246 | + keys varchar(10000000), | ||
247 | + start_ts bigint, | ||
248 | + end_ts bigint, | ||
249 | + search_text varchar(255), | ||
250 | + additional_info varchar | ||
251 | +); |
@@ -74,6 +74,7 @@ CREATE TABLE IF NOT EXISTS attribute_kv ( | @@ -74,6 +74,7 @@ CREATE TABLE IF NOT EXISTS attribute_kv ( | ||
74 | str_v varchar(10000000), | 74 | str_v varchar(10000000), |
75 | long_v bigint, | 75 | long_v bigint, |
76 | dbl_v double precision, | 76 | dbl_v double precision, |
77 | + json_v json, | ||
77 | last_update_ts bigint, | 78 | last_update_ts bigint, |
78 | CONSTRAINT attribute_kv_pkey PRIMARY KEY (entity_type, entity_id, attribute_type, attribute_key) | 79 | CONSTRAINT attribute_kv_pkey PRIMARY KEY (entity_type, entity_id, attribute_type, attribute_key) |
79 | ); | 80 | ); |
@@ -25,6 +25,7 @@ CREATE TABLE IF NOT EXISTS tenant_ts_kv ( | @@ -25,6 +25,7 @@ CREATE TABLE IF NOT EXISTS tenant_ts_kv ( | ||
25 | str_v varchar(10000000), | 25 | str_v varchar(10000000), |
26 | long_v bigint, | 26 | long_v bigint, |
27 | dbl_v double precision, | 27 | dbl_v double precision, |
28 | + json_v json, | ||
28 | CONSTRAINT tenant_ts_kv_pkey PRIMARY KEY (tenant_id, entity_id, key, ts) | 29 | CONSTRAINT tenant_ts_kv_pkey PRIMARY KEY (tenant_id, entity_id, key, ts) |
29 | ); | 30 | ); |
30 | 31 | ||
@@ -42,5 +43,6 @@ CREATE TABLE IF NOT EXISTS ts_kv_latest ( | @@ -42,5 +43,6 @@ CREATE TABLE IF NOT EXISTS ts_kv_latest ( | ||
42 | str_v varchar(10000000), | 43 | str_v varchar(10000000), |
43 | long_v bigint, | 44 | long_v bigint, |
44 | dbl_v double precision, | 45 | dbl_v double precision, |
46 | + json_v json, | ||
45 | CONSTRAINT ts_kv_latest_pkey PRIMARY KEY (entity_id, key) | 47 | CONSTRAINT ts_kv_latest_pkey PRIMARY KEY (entity_id, key) |
46 | ); | 48 | ); |
@@ -24,6 +24,7 @@ CREATE TABLE IF NOT EXISTS ts_kv ( | @@ -24,6 +24,7 @@ CREATE TABLE IF NOT EXISTS ts_kv ( | ||
24 | str_v varchar(10000000), | 24 | str_v varchar(10000000), |
25 | long_v bigint, | 25 | long_v bigint, |
26 | dbl_v double precision, | 26 | dbl_v double precision, |
27 | + json_v varchar(10000000), | ||
27 | CONSTRAINT ts_kv_pkey PRIMARY KEY (entity_id, key, ts) | 28 | CONSTRAINT ts_kv_pkey PRIMARY KEY (entity_id, key, ts) |
28 | ); | 29 | ); |
29 | 30 | ||
@@ -35,6 +36,7 @@ CREATE TABLE IF NOT EXISTS ts_kv_latest ( | @@ -35,6 +36,7 @@ CREATE TABLE IF NOT EXISTS ts_kv_latest ( | ||
35 | str_v varchar(10000000), | 36 | str_v varchar(10000000), |
36 | long_v bigint, | 37 | long_v bigint, |
37 | dbl_v double precision, | 38 | dbl_v double precision, |
39 | + json_v varchar(10000000), | ||
38 | CONSTRAINT ts_kv_latest_pkey PRIMARY KEY (entity_id, key) | 40 | CONSTRAINT ts_kv_latest_pkey PRIMARY KEY (entity_id, key) |
39 | ); | 41 | ); |
40 | 42 |
@@ -21,7 +21,8 @@ CREATE TABLE IF NOT EXISTS ts_kv ( | @@ -21,7 +21,8 @@ CREATE TABLE IF NOT EXISTS ts_kv ( | ||
21 | bool_v boolean, | 21 | bool_v boolean, |
22 | str_v varchar(10000000), | 22 | str_v varchar(10000000), |
23 | long_v bigint, | 23 | long_v bigint, |
24 | - dbl_v double precision | 24 | + dbl_v double precision, |
25 | + json_v json | ||
25 | ) PARTITION BY RANGE (ts); | 26 | ) PARTITION BY RANGE (ts); |
26 | 27 | ||
27 | CREATE TABLE IF NOT EXISTS ts_kv_latest ( | 28 | CREATE TABLE IF NOT EXISTS ts_kv_latest ( |
@@ -32,6 +33,7 @@ CREATE TABLE IF NOT EXISTS ts_kv_latest ( | @@ -32,6 +33,7 @@ CREATE TABLE IF NOT EXISTS ts_kv_latest ( | ||
32 | str_v varchar(10000000), | 33 | str_v varchar(10000000), |
33 | long_v bigint, | 34 | long_v bigint, |
34 | dbl_v double precision, | 35 | dbl_v double precision, |
36 | + json_v json, | ||
35 | CONSTRAINT ts_kv_latest_pkey PRIMARY KEY (entity_id, key) | 37 | CONSTRAINT ts_kv_latest_pkey PRIMARY KEY (entity_id, key) |
36 | ); | 38 | ); |
37 | 39 |
@@ -30,7 +30,7 @@ public class JpaDaoTestSuite { | @@ -30,7 +30,7 @@ public class JpaDaoTestSuite { | ||
30 | 30 | ||
31 | @ClassRule | 31 | @ClassRule |
32 | public static CustomSqlUnit sqlUnit = new CustomSqlUnit( | 32 | public static CustomSqlUnit sqlUnit = new CustomSqlUnit( |
33 | - Arrays.asList("sql/schema-ts-hsql.sql", "sql/schema-entities.sql", "sql/system-data.sql"), | 33 | + Arrays.asList("sql/schema-ts-hsql.sql", "sql/schema-entities-hsql.sql", "sql/system-data.sql"), |
34 | "sql/drop-all-tables.sql", | 34 | "sql/drop-all-tables.sql", |
35 | "sql-test.properties" | 35 | "sql-test.properties" |
36 | ); | 36 | ); |
@@ -30,7 +30,7 @@ public class SqlDaoServiceTestSuite { | @@ -30,7 +30,7 @@ public class SqlDaoServiceTestSuite { | ||
30 | 30 | ||
31 | @ClassRule | 31 | @ClassRule |
32 | public static CustomSqlUnit sqlUnit = new CustomSqlUnit( | 32 | public static CustomSqlUnit sqlUnit = new CustomSqlUnit( |
33 | - Arrays.asList("sql/schema-ts-hsql.sql", "sql/schema-entities.sql", "sql/schema-entities-idx.sql", "sql/system-data.sql", "sql/system-test.sql"), | 33 | + Arrays.asList("sql/schema-ts-hsql.sql", "sql/schema-entities-hsql.sql", "sql/schema-entities-idx.sql", "sql/system-data.sql", "sql/system-test.sql"), |
34 | "sql/drop-all-tables.sql", | 34 | "sql/drop-all-tables.sql", |
35 | "sql-test.properties" | 35 | "sql-test.properties" |
36 | ); | 36 | ); |
@@ -21,6 +21,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; | @@ -21,6 +21,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; | ||
21 | import com.fasterxml.jackson.databind.node.ObjectNode; | 21 | import com.fasterxml.jackson.databind.node.ObjectNode; |
22 | import com.google.common.util.concurrent.Futures; | 22 | import com.google.common.util.concurrent.Futures; |
23 | import com.google.common.util.concurrent.ListenableFuture; | 23 | import com.google.common.util.concurrent.ListenableFuture; |
24 | +import com.google.gson.JsonParseException; | ||
24 | import org.apache.commons.collections.CollectionUtils; | 25 | import org.apache.commons.collections.CollectionUtils; |
25 | import org.apache.commons.lang3.BooleanUtils; | 26 | import org.apache.commons.lang3.BooleanUtils; |
26 | import org.thingsboard.rule.engine.api.TbContext; | 27 | import org.thingsboard.rule.engine.api.TbContext; |
@@ -33,6 +34,7 @@ import org.thingsboard.server.common.data.kv.KvEntry; | @@ -33,6 +34,7 @@ import org.thingsboard.server.common.data.kv.KvEntry; | ||
33 | import org.thingsboard.server.common.data.kv.TsKvEntry; | 34 | import org.thingsboard.server.common.data.kv.TsKvEntry; |
34 | import org.thingsboard.server.common.msg.TbMsg; | 35 | import org.thingsboard.server.common.msg.TbMsg; |
35 | 36 | ||
37 | +import java.io.IOException; | ||
36 | import java.util.ArrayList; | 38 | import java.util.ArrayList; |
37 | import java.util.List; | 39 | import java.util.List; |
38 | import java.util.concurrent.ConcurrentHashMap; | 40 | import java.util.concurrent.ConcurrentHashMap; |
@@ -77,7 +79,8 @@ public abstract class TbAbstractGetAttributesNode<C extends TbGetAttributesNodeC | @@ -77,7 +79,8 @@ public abstract class TbAbstractGetAttributesNode<C extends TbGetAttributesNodeC | ||
77 | } | 79 | } |
78 | 80 | ||
79 | @Override | 81 | @Override |
80 | - public void destroy() { } | 82 | + public void destroy() { |
83 | + } | ||
81 | 84 | ||
82 | protected abstract ListenableFuture<T> findEntityIdAsync(TbContext ctx, TbMsg msg); | 85 | protected abstract ListenableFuture<T> findEntityIdAsync(TbContext ctx, TbMsg msg); |
83 | 86 | ||
@@ -168,6 +171,12 @@ public abstract class TbAbstractGetAttributesNode<C extends TbGetAttributesNodeC | @@ -168,6 +171,12 @@ public abstract class TbAbstractGetAttributesNode<C extends TbGetAttributesNodeC | ||
168 | case DOUBLE: | 171 | case DOUBLE: |
169 | value.put(VALUE, r.getDoubleValue().get()); | 172 | value.put(VALUE, r.getDoubleValue().get()); |
170 | break; | 173 | break; |
174 | + case JSON: | ||
175 | + try { | ||
176 | + value.set(VALUE, mapper.readTree(r.getJsonValue().get())); | ||
177 | + } catch (IOException e) { | ||
178 | + throw new JsonParseException("Can't parse jsonValue: " + r.getJsonValue().get(), e); | ||
179 | + } | ||
171 | } | 180 | } |
172 | msg.getMetaData().putValue(r.getKey(), value.toString()); | 181 | msg.getMetaData().putValue(r.getKey(), value.toString()); |
173 | } | 182 | } |
@@ -21,6 +21,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; | @@ -21,6 +21,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; | ||
21 | import com.fasterxml.jackson.databind.node.ArrayNode; | 21 | import com.fasterxml.jackson.databind.node.ArrayNode; |
22 | import com.fasterxml.jackson.databind.node.ObjectNode; | 22 | import com.fasterxml.jackson.databind.node.ObjectNode; |
23 | import com.google.common.util.concurrent.ListenableFuture; | 23 | import com.google.common.util.concurrent.ListenableFuture; |
24 | +import com.google.gson.JsonParseException; | ||
24 | import lombok.Data; | 25 | import lombok.Data; |
25 | import lombok.NoArgsConstructor; | 26 | import lombok.NoArgsConstructor; |
26 | import lombok.extern.slf4j.Slf4j; | 27 | import lombok.extern.slf4j.Slf4j; |
@@ -39,6 +40,7 @@ import org.thingsboard.server.common.data.kv.TsKvEntry; | @@ -39,6 +40,7 @@ import org.thingsboard.server.common.data.kv.TsKvEntry; | ||
39 | import org.thingsboard.server.common.data.plugin.ComponentType; | 40 | import org.thingsboard.server.common.data.plugin.ComponentType; |
40 | import org.thingsboard.server.common.msg.TbMsg; | 41 | import org.thingsboard.server.common.msg.TbMsg; |
41 | 42 | ||
43 | +import java.io.IOException; | ||
42 | import java.util.List; | 44 | import java.util.List; |
43 | import java.util.concurrent.ExecutionException; | 45 | import java.util.concurrent.ExecutionException; |
44 | import java.util.concurrent.TimeUnit; | 46 | import java.util.concurrent.TimeUnit; |
@@ -180,6 +182,13 @@ public class TbGetTelemetryNode implements TbNode { | @@ -180,6 +182,13 @@ public class TbGetTelemetryNode implements TbNode { | ||
180 | case DOUBLE: | 182 | case DOUBLE: |
181 | obj.put("value", entry.getDoubleValue().get()); | 183 | obj.put("value", entry.getDoubleValue().get()); |
182 | break; | 184 | break; |
185 | + case JSON: | ||
186 | + try { | ||
187 | + obj.set("value", mapper.readTree(entry.getJsonValue().get())); | ||
188 | + } catch (IOException e) { | ||
189 | + throw new JsonParseException("Can't parse jsonValue: " + entry.getJsonValue().get(), e); | ||
190 | + } | ||
191 | + break; | ||
183 | } | 192 | } |
184 | return obj; | 193 | return obj; |
185 | } | 194 | } |