node-red-contrib-prib-functions 0.23.2 → 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 +153 -140
- 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/AlphaBeta.js +32 -0
- package/lib/GraphDB.js +40 -9
- package/lib/MinMax.js +17 -0
- package/lib/Tree.js +64 -0
- package/lib/objectExtensions.js +28 -5
- package/lib/timeDimension.js +36 -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 +21 -9
- package/test/02-graphdb.js +46 -0
- 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 +1545 -369
- package/test/data/flow.json +1457 -270
- package/test/data/package-lock.json +11 -11
- package/test/data/shares/.config.nodes.json +611 -0
- package/test/data/shares/.config.nodes.json.backup +589 -0
- package/test/data/shares/.config.runtime.json +5 -0
- package/test/data/shares/.config.runtime.json.backup +4 -0
- package/test/data/shares/.config.users.json +33 -0
- package/test/data/shares/.config.users.json.backup +33 -0
- package/test/data/shares/.flow.json.backup +230 -0
- package/test/data/shares/.flow_cred.json.backup +3 -0
- package/test/data/shares/flow.json +267 -0
- package/test/data/shares/flow_cred.json +3 -0
- package/test/data/shares/package.json +6 -0
- package/test/data/shares/settings.js +544 -0
- 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
- package/testing/test.js +8 -7
- package/transform/transform.html +23 -2
- package/transform/transform.js +239 -283
- package/transform/xlsx2.js +74 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const timePeriod={second:1000}
|
|
2
|
+
timePeriod.minute=timePeriod.second*60
|
|
3
|
+
timePeriod.hour=timePeriod.minute*60
|
|
4
|
+
timePeriod.day=timePeriod.hour*24
|
|
5
|
+
timePeriod.week=timePeriod.day*7
|
|
6
|
+
timePeriod.fortnight=timePeriod.week*2
|
|
7
|
+
|
|
8
|
+
const isPeriod={
|
|
9
|
+
second:t=>t%timePeriod.second,
|
|
10
|
+
minute:t=>t%timePeriod.minute
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
const timeDimension=(period=timePeriod.second,from=0,to=60,call)=>{
|
|
16
|
+
const fromDate=new Date(from).getMilliseconds%period
|
|
17
|
+
const toDate=new Date(to).getMilliseconds%increment
|
|
18
|
+
const periods=Object.keys(callFunctions)
|
|
19
|
+
for(let i=fromDate;i<toDate;i+increment){
|
|
20
|
+
call(i)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const repeat=(r,call)=>{for(let i=0;i<r; i++) call(i)}
|
|
25
|
+
const secondsInMinute=call=>repeat(60,call)
|
|
26
|
+
const minutesInHour=repeat(60,call)
|
|
27
|
+
const hoursInDay=repeat(24,call)
|
|
28
|
+
const daysInWeek=repeat(7,call)
|
|
29
|
+
|
|
30
|
+
module.exports={
|
|
31
|
+
repeat:repeat,
|
|
32
|
+
secondsInMinute:secondsInMinute,
|
|
33
|
+
minutesInHour:minutesInHour,
|
|
34
|
+
hoursInDay:hoursInDay,
|
|
35
|
+
daysInWeek:daysInWeek
|
|
36
|
+
}
|
package/lib/typedInput.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const { get } = require("http");
|
|
1
2
|
|
|
2
3
|
const setGetFunction=(RED,node,propertyName)=>node["get"+propertyName[0].toUpperCase() + propertyName.slice(1)]=getFunction(RED,node,propertyName)
|
|
3
4
|
|
|
@@ -69,9 +70,24 @@ const setFunction=(RED,node,name)=>{
|
|
|
69
70
|
|
|
70
71
|
const getValue=(RED,node,propertyName,defaultValue)=>propertyName in node?getFunction(RED,node,propertyName)():defaultValue
|
|
71
72
|
|
|
73
|
+
const getArgFunctions=(RED,node,msg,propertyNames)=>{
|
|
74
|
+
const args=[]
|
|
75
|
+
propertyNames.forEach(property => {
|
|
76
|
+
args.push(node["get"+property])
|
|
77
|
+
});
|
|
78
|
+
return args
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const getArgs=(RED,node,msg,propertyNames,argFunctions=getArgFunctions(RED,node,propertyNames))=>
|
|
82
|
+
argFunctions.reduce((args,propertyFunction)=>propertyFunction(RED,node,msg),[])
|
|
83
|
+
const getArgsFromFunctions=(RED,node,msg,argFunctions)=>argFunctions.reduce((args,propertyFunction)=>propertyFunction(RED,node,msg),[])
|
|
84
|
+
|
|
72
85
|
module.exports={
|
|
86
|
+
getArgs:getArgs,
|
|
87
|
+
getArgsFromFunctions:getArgsFromFunctions,
|
|
88
|
+
getArgFunctions:getArgFunctions,
|
|
73
89
|
getFunction:getFunction,
|
|
90
|
+
getValue:getValue,
|
|
74
91
|
setFunction:setFunction,
|
|
75
|
-
setGetFunction:setGetFunction
|
|
76
|
-
getValue:getValue
|
|
92
|
+
setGetFunction:setGetFunction
|
|
77
93
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
|
2
|
+
<!-- Background -->
|
|
3
|
+
<rect width="32" height="32" fill="none"/>
|
|
4
|
+
|
|
5
|
+
<!-- Axes -->
|
|
6
|
+
<line x1="4" y1="24" x2="28" y2="24" stroke="#666" stroke-width="1"/>
|
|
7
|
+
<line x1="4" y1="4" x2="4" y2="24" stroke="#666" stroke-width="1"/>
|
|
8
|
+
|
|
9
|
+
<!-- Sigmoid curve (logistic function) -->
|
|
10
|
+
<path d="M 4 22 Q 8 20, 12 18 Q 16 14, 16 12 Q 16 10, 20 6 Q 24 4, 28 4"
|
|
11
|
+
fill="none" stroke="#0066cc" stroke-width="2" stroke-linecap="round"/>
|
|
12
|
+
|
|
13
|
+
<!-- Data points -->
|
|
14
|
+
<circle cx="8" cy="20" r="1.5" fill="#ff6600"/>
|
|
15
|
+
<circle cx="12" cy="16" r="1.5" fill="#ff6600"/>
|
|
16
|
+
<circle cx="16" cy="12" r="1.5" fill="#ff6600"/>
|
|
17
|
+
<circle cx="20" cy="8" r="1.5" fill="#ff6600"/>
|
|
18
|
+
<circle cx="24" cy="5" r="1.5" fill="#ff6600"/>
|
|
19
|
+
|
|
20
|
+
<!-- Horizontal dashed line at 0.5 threshold -->
|
|
21
|
+
<line x1="4" y1="14" x2="28" y2="14" stroke="#999" stroke-width="0.5" stroke-dasharray="2,2"/>
|
|
22
|
+
</svg>
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
<script type="text/javascript">
|
|
2
|
+
RED.nodes.registerType('logisticRegression', {
|
|
3
|
+
category: 'function',
|
|
4
|
+
color: '#a6bbcf',
|
|
5
|
+
defaults: {
|
|
6
|
+
name: {value:"", required:false},
|
|
7
|
+
modelName: {value:"", required:true},
|
|
8
|
+
action: {value:"predict", required:true},
|
|
9
|
+
learningRate: {value:0.1, required:false},
|
|
10
|
+
iterations: {value:2000, required:false},
|
|
11
|
+
fitIntercept: {value:true, required:false},
|
|
12
|
+
l2: {value:0.0, required:false},
|
|
13
|
+
tolerance: {value:1e-7, required:false},
|
|
14
|
+
verbose: {value:false, required:false},
|
|
15
|
+
threshold: {value:0.5, required:false}
|
|
16
|
+
},
|
|
17
|
+
inputs: 1,
|
|
18
|
+
inputLabels: "in",
|
|
19
|
+
outputs:1,
|
|
20
|
+
outputLabels: ["Result"],
|
|
21
|
+
paletteLabel: "Logistic Regression",
|
|
22
|
+
icon: "logisticregression.svg",
|
|
23
|
+
label: function() {
|
|
24
|
+
return this.name || (this.action + " Logistic Regression");
|
|
25
|
+
},
|
|
26
|
+
labelStyle: function() {
|
|
27
|
+
return "node_label_italic";
|
|
28
|
+
},
|
|
29
|
+
oneditprepare: function() {
|
|
30
|
+
const node = this;
|
|
31
|
+
$("#node-input-action").change(function() {
|
|
32
|
+
const action = $(this).val();
|
|
33
|
+
if (action === 'fit') {
|
|
34
|
+
$("#fit-params").show();
|
|
35
|
+
$("#predict-params").hide();
|
|
36
|
+
} else {
|
|
37
|
+
$("#fit-params").hide();
|
|
38
|
+
$("#predict-params").show();
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
$("#node-input-action").change(); // trigger on load
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
</script>
|
|
45
|
+
|
|
46
|
+
<script type="text/x-red" data-template-name="logisticRegression">
|
|
47
|
+
<div class="form-row">
|
|
48
|
+
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
49
|
+
<input type="text" id="node-input-name" placeholder="Name">
|
|
50
|
+
</div>
|
|
51
|
+
<div class="form-row">
|
|
52
|
+
<label for="node-input-modelName"><i class="fa fa-database"></i> Model</label>
|
|
53
|
+
<input type="text" id="node-input-modelName" data-type="flow" data-value="">
|
|
54
|
+
</div>
|
|
55
|
+
<div class="form-row">
|
|
56
|
+
<label for="node-input-action"><i class="fa fa-cogs"></i> Action</label>
|
|
57
|
+
<select type="text" id="node-input-action">
|
|
58
|
+
<option value="fit">Fit Model</option>
|
|
59
|
+
<option value="predict">Predict Class</option>
|
|
60
|
+
<option value="predictProba">Predict Probability</option>
|
|
61
|
+
</select>
|
|
62
|
+
</div>
|
|
63
|
+
<div id="fit-params">
|
|
64
|
+
<div class="form-row">
|
|
65
|
+
<label for="node-input-learningRate">Learning Rate</label>
|
|
66
|
+
<input type="number" id="node-input-learningRate" step="0.01">
|
|
67
|
+
</div>
|
|
68
|
+
<div class="form-row">
|
|
69
|
+
<label for="node-input-iterations">Iterations</label>
|
|
70
|
+
<input type="number" id="node-input-iterations">
|
|
71
|
+
</div>
|
|
72
|
+
<div class="form-row">
|
|
73
|
+
<label for="node-input-fitIntercept">Fit Intercept</label>
|
|
74
|
+
<input type="checkbox" id="node-input-fitIntercept" checked>
|
|
75
|
+
</div>
|
|
76
|
+
<div class="form-row">
|
|
77
|
+
<label for="node-input-l2">L2 Regularization</label>
|
|
78
|
+
<input type="number" id="node-input-l2" step="0.01">
|
|
79
|
+
</div>
|
|
80
|
+
<div class="form-row">
|
|
81
|
+
<label for="node-input-tolerance">Tolerance</label>
|
|
82
|
+
<input type="number" id="node-input-tolerance" step="1e-8">
|
|
83
|
+
</div>
|
|
84
|
+
<div class="form-row">
|
|
85
|
+
<label for="node-input-verbose">Verbose</label>
|
|
86
|
+
<input type="checkbox" id="node-input-verbose">
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
<div id="predict-params" style="display:none;">
|
|
90
|
+
<div class="form-row">
|
|
91
|
+
<label for="node-input-threshold">Threshold (for predict)</label>
|
|
92
|
+
<input type="number" id="node-input-threshold" step="0.01" min="0" max="1">
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
</script>
|
|
96
|
+
|
|
97
|
+
<script type="text/x-red" data-help-name="logisticRegression">
|
|
98
|
+
<p>Logistic Regression node for binary classification using gradient descent optimization.</p>
|
|
99
|
+
|
|
100
|
+
<h3>Actions</h3>
|
|
101
|
+
<ul>
|
|
102
|
+
<li><b>Fit Model:</b> Train the model with training data. Input msg.payload should be:
|
|
103
|
+
<pre>{X: [[feature1, feature2, ...], ...], y: [0, 1, 0, ...]}</pre>
|
|
104
|
+
where X is array of feature vectors and y is array of binary labels (0 or 1)</li>
|
|
105
|
+
<li><b>Predict Class:</b> Predict class labels (0 or 1) for input features. Input msg.payload should be array of feature vectors:
|
|
106
|
+
<pre>[[feature1, feature2, ...], ...]</pre></li>
|
|
107
|
+
<li><b>Predict Probability:</b> Predict probabilities for class 1. Same input format as Predict Class, returns array of probabilities between 0 and 1</li>
|
|
108
|
+
</ul>
|
|
109
|
+
|
|
110
|
+
<h3>Configuration Parameters</h3>
|
|
111
|
+
<ul>
|
|
112
|
+
<li><b>Learning Rate:</b> Step size for gradient descent optimization (default: 0.1). Smaller values lead to slower convergence, larger values may overshoot.</li>
|
|
113
|
+
<li><b>Iterations:</b> Number of gradient descent iterations for model training (default: 2000). More iterations allow better convergence.</li>
|
|
114
|
+
<li><b>Fit Intercept:</b> Whether to fit an intercept (bias) term in the model (default: true).</li>
|
|
115
|
+
<li><b>L2 Regularization:</b> Regularization parameter to prevent overfitting (default: 0.0). Higher values apply stronger regularization.</li>
|
|
116
|
+
<li><b>Tolerance:</b> Convergence tolerance for optimization (default: 1e-7). Algorithm stops when cost change is below this threshold.</li>
|
|
117
|
+
<li><b>Verbose:</b> Enable logging of training progress (default: false).</li>
|
|
118
|
+
<li><b>Threshold:</b> Decision threshold for class prediction (default: 0.5). Probabilities >= threshold predict class 1, otherwise class 0.</li>
|
|
119
|
+
</ul>
|
|
120
|
+
|
|
121
|
+
<h3>Model Storage</h3>
|
|
122
|
+
<p><b>Model:</b> Required name for the logistic regression model. For fit actions, this specifies where to store the trained model in the flow context. For predict actions, this selects which fitted model to use from the flow context. The dropdown shows all available flow context variables - select an existing fitted model or enter a new name to create one.</p>
|
|
123
|
+
<p>Models are stored in the flow context and can be shared between multiple Logistic Regression nodes in the same flow. Fit a model with one node, then use the same model name in predict nodes to make predictions.</p>
|
|
124
|
+
|
|
125
|
+
<h3>Example Payload</h3>
|
|
126
|
+
<p><b>For Fit:</b></p>
|
|
127
|
+
<pre>{
|
|
128
|
+
"X": [[0, 0], [0, 1], [1, 0], [1, 1]],
|
|
129
|
+
"y": [0, 1, 1, 1]
|
|
130
|
+
}</pre>
|
|
131
|
+
<p><b>For Predict:</b></p>
|
|
132
|
+
<pre>[[0.5, 0.5], [1, 1]]</pre>
|
|
133
|
+
|
|
134
|
+
<h3>Output</h3>
|
|
135
|
+
<p>Results are returned in msg.result with appropriate format (fitted model confirmation for fit, predictions array for predict/predictProba).</p>
|
|
136
|
+
</script>
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
const logger = new (require("node-red-contrib-logger"))("Logistic Regression");
|
|
2
|
+
logger.sendInfo("Copyright 2025 Jaroslav Peter Prib");
|
|
3
|
+
|
|
4
|
+
const LogisticRegression = require('logisticegression');
|
|
5
|
+
const actions = {
|
|
6
|
+
fit: (RED, node, msg) => {
|
|
7
|
+
if (!node.modelName) {
|
|
8
|
+
throw new Error("Model name is required for fitting");
|
|
9
|
+
}
|
|
10
|
+
if (!msg.payload || !msg.payload.X || !msg.payload.y) {
|
|
11
|
+
throw new Error("For fit, msg.payload must contain X and y arrays");
|
|
12
|
+
}
|
|
13
|
+
const model = new LogisticRegression({
|
|
14
|
+
learningRate: node.learningRate,
|
|
15
|
+
iterations: node.iterations,
|
|
16
|
+
fitIntercept: node.fitIntercept,
|
|
17
|
+
l2: node.l2,
|
|
18
|
+
tolerance: node.tolerance,
|
|
19
|
+
verbose: node.verbose
|
|
20
|
+
});
|
|
21
|
+
model.fit(msg.payload.X, msg.payload.y);
|
|
22
|
+
node.context().flow.set(node.modelName, model);
|
|
23
|
+
return "Model fitted and stored as: " + node.modelName;
|
|
24
|
+
},
|
|
25
|
+
predict: (RED, node, msg) => {
|
|
26
|
+
if (!node.modelName) {
|
|
27
|
+
throw new Error("Model name is required for prediction");
|
|
28
|
+
}
|
|
29
|
+
const model = node.context().flow.get(node.modelName);
|
|
30
|
+
if (!model) {
|
|
31
|
+
throw new Error("Model '" + node.modelName + "' not found. Please fit the model first.");
|
|
32
|
+
}
|
|
33
|
+
if (!Array.isArray(msg.payload)) {
|
|
34
|
+
throw new Error("For predict, msg.payload must be array of features");
|
|
35
|
+
}
|
|
36
|
+
return model.predict(msg.payload);
|
|
37
|
+
},
|
|
38
|
+
predictProba: (RED, node, msg) => {
|
|
39
|
+
if (!node.modelName) {
|
|
40
|
+
throw new Error("Model name is required for prediction");
|
|
41
|
+
}
|
|
42
|
+
const model = node.context().flow.get(node.modelName);
|
|
43
|
+
if (!model) {
|
|
44
|
+
throw new Error("Model '" + node.modelName + "' not found. Please fit the model first.");
|
|
45
|
+
}
|
|
46
|
+
if (!Array.isArray(msg.payload)) {
|
|
47
|
+
throw new Error("For predictProba, msg.payload must be array of features");
|
|
48
|
+
}
|
|
49
|
+
return model.predictProba(msg.payload);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
module.exports = function (RED) {
|
|
54
|
+
function LogisticRegressionNode(config) {
|
|
55
|
+
RED.nodes.createNode(this, config);
|
|
56
|
+
const node = Object.assign(this, config,{
|
|
57
|
+
learningRate: 0.1,
|
|
58
|
+
iterations: 2000,
|
|
59
|
+
fitIntercept: true,
|
|
60
|
+
l2: 0.0,
|
|
61
|
+
tolerance: 1e-7,
|
|
62
|
+
verbose: false
|
|
63
|
+
});
|
|
64
|
+
node.callFunction = actions[config.action];
|
|
65
|
+
if (!node.callFunction) {
|
|
66
|
+
node.error("Unknown action: " + config.action);
|
|
67
|
+
node.status({ fill: "red", shape: "ring", text: "Unknown action: " + config.action });
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
node.status({ fill: "yellow", shape: "ring", text: "model not fitted" });
|
|
71
|
+
node.on('input', function (msg) {
|
|
72
|
+
try {
|
|
73
|
+
msg.result = node.callFunction(RED, node, msg);
|
|
74
|
+
node.send(msg);
|
|
75
|
+
node.status({ fill: "green", shape: "dot", text: "Done" });
|
|
76
|
+
} catch (error) {
|
|
77
|
+
node.error(error.message, msg);
|
|
78
|
+
node.status({ fill: "red", shape: "ring", text: error.message });
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
RED.nodes.registerType("logisticRegression", LogisticRegressionNode);
|
|
83
|
+
};
|
package/package.json
CHANGED
|
@@ -1,33 +1,36 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-red-contrib-prib-functions",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.0",
|
|
4
4
|
"description": "Node-RED added node functions.",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"avsc": ">=5.7.7",
|
|
7
7
|
"compressiontool": "*",
|
|
8
8
|
"fast-xml-parser": "^5.2.1",
|
|
9
|
+
"logisticegression": "git+https://gitlab.com/peter.prib/logisticregression.git",
|
|
9
10
|
"node": ">=22.15.0",
|
|
10
11
|
"node-red-contrib-logger": "latest",
|
|
11
12
|
"node-red-contrib-noderedbase": ">=0.0.16",
|
|
12
13
|
"node-red-contrib-prib-functions": "file:",
|
|
14
|
+
"node-red-node-data-generator":">=1.0.1",
|
|
13
15
|
"npm": "^11.3.0",
|
|
14
16
|
"xlsx": "*"
|
|
15
17
|
},
|
|
16
18
|
"devDependencies": {
|
|
17
19
|
"@node-red/runtime": ">=4.0.9",
|
|
18
20
|
"axios": ">=1.9.0",
|
|
19
|
-
"bcrypt": "^5.1.1",
|
|
20
21
|
"bl": "^6.1.0",
|
|
21
|
-
"
|
|
22
|
+
"should": ">=8.x",
|
|
22
23
|
"node-red": ">=4.0.9",
|
|
23
24
|
"node-red-node-test-helper": ">=0.3.4"
|
|
24
25
|
},
|
|
25
26
|
"scripts": {
|
|
26
|
-
"test": "
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
27
|
+
"test": "node test-runner.js",
|
|
28
|
+
"testLogisticRegression": "node test-runner.js",
|
|
29
|
+
"testTransform": "node test-runner.js transform",
|
|
30
|
+
"testDataAnalysis": "node test-runner.js dataanalysis",
|
|
31
|
+
"testColumnar": "node test-runner.js columnar",
|
|
32
|
+
"startNodeRed": "node-red --userDir ./test/data --setting ./test/data/settings.js ./test/data/flow.json --title '***test nodered***'",
|
|
33
|
+
"sharesNodeRed": "node-red --userDir ./test/data/shares --setting ./test/data/shares/settings.js ./test/data/shares/flow.json --title '***shares nodered***'"
|
|
31
34
|
},
|
|
32
35
|
"repository": {
|
|
33
36
|
"type": "git",
|
|
@@ -47,6 +50,9 @@
|
|
|
47
50
|
"ARMIA",
|
|
48
51
|
"arvo",
|
|
49
52
|
"backward substitution",
|
|
53
|
+
"columnar",
|
|
54
|
+
"columnar data",
|
|
55
|
+
"columnar storage",
|
|
50
56
|
"compare",
|
|
51
57
|
"Complement Minor",
|
|
52
58
|
"compression",
|
|
@@ -84,6 +90,7 @@
|
|
|
84
90
|
"levenshtein distance",
|
|
85
91
|
"load",
|
|
86
92
|
"load injector",
|
|
93
|
+
"logistic regression",
|
|
87
94
|
"matrix",
|
|
88
95
|
"maximum",
|
|
89
96
|
"mean",
|
|
@@ -108,6 +115,7 @@
|
|
|
108
115
|
"skew",
|
|
109
116
|
"SMA",
|
|
110
117
|
"snappy",
|
|
118
|
+
"sql",
|
|
111
119
|
"standard deviation",
|
|
112
120
|
"standardize",
|
|
113
121
|
"statistics",
|
|
@@ -138,9 +146,11 @@
|
|
|
138
146
|
"version": ">=2.0.0",
|
|
139
147
|
"nodes": {
|
|
140
148
|
"append": "append/append.js",
|
|
149
|
+
"filesystem": "filesystem/filesystem.js",
|
|
141
150
|
"dataAnalysis": "dataAnalysis/dataAnalysis.js",
|
|
142
151
|
"hostAvailable": "testing/hostAvailable.js",
|
|
143
152
|
"levenshteinDistance": "levenshteinDistance/levenshteinDistanceNode.js",
|
|
153
|
+
"logisticRegression": "logisticRegression/logisticRegression.js",
|
|
144
154
|
"load-injector": "testing/load-injector.js",
|
|
145
155
|
"matrix": "matrix/matrixNode.js",
|
|
146
156
|
"monitorflow": "monitor/monitorflow.js",
|
|
@@ -148,7 +158,9 @@
|
|
|
148
158
|
"os": "nodejs/os.js",
|
|
149
159
|
"spawnProcess": "spawnProcess/spawnProcess.js",
|
|
150
160
|
"test": "testing/test.js",
|
|
151
|
-
"transform": "transform/transform.js"
|
|
161
|
+
"transform": "transform/transform.js",
|
|
162
|
+
"gitlab": "gitlab/gitlab.js",
|
|
163
|
+
"columnar": "columnar/columnar.js"
|
|
152
164
|
}
|
|
153
165
|
},
|
|
154
166
|
"author": "Peter Prib",
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const assert=require('assert')
|
|
2
|
+
const should=require("should")
|
|
3
|
+
const GraphDB = require('../lib/GraphDB.js')
|
|
4
|
+
|
|
5
|
+
describe('GraphDB', function() {
|
|
6
|
+
it("define vertices", function(done) {
|
|
7
|
+
const graphDB=new GraphDB()
|
|
8
|
+
assert.equal(graphDB.getVerticesCount(),0)
|
|
9
|
+
assert.equal(graphDB.getEdgesCount(),0)
|
|
10
|
+
graphDB.addVertex({id:"a"})
|
|
11
|
+
assert.equal(graphDB.getVerticesCount(),1)
|
|
12
|
+
graphDB.addVertex({id:"b"})
|
|
13
|
+
assert.equal(graphDB.getVerticesCount(),2)
|
|
14
|
+
done()
|
|
15
|
+
});
|
|
16
|
+
const graphDB=new GraphDB()
|
|
17
|
+
const a=graphDB.addVertex({id:"a",name:"aname"})
|
|
18
|
+
const b=graphDB.addVertex({id:"b",name:"bname"})
|
|
19
|
+
const c=graphDB.addVertex({id:"c",name:"cname"})
|
|
20
|
+
it("find vertices", function(done) {
|
|
21
|
+
assert.deepEqual(graphDB.getVertices(v=>v.id=="d"),[])
|
|
22
|
+
assert.deepEqual(graphDB.getVertices(v=>v.id=="a"),[a])
|
|
23
|
+
assert.deepEqual(graphDB.getVertices(v=>v.id=="c"),[c])
|
|
24
|
+
assert.deepEqual(graphDB.getVertices(v=>v.id=="a"||v.id=="c"),[a,c])
|
|
25
|
+
done()
|
|
26
|
+
});
|
|
27
|
+
const ea=graphDB.addEdge(a,b,{id:"ea",name:"eaname"})
|
|
28
|
+
const eb=graphDB.addEdge(b,c,{id:"eb",name:"ebname"})
|
|
29
|
+
const ec=graphDB.addEdge(a,c,{id:"ec",name:"ecname"})
|
|
30
|
+
it("define edges", function(done) {
|
|
31
|
+
assert.throws(graphDB.addEdge({}),Error("from not type Vertex"))
|
|
32
|
+
assert.throws(graphDB.addEdge(a,{}),Error("to not type Vertex"))
|
|
33
|
+
assert.equal(graphDB.getEdgesCount(),0)
|
|
34
|
+
graphDB.addEdge(a,b)
|
|
35
|
+
assert.equal(graphDB.getEdgesCount(),1)
|
|
36
|
+
graphDB.addEdge(b,c,{cost:1})
|
|
37
|
+
assert.equal(graphDB.getEdgesCount(),2)
|
|
38
|
+
done()
|
|
39
|
+
});
|
|
40
|
+
it("find edges", function(done) {
|
|
41
|
+
assert.deepEqual(graphDB.getEdges(e=>e.id=="ed"),[])
|
|
42
|
+
assert.deepEqual(graphDB.getEdges(e=>e.id=="ea"),[a])
|
|
43
|
+
assert.deepEqual(graphDB.getEdges(e=>e.id=="ec"),[c])
|
|
44
|
+
assert.deepEqual(graphDB.getEdges(e=>e.id=="ea"||e.id=="ec"),[ea,ec])
|
|
45
|
+
});
|
|
46
|
+
})
|