node-red-contrib-prib-functions 0.23.3 → 0.26.0
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.
- package/.github/copilot-instructions.md +36 -0
- package/README.md +15 -0
- package/columnar/columnar.html +258 -0
- package/columnar/columnar.js +1055 -0
- package/columnar/icons/columnar.svg +38 -0
- package/fileSystem/filesystem.html +299 -0
- package/fileSystem/filesystem.js +170 -0
- package/gitlab/gitlab.html +191 -0
- package/gitlab/gitlab.js +248 -0
- package/gitlab/icons/gitlab.svg +17 -0
- package/lib/typedInput.js +18 -2
- package/logisticRegression/icons/logisticregression.svg +22 -0
- package/logisticRegression/logisticRegression.html +136 -0
- package/logisticRegression/logisticRegression.js +83 -0
- package/package.json +19 -8
- package/test/columnar.js +509 -0
- package/test/data/.config.nodes.json +114 -70
- package/test/data/.config.nodes.json.backup +104 -71
- package/test/data/.config.runtime.json +2 -1
- package/test/data/.config.runtime.json.backup +2 -1
- package/test/data/.config.users.json +3 -2
- package/test/data/.config.users.json.backup +3 -2
- package/test/data/.flow.json.backup +561 -5
- package/test/data/flow.json +571 -2
- package/test/data/package-lock.json +1 -1
- package/test/data/shares/.config.nodes.json +74 -52
- package/test/data/shares/.config.nodes.json.backup +589 -0
- package/test/data/shares/.config.runtime.json +2 -1
- package/test/data/shares/.config.runtime.json.backup +2 -1
- package/test/data/shares/.config.users.json +3 -2
- package/test/data/shares/.config.users.json.backup +5 -1
- package/test/dataAnalysisExtensions.js +93 -93
- package/test/logisticRegression.js +379 -0
- package/test/transform.js +11 -11
- package/test/transformConfluence.js +4 -2
- package/test/transformNumPy.js +3 -1
- package/test/transformXLSX.js +4 -2
- package/test/transformXML.js +4 -2
- package/test-runner.js +400 -0
- package/test.parq +0 -0
- package/test_select.js +37 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Node-RED Contrib Functions
|
|
2
|
+
|
|
3
|
+
## Project Overview
|
|
4
|
+
This is a Node-RED contrib package providing custom nodes for data analysis, matrix operations, transformations, monitoring, and utilities. Nodes process messages in real-time, supporting statistical metrics, data transformations, and system monitoring.
|
|
5
|
+
|
|
6
|
+
## Architecture
|
|
7
|
+
- **Node Structure**: Each node resides in its own directory containing:
|
|
8
|
+
- `.js`: Backend logic using Node-RED's API (e.g., `dataAnalysis/dataAnalysis.js`)
|
|
9
|
+
- `.html`: Frontend configuration UI with typed inputs (e.g., `dataAnalysis/dataAnalysis.html`)
|
|
10
|
+
- `icons/`: Custom node icons
|
|
11
|
+
- **Shared Libraries**: `lib/` holds common utilities like `common.js` (dynamic property evaluation), `objectExtensions.js` (array extensions), and domain-specific modules
|
|
12
|
+
- **Data Flow**: Nodes typically input `msg.payload`, output results to multiple ports (primary results, details/stats, outliers)
|
|
13
|
+
|
|
14
|
+
## Key Patterns
|
|
15
|
+
- **Node Registration**: Follow `RED.nodes.registerType('NodeName', function(config) { this.on('input', (msg) => { ... }); })`
|
|
16
|
+
- **Configuration Properties**: Use defaults like `dataProperty: {value:"msg.payload"}`, leverage typedInput for dynamic paths
|
|
17
|
+
- **Error Handling**: Call `node.error(message)` and update status: `node.status({fill:"red",shape:"ring",text:"shortMsg"})`
|
|
18
|
+
- **Logging**: Instantiate `const logger = new (require("node-red-contrib-logger"))("NodeName")`; use `logger.sendInfo()` for debugging
|
|
19
|
+
- **Real-time Accumulation**: Store data in `node.dataPoint[key]` objects, recalculate metrics on each input (e.g., running averages, deltas)
|
|
20
|
+
- **Dynamic Evaluation**: Use `evalInFunction(node, 'propertyName')` from `lib/common.js` to access msg/flow/global properties safely
|
|
21
|
+
- **Outlier Detection**: For realtime nodes, check against mean/median ± N standard deviations, output to third port
|
|
22
|
+
|
|
23
|
+
## Development Workflow
|
|
24
|
+
- **Testing**: Run `npm test` (mocha on `test/dataAnalysisE*.js`); use `node-red-node-test-helper` for node testing
|
|
25
|
+
- **Local Development**: `npm run startNodeRed` launches test Node-RED instance with flows in `test/data/`
|
|
26
|
+
- **Dependencies**: `npm ci` installs; requires Node >=22.15.0, Node-RED >=4.0.9; external libs like `avsc` (AVRO), `xlsx` (Excel), `fast-xml-parser` (XML)
|
|
27
|
+
|
|
28
|
+
## Examples
|
|
29
|
+
- **Data Analysis**: Accumulates arrays/objects, computes stats (avg, stddev, autocorrelation); realtime mode detects outliers beyond 3σ
|
|
30
|
+
- **Transform**: Converts formats (JSON↔CSV↔XLSX↔AVRO) using dedicated libraries; supports path operations and string manipulations
|
|
31
|
+
- **Matrix**: Performs linear algebra (inverse, determinant, Gaussian elimination) on 2D arrays
|
|
32
|
+
|
|
33
|
+
## Deployment
|
|
34
|
+
- Automated npm publish on GitHub release via `.github/workflows/npmpublish.yml`
|
|
35
|
+
- Set `NODE_AUTH_TOKEN` secret for publishing</content>
|
|
36
|
+
<parameter name="filePath">c:\Users\peter\repros\node-red-contrib-prib-functions\.github\copilot-instructions.md
|
package/README.md
CHANGED
|
@@ -75,6 +75,17 @@ example:
|
|
|
75
75
|
|
|
76
76
|
------------------------------------------------------------
|
|
77
77
|
|
|
78
|
+
## logistic regression
|
|
79
|
+
|
|
80
|
+
Perform logistic regression by setting up model with fit then using the model in other nodes to do prediction probability or class
|
|
81
|
+
------------------------------------------------------------
|
|
82
|
+
|
|
83
|
+
## columnar store
|
|
84
|
+
|
|
85
|
+
Store data into a columnar store with sql select access
|
|
86
|
+
|
|
87
|
+
------------------------------------------------------------
|
|
88
|
+
|
|
78
89
|
## Matrix
|
|
79
90
|
|
|
80
91
|
Define a matrix and perform various functions
|
|
@@ -352,6 +363,10 @@ Test/example flow in test/generalTest.json
|
|
|
352
363
|
|
|
353
364
|
## Version
|
|
354
365
|
|
|
366
|
+
0.26.0 add columnar store node with sql query capabilty
|
|
367
|
+
|
|
368
|
+
0.24.0 add in logistic regression
|
|
369
|
+
|
|
355
370
|
0.23.3 Removes bug in test, more translation
|
|
356
371
|
|
|
357
372
|
0.23.0 Removes bug in test, more translation
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
<script type="text/javascript">
|
|
2
|
+
RED.nodes.registerType('columnar', {
|
|
3
|
+
category: 'storage',
|
|
4
|
+
color: '#8BC34A',
|
|
5
|
+
defaults: {
|
|
6
|
+
name: {value:"", required:false},
|
|
7
|
+
action: {value:"readFile", required:true},
|
|
8
|
+
filePath: {value:"", required:false},
|
|
9
|
+
sqlQuery: {value:"", required:false},
|
|
10
|
+
outputProperty: {value:"result", required:false},
|
|
11
|
+
parameterMappings: {value:[], required:false}
|
|
12
|
+
},
|
|
13
|
+
inputs: 1,
|
|
14
|
+
inputLabels: "in",
|
|
15
|
+
outputs:1,
|
|
16
|
+
outputLabels: function() { return [this.outputProperty || "result"]; },
|
|
17
|
+
paletteLabel: "Columnar Store",
|
|
18
|
+
icon: "columnar.svg",
|
|
19
|
+
label: function() {
|
|
20
|
+
const actionLabels = {
|
|
21
|
+
readFile: "Read",
|
|
22
|
+
writeFile: "Write",
|
|
23
|
+
appendFile: "Append",
|
|
24
|
+
queryFile: "Query",
|
|
25
|
+
sqlQuery: "SQL Query",
|
|
26
|
+
getSchema: "Get Schema",
|
|
27
|
+
getMetadata: "Get Metadata"
|
|
28
|
+
};
|
|
29
|
+
const actionLabel = actionLabels[this.action] || this.action;
|
|
30
|
+
|
|
31
|
+
if (this.name) {
|
|
32
|
+
return this.name;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (this.action === 'sqlQuery' && this.sqlQuery) {
|
|
36
|
+
// Show truncated SQL query in label
|
|
37
|
+
const sqlPreview = this.sqlQuery.length > 30 ?
|
|
38
|
+
this.sqlQuery.substring(0, 27) + '...' :
|
|
39
|
+
this.sqlQuery;
|
|
40
|
+
return sqlPreview;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return actionLabel + " Columnar Store";
|
|
44
|
+
},
|
|
45
|
+
labelStyle: function() {
|
|
46
|
+
return "node_label_italic";
|
|
47
|
+
},
|
|
48
|
+
oneditprepare: function() {
|
|
49
|
+
// Show/hide SQL query field based on action
|
|
50
|
+
function updateSqlField() {
|
|
51
|
+
const action = $('#node-input-action').val();
|
|
52
|
+
if (action === 'sqlQuery') {
|
|
53
|
+
$('#sql-query-row').show();
|
|
54
|
+
} else {
|
|
55
|
+
$('#sql-query-row').hide();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
$('#node-input-action').change(updateSqlField);
|
|
60
|
+
updateSqlField(); // Initial check
|
|
61
|
+
|
|
62
|
+
},
|
|
63
|
+
oneditsave: function() {
|
|
64
|
+
// No parameter mappings needed; explicit :msg., :flow., :global. markers only
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
</script>
|
|
68
|
+
|
|
69
|
+
<script type="text/x-red" data-template-name="columnar">
|
|
70
|
+
<div class="form-row">
|
|
71
|
+
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
72
|
+
<input type="text" id="node-input-name" placeholder="Name">
|
|
73
|
+
</div>
|
|
74
|
+
<div class="form-row">
|
|
75
|
+
<label for="node-input-action"><i class="fa fa-cogs"></i> Action</label>
|
|
76
|
+
<select type="text" id="node-input-action">
|
|
77
|
+
<option value="readFile">Read File</option>
|
|
78
|
+
<option value="writeFile">Write File</option>
|
|
79
|
+
<option value="appendFile">Append File</option>
|
|
80
|
+
<option value="queryFile">Query File</option>
|
|
81
|
+
<option value="sqlQuery">SQL Query</option>
|
|
82
|
+
<option value="getSchema">Get Schema</option>
|
|
83
|
+
<option value="getMetadata">Get Metadata</option>
|
|
84
|
+
</select>
|
|
85
|
+
</div>
|
|
86
|
+
<div class="form-row">
|
|
87
|
+
<label for="node-input-filePath">File Path</label>
|
|
88
|
+
<input type="text" id="node-input-filePath" placeholder="/path/to/file.columnar">
|
|
89
|
+
</div>
|
|
90
|
+
<div class="form-row">
|
|
91
|
+
<label for="node-input-outputProperty"><i class="fa fa-sign-out"></i> Output Property</label>
|
|
92
|
+
<input type="text" id="node-input-outputProperty" placeholder="result">
|
|
93
|
+
</div>
|
|
94
|
+
<div class="form-row" id="sql-query-row" style="display:none;">
|
|
95
|
+
<label for="node-input-sqlQuery"><i class="fa fa-code"></i> SQL Query</label>
|
|
96
|
+
<textarea type="text" id="node-input-sqlQuery" placeholder="SELECT * FROM ? WHERE ..." rows="3" style="width:100%; resize:vertical;"></textarea>
|
|
97
|
+
</div>
|
|
98
|
+
</script>
|
|
99
|
+
|
|
100
|
+
<script type="text/x-red" data-help-name="columnar">
|
|
101
|
+
<p>Columnar Store node for reading and writing columnar data files. This implementation is based on <a href="https://github.com/apache/parquet-format/" target="_blank">Apache Parquet specification</a> concepts including columnar storage, metadata structures, and compression principles, but provides a simplified JavaScript implementation for basic use cases.</p>
|
|
102
|
+
|
|
103
|
+
<h3>Actions</h3>
|
|
104
|
+
<ul>
|
|
105
|
+
<li><b>Read File:</b> Reads all records from a Columnar Store file</li>
|
|
106
|
+
<li><b>Write File:</b> Writes records to a Columnar Store file (overwrites existing file)</li>
|
|
107
|
+
<li><b>Append File:</b> Appends records to an existing Columnar Store file (creates file if it doesn't exist)</li>
|
|
108
|
+
<li><b>Query File:</b> Reads and filters records from a Columnar Store file</li>
|
|
109
|
+
<li><b>SQL Query:</b> Execute SQL queries with aggregates, grouping, sorting, JOINs, and filtering</li>
|
|
110
|
+
<li><b>Get Schema:</b> Retrieves the schema information from a Columnar Store file</li>
|
|
111
|
+
<li><b>Get Metadata:</b> Retrieves metadata information from a Columnar Store file</li>
|
|
112
|
+
</ul>
|
|
113
|
+
|
|
114
|
+
<h3>Configuration</h3>
|
|
115
|
+
<ul>
|
|
116
|
+
<li><b>File Path:</b> Path to the Columnar file (.columnar extension). Can be overridden in msg.payload.filePath</li>
|
|
117
|
+
<li><b>Output Property:</b> Property name on msg object where results will be stored (default: "result")</li>
|
|
118
|
+
<li><b>SQL Query:</b> (SQL Query action only) Hardcoded SQL query to execute. Can be overridden by msg.payload.sql. Hardcoded queries are parsed once and cached for better performance.</li>
|
|
119
|
+
<li><b>Parameters:</b> <b>Only</b> <code>:msg.path</code>, <code>:flow.path</code>, or <code>:global.path</code> markers are allowed in SQL. Example: <code>WHERE a.name = :msg.payload.name AND a.age > :flow.minAge</code>. No other parameter markers are supported.</li>
|
|
120
|
+
</ul>
|
|
121
|
+
|
|
122
|
+
<h3>Input/Output Formats</h3>
|
|
123
|
+
|
|
124
|
+
<h4>Read File</h4>
|
|
125
|
+
<p><b>Input:</b> Optional filePath in msg.payload</p>
|
|
126
|
+
<p><b>Output:</b></p>
|
|
127
|
+
<pre>{
|
|
128
|
+
"result": {
|
|
129
|
+
"records": [...],
|
|
130
|
+
"count": 100,
|
|
131
|
+
"schema": {...},
|
|
132
|
+
"filePath": "/path/to/file.columnar"
|
|
133
|
+
}
|
|
134
|
+
}</pre>
|
|
135
|
+
|
|
136
|
+
<h4>Write File</h4>
|
|
137
|
+
<p><b>Input:</b></p>
|
|
138
|
+
<pre>{
|
|
139
|
+
"records": [
|
|
140
|
+
{"name": "John", "age": 30, "city": "NYC"},
|
|
141
|
+
{"name": "Jane", "age": 25, "city": "LA"}
|
|
142
|
+
],
|
|
143
|
+
"filePath": "/path/to/output.columnar",
|
|
144
|
+
"schema": {...} // optional, auto-inferred if not provided
|
|
145
|
+
}</pre>
|
|
146
|
+
<p><b>Output:</b></p>
|
|
147
|
+
<pre>{
|
|
148
|
+
"result": {
|
|
149
|
+
"filePath": "/path/to/output.columnar",
|
|
150
|
+
"recordsWritten": 2,
|
|
151
|
+
"schema": {...}
|
|
152
|
+
}
|
|
153
|
+
}</pre>
|
|
154
|
+
|
|
155
|
+
<h4>Query File</h4>
|
|
156
|
+
<p><b>Input:</b></p>
|
|
157
|
+
<pre>{
|
|
158
|
+
"filePath": "/path/to/file.columnar",
|
|
159
|
+
"limit": 100, // optional
|
|
160
|
+
// either provide a javascript filter function or a ridMap to perform
|
|
161
|
+
// a column‑based intersection. The columnar layout stores each field
|
|
162
|
+
// as its own buffer along with a list of row‑ids (rids). A ridMap is an
|
|
163
|
+
// object whose keys are column names and whose values are arrays or
|
|
164
|
+
// Sets of rids; only rows present in *every* set will be returned.
|
|
165
|
+
// Example using a filter function:
|
|
166
|
+
"filter": "function(record) { return record.age > 25; }" // optional
|
|
167
|
+
// Example using a ridMap (select rows 1 and 2 from the 'age' column):
|
|
168
|
+
// "ridMap": {"age": [1,2]}
|
|
169
|
+
}</pre>
|
|
170
|
+
<p><b>Output:</b></p>
|
|
171
|
+
<pre>{
|
|
172
|
+
"result": {
|
|
173
|
+
"records": [...],
|
|
174
|
+
"count": 50,
|
|
175
|
+
"totalScanned": 100,
|
|
176
|
+
"schema": {...},
|
|
177
|
+
"filtered": true
|
|
178
|
+
}
|
|
179
|
+
}</pre>
|
|
180
|
+
|
|
181
|
+
<h4>SQL Query</h4>
|
|
182
|
+
<p><b>Input:</b> SQL query can be configured in the node or provided in msg.payload.sql (msg.payload.sql takes precedence)</p>
|
|
183
|
+
<pre>{
|
|
184
|
+
"filePath": "/path/to/file.columnar",
|
|
185
|
+
"sql": "SELECT * FROM ? WHERE a.name = :msg.payload.name AND a.age > :flow.minAge"
|
|
186
|
+
}</pre>
|
|
187
|
+
<p><b>Parameters:</b> Use <code>:msg.path</code>, <code>:flow.path</code>, or <code>:global.path</code> in your SQL to reference values from <code>msg</code>, <code>flow</code>, or <code>global</code> context. <b>Other parameter markers are not supported and will cause an error.</b></p>
|
|
188
|
+
<p><b>Supported Features:</b></p>
|
|
189
|
+
<ul>
|
|
190
|
+
<li><b>SELECT:</b> Column names, <code>*</code>, or aggregates (COUNT, SUM, AVG, MIN, MAX, COUNT DISTINCT)</li>
|
|
191
|
+
<li><b>FROM/JOIN:</b> Main table (<code>?</code>) with optional INNER/LEFT/RIGHT/FULL OUTER JOIN on other .columnar files</li>
|
|
192
|
+
<li><b>WHERE:</b> Filter records using JavaScript expressions</li>
|
|
193
|
+
<li><b>GROUP BY:</b> Group results by one or more columns with aggregate functions</li>
|
|
194
|
+
<li><b>HAVING:</b> Filter grouped results</li>
|
|
195
|
+
<li><b>ORDER BY:</b> Sort by column(s) with ASC/DESC</li>
|
|
196
|
+
<li><b>LIMIT:</b> Restrict result count</li>
|
|
197
|
+
<li><b>Parameters:</b> Use <code>:msg.path</code>, <code>:flow.path</code>, or <code>:global.path</code> in queries. No other parameter markers are allowed.</li>
|
|
198
|
+
</ul>
|
|
199
|
+
<p><b>JOIN Syntax:</b></p>
|
|
200
|
+
<pre>[INNER|LEFT|RIGHT|FULL OUTER] JOIN 'filepath.columnar' ON table1.col = table2.col</pre>
|
|
201
|
+
<p><b>Output:</b></p>
|
|
202
|
+
<pre>{
|
|
203
|
+
"result": [ /* array of result rows */ ]
|
|
204
|
+
}</pre>
|
|
205
|
+
|
|
206
|
+
<h4>Get Schema</h4>
|
|
207
|
+
<p><b>Input:</b> Optional filePath in msg.payload</p>
|
|
208
|
+
<p><b>Output:</b></p>
|
|
209
|
+
<pre>{
|
|
210
|
+
"result": {
|
|
211
|
+
"schema": {...},
|
|
212
|
+
"fields": ["name", "age", "city"],
|
|
213
|
+
"filePath": "/path/to/file.columnar"
|
|
214
|
+
}
|
|
215
|
+
}</pre>
|
|
216
|
+
|
|
217
|
+
<h4>Get Metadata</h4>
|
|
218
|
+
<p><b>Input:</b> Optional filePath in msg.payload</p>
|
|
219
|
+
<p><b>Output:</b></p>
|
|
220
|
+
<pre>{
|
|
221
|
+
"result": {
|
|
222
|
+
"metadata": {...},
|
|
223
|
+
"filePath": "/path/to/file.columnar",
|
|
224
|
+
"numRows": 1000,
|
|
225
|
+
"numRowGroups": 5
|
|
226
|
+
}
|
|
227
|
+
}</pre>
|
|
228
|
+
|
|
229
|
+
<h3>Data Types</h3>
|
|
230
|
+
<p>Columnar supports various data types. The node automatically infers types when writing:</p>
|
|
231
|
+
<ul>
|
|
232
|
+
<li>String → UTF8</li>
|
|
233
|
+
<li>Number → DOUBLE</li>
|
|
234
|
+
<li>Boolean → BOOLEAN</li>
|
|
235
|
+
<li>Date → TIMESTAMP</li>
|
|
236
|
+
</ul>
|
|
237
|
+
|
|
238
|
+
<h3>Implementation Notes</h3>
|
|
239
|
+
<ul>
|
|
240
|
+
<li>Based on <a href="https://github.com/apache/parquet-format/" target="_blank">Apache Parquet specification</a> concepts</li>
|
|
241
|
+
<li>Implements columnar storage for efficient data organization</li>
|
|
242
|
+
<li>Uses metadata structures inspired by Parquet format specification</li>
|
|
243
|
+
<li>Applies compression principles from the Parquet specification</li>
|
|
244
|
+
<li>Custom JavaScript implementation, not a full Apache Parquet compatible format</li>
|
|
245
|
+
<li>Best suited for basic tabular data storage and retrieval</li>
|
|
246
|
+
<li>Supports filtering via row‑id (rid) maps; query operations on
|
|
247
|
+
multiple columns efficiently intersect the provided rid sets</li>
|
|
248
|
+
</ul>
|
|
249
|
+
|
|
250
|
+
<h3>Error Handling</h3>
|
|
251
|
+
<p>Errors are reported in msg.error. Common issues:</p>
|
|
252
|
+
<ul>
|
|
253
|
+
<li>File not found</li>
|
|
254
|
+
<li>Invalid Columnar format</li>
|
|
255
|
+
<li>Schema mismatches</li>
|
|
256
|
+
<li>Permission issues</li>
|
|
257
|
+
</ul>
|
|
258
|
+
</script>
|