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.
Files changed (41) hide show
  1. package/.github/copilot-instructions.md +36 -0
  2. package/README.md +15 -0
  3. package/columnar/columnar.html +258 -0
  4. package/columnar/columnar.js +1055 -0
  5. package/columnar/icons/columnar.svg +38 -0
  6. package/fileSystem/filesystem.html +299 -0
  7. package/fileSystem/filesystem.js +170 -0
  8. package/gitlab/gitlab.html +191 -0
  9. package/gitlab/gitlab.js +248 -0
  10. package/gitlab/icons/gitlab.svg +17 -0
  11. package/lib/typedInput.js +18 -2
  12. package/logisticRegression/icons/logisticregression.svg +22 -0
  13. package/logisticRegression/logisticRegression.html +136 -0
  14. package/logisticRegression/logisticRegression.js +83 -0
  15. package/package.json +19 -8
  16. package/test/columnar.js +509 -0
  17. package/test/data/.config.nodes.json +114 -70
  18. package/test/data/.config.nodes.json.backup +104 -71
  19. package/test/data/.config.runtime.json +2 -1
  20. package/test/data/.config.runtime.json.backup +2 -1
  21. package/test/data/.config.users.json +3 -2
  22. package/test/data/.config.users.json.backup +3 -2
  23. package/test/data/.flow.json.backup +561 -5
  24. package/test/data/flow.json +571 -2
  25. package/test/data/package-lock.json +1 -1
  26. package/test/data/shares/.config.nodes.json +74 -52
  27. package/test/data/shares/.config.nodes.json.backup +589 -0
  28. package/test/data/shares/.config.runtime.json +2 -1
  29. package/test/data/shares/.config.runtime.json.backup +2 -1
  30. package/test/data/shares/.config.users.json +3 -2
  31. package/test/data/shares/.config.users.json.backup +5 -1
  32. package/test/dataAnalysisExtensions.js +93 -93
  33. package/test/logisticRegression.js +379 -0
  34. package/test/transform.js +11 -11
  35. package/test/transformConfluence.js +4 -2
  36. package/test/transformNumPy.js +3 -1
  37. package/test/transformXLSX.js +4 -2
  38. package/test/transformXML.js +4 -2
  39. package/test-runner.js +400 -0
  40. package/test.parq +0 -0
  41. 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>