node-red-contrib-questdb 0.6.6 → 0.6.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -100,7 +100,7 @@
100
100
  const typeSelect = $('<select/>', {class: "column-type"})
101
101
  .css({width: "20%"})
102
102
  .appendTo(row);
103
- ['auto', 'float', 'double', 'integer', 'long', 'decimal', 'string', 'boolean', 'timestamp', 'array'].forEach(function(t) {
103
+ ['auto', 'float', 'double', 'integer', 'long', 'decimal', 'varchar', 'string', 'boolean', 'timestamp', 'array'].forEach(function(t) {
104
104
  $('<option/>').val(t).text(t).appendTo(typeSelect);
105
105
  });
106
106
  typeSelect.val(data.type || "auto");
@@ -211,7 +211,8 @@
211
211
  <li><b>integer</b> - 32-bit signed integer</li>
212
212
  <li><b>long</b> - 64-bit signed integer</li>
213
213
  <li><b>decimal</b> - Arbitrary precision decimal</li>
214
- <li><b>string</b> - Text value</li>
214
+ <li><b>varchar</b> - Variable-length text (QuestDB native type, preferred over string)</li>
215
+ <li><b>string</b> - Text value (alias for varchar)</li>
215
216
  <li><b>boolean</b> - true/false</li>
216
217
  <li><b>timestamp</b> - Date/time value</li>
217
218
  <li><b>array</b> - Array of doubles (double[])</li>
@@ -72,6 +72,7 @@ module.exports = function(RED) {
72
72
  case 'boolean':
73
73
  value = Boolean(value);
74
74
  break;
75
+ case 'varchar':
75
76
  case 'string':
76
77
  value = String(value);
77
78
  break;
@@ -11,8 +11,8 @@
11
11
  name: {value:""}
12
12
  },
13
13
  inputs:1,
14
- outputs:4,
15
- outputLabels: ["int", "float", "bool", "string"],
14
+ outputs:5,
15
+ outputLabels: ["long", "double", "bool", "string", "unresolvable"],
16
16
  icon: "font-awesome/fa-random",
17
17
  paletteLabel: "Type Router",
18
18
  label: function() { return this.name || "Type Router"; },
@@ -29,20 +29,25 @@
29
29
 
30
30
  <script type="text/html" data-help-name="questdb-type-router">
31
31
  <p>Routes messages to different outputs based on payload data type.</p>
32
+
32
33
  <h3>Outputs</h3>
33
34
  <ol class="node-ports">
34
- <li><b>Int:</b> Integer values</li>
35
- <li><b>Float:</b> Floating point values</li>
35
+ <li><b>Long:</b> Integer values (int and long both map here)</li>
36
+ <li><b>Double:</b> Floating-point values (float and double both map here)</li>
36
37
  <li><b>Bool:</b> Boolean values (including "true"/"false" strings)</li>
37
- <li><b>String:</b> String values</li>
38
+ <li><b>String:</b> String values that are not numbers or booleans</li>
39
+ <li><b>Unresolvable:</b> Values whose type cannot be determined (objects, arrays, null, NaN, Infinity)</li>
38
40
  </ol>
41
+
39
42
  <h3>Type Detection</h3>
40
43
  <ul>
41
- <li>Boolean: <code>true</code>, <code>false</code>, or strings "true"/"false"</li>
42
- <li>Integer: Whole numbers without decimal point</li>
43
- <li>Float: Numbers with decimal point</li>
44
- <li>String: Everything else (converted if needed)</li>
44
+ <li><b>Bool:</b> <code>true</code>, <code>false</code>, or strings <code>"true"</code>/<code>"false"</code> (case-insensitive)</li>
45
+ <li><b>Long:</b> Whole numbers or numeric strings without a decimal point (e.g. <code>42</code>, <code>"42"</code>)</li>
46
+ <li><b>Double:</b> Numbers with a decimal point or numeric strings with <code>.</code> (e.g. <code>3.14</code>, <code>"3.14"</code>)</li>
47
+ <li><b>String:</b> Non-numeric, non-boolean strings</li>
48
+ <li><b>Unresolvable:</b> Objects, arrays, <code>null</code>, <code>NaN</code>, <code>Infinity</code></li>
45
49
  </ul>
50
+
46
51
  <h3>Conversion</h3>
47
52
  <p>String representations of numbers and booleans are automatically converted to their native types.</p>
48
53
  </script>
@@ -1,71 +1,96 @@
1
- module.exports = function(RED) {
2
- function TypeRouterNode(config) {
3
- RED.nodes.createNode(this, config);
4
- var node = this;
1
+ module.exports = function (RED) {
2
+ function TypeRouterNode(config) {
3
+ RED.nodes.createNode(this, config);
4
+ var node = this;
5
5
 
6
- node.on('input', function(msg) {
7
- var value = msg.payload;
6
+ // Detect the canonical type from a value.
7
+ // Returns 'long' | 'double' | 'bool' | 'string' | null (unresolvable)
8
+ function detectType(value) {
9
+ if (typeof value === 'boolean') return 'bool';
10
+ if (typeof value === 'number') {
11
+ if (!isFinite(value)) return null;
12
+ return Number.isInteger(value) ? 'long' : 'double';
13
+ }
14
+ if (typeof value === 'string') {
15
+ var trimmed = value.trim();
16
+ var lower = trimmed.toLowerCase();
17
+ if (lower === 'true' || lower === 'false') return 'bool';
18
+ if (trimmed !== '' && !isNaN(trimmed)) {
19
+ return trimmed.includes('.') ? 'double' : 'long';
20
+ }
21
+ return 'string';
22
+ }
23
+ return null;
24
+ }
8
25
 
9
- if (typeof value === 'boolean') {
10
- msg.dataType = 'bool';
11
- node.status({ fill: "purple", shape: "dot", text: "bool: " + value });
12
- return node.send([null, null, msg, null]);
13
- }
14
- else if (typeof value === 'number') {
15
- if (Number.isInteger(value)) {
16
- msg.dataType = 'int';
17
- node.status({ fill: "blue", shape: "dot", text: "int: " + value });
18
- return node.send([msg, null, null, null]);
19
- } else {
20
- msg.dataType = 'float';
21
- node.status({ fill: "green", shape: "dot", text: "float: " + value.toFixed(2) });
22
- return node.send([null, msg, null, null]);
23
- }
24
- }
25
- else if (typeof value === 'string') {
26
- var trimmed = value.trim();
26
+ // Cast value to the required type.
27
+ // Returns the casted value, or null if the cast is not possible.
28
+ function castToType(value, type) {
29
+ var num, t;
30
+ if (type === 'long') {
31
+ if (typeof value === 'boolean') return null;
32
+ num = Number(value);
33
+ if (!isFinite(num) || !Number.isInteger(num)) return null;
34
+ return num;
35
+ }
36
+ if (type === 'double') {
37
+ if (typeof value === 'boolean') return null;
38
+ num = Number(value);
39
+ if (!isFinite(num)) return null;
40
+ return num;
41
+ }
42
+ if (type === 'bool') {
43
+ if (typeof value === 'boolean') return value;
44
+ if (typeof value === 'string') {
45
+ t = value.trim().toLowerCase();
46
+ if (t === 'true') return true;
47
+ if (t === 'false') return false;
48
+ }
49
+ return null;
50
+ }
51
+ if (type === 'string') {
52
+ if (typeof value === 'string') return value;
53
+ return null;
54
+ }
55
+ return null;
56
+ }
27
57
 
28
- if (trimmed.toLowerCase() === 'true') {
29
- msg.payload = true;
30
- msg.dataType = 'bool';
31
- node.status({ fill: "purple", shape: "dot", text: "bool: true" });
32
- return node.send([null, null, msg, null]);
33
- }
34
- else if (trimmed.toLowerCase() === 'false') {
35
- msg.payload = false;
36
- msg.dataType = 'bool';
37
- node.status({ fill: "purple", shape: "dot", text: "bool: false" });
38
- return node.send([null, null, msg, null]);
39
- }
40
- else if (trimmed !== '' && !isNaN(trimmed)) {
41
- var num = Number(trimmed);
42
- msg.payload = num;
43
- if (Number.isInteger(num) && !trimmed.includes('.')) {
44
- msg.dataType = 'int';
45
- node.status({ fill: "blue", shape: "dot", text: "int: " + num });
46
- return node.send([msg, null, null, null]);
47
- } else {
48
- msg.dataType = 'float';
49
- node.status({ fill: "green", shape: "dot", text: "float: " + num.toFixed(2) });
50
- return node.send([null, msg, null, null]);
51
- }
52
- }
53
- else {
54
- msg.payload = String(value);
55
- msg.dataType = 'string';
56
- node.status({ fill: "yellow", shape: "dot", text: "str: " + (value.length > 10 ? value.substring(0,10) + "..." : value) });
57
- return node.send([null, null, null, msg]);
58
- }
59
- }
60
- else {
61
- msg.payload = String(value);
62
- msg.dataType = 'string';
63
- node.status({ fill: "yellow", shape: "dot", text: "str: (converted)" });
64
- return node.send([null, null, null, msg]);
65
- }
66
- });
58
+ var TYPE_OUTPUT = { long: 0, double: 1, bool: 2, string: 3 };
59
+ var TYPE_COLOR = { long: 'blue', double: 'green', bool: 'purple', string: 'yellow' };
67
60
 
68
- node.on('close', function() { node.status({}); });
69
- }
70
- RED.nodes.registerType("questdb-type-router", TypeRouterNode);
61
+ node.on('input', function (msg) {
62
+ var value = msg.payload;
63
+
64
+ var type = detectType(value);
65
+ if (type === null) {
66
+ node.status({ fill: 'red', shape: 'ring', text: 'unresolvable' });
67
+ return node.send([null, null, null, null, msg]);
68
+ }
69
+
70
+ var castedValue = castToType(value, type);
71
+ if (castedValue === null) {
72
+ node.status({ fill: 'red', shape: 'ring', text: 'cast error' });
73
+ return node.send([null, null, null, null, msg]);
74
+ }
75
+
76
+ msg.payload = castedValue;
77
+ msg.dataType = type;
78
+
79
+ var outputIndex = TYPE_OUTPUT[type];
80
+ var outputs = [null, null, null, null, null];
81
+ outputs[outputIndex] = msg;
82
+
83
+ var label = String(castedValue);
84
+ if (label.length > 12) label = label.substring(0, 12) + '...';
85
+ node.status({ fill: TYPE_COLOR[type], shape: 'dot', text: type + ': ' + label });
86
+
87
+ return node.send(outputs);
88
+ });
89
+
90
+ node.on('close', function () {
91
+ node.status({});
92
+ });
93
+ }
94
+
95
+ RED.nodes.registerType('questdb-type-router', TypeRouterNode);
71
96
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-red-contrib-questdb",
3
- "version": "0.6.6",
3
+ "version": "0.6.9",
4
4
  "description": "Node-RED nodes for writing high-performance time-series data to QuestDB using Influx Line Protocol (ILP). Supports IoT, industrial monitoring, smart buildings, fleet telematics, healthcare, agriculture, and more.",
5
5
  "author": {
6
6
  "name": "Holger Amort"