1
0
mirror of https://github.com/node-red/node-red.git synced 2023-10-10 13:36:53 +02:00

Update sort node for async use of jsonata

This commit is contained in:
Nick O'Leary 2018-07-09 23:06:51 +01:00
parent 807b512ef7
commit d8d82e2ba3
No known key found for this signature in database
GPG Key ID: 4F2157149161A6C9

View File

@ -17,7 +17,7 @@
module.exports = function(RED) { module.exports = function(RED) {
"use strict"; "use strict";
var _max_kept_msgs_count = undefined; var _max_kept_msgs_count;
function max_kept_msgs_count(node) { function max_kept_msgs_count(node) {
if (_max_kept_msgs_count === undefined) { if (_max_kept_msgs_count === undefined) {
@ -32,30 +32,20 @@ module.exports = function(RED) {
return _max_kept_msgs_count; return _max_kept_msgs_count;
} }
function eval_jsonata(node, code, val) { // function get_context_val(node, name, dval) {
try { // var context = node.context();
return RED.util.evaluateJSONataExpression(code, val); // var val = context.get(name);
} // if (val === undefined) {
catch (e) { // context.set(name, dval);
node.error(RED._("sort.invalid-exp")); // return dval;
throw e; // }
} // return val;
} // }
function get_context_val(node, name, dval) {
var context = node.context();
var val = context.get(name);
if (val === undefined) {
context.set(name, dval);
return dval;
}
return val;
}
function SortNode(n) { function SortNode(n) {
RED.nodes.createNode(this, n); RED.nodes.createNode(this, n);
var node = this; var node = this;
var pending = get_context_val(node, 'pending', {}) var pending = {};//get_context_val(node, 'pending', {})
var pending_count = 0; var pending_count = 0;
var pending_id = 0; var pending_id = 0;
var order = n.order || "ascending"; var order = n.order || "ascending";
@ -76,11 +66,10 @@ module.exports = function(RED) {
} }
} }
var dir = (order === "ascending") ? 1 : -1; var dir = (order === "ascending") ? 1 : -1;
var conv = as_num var conv = as_num ? function(x) { return Number(x); }
? function(x) { return Number(x); } : function(x) { return x; };
: function(x) { return x; };
function gen_comp(key) { function generateComparisonFunction(key) {
return function(x, y) { return function(x, y) {
var xp = conv(key(x)); var xp = conv(key(x));
var yp = conv(key(y)); var yp = conv(key(y));
@ -90,74 +79,97 @@ module.exports = function(RED) {
}; };
} }
function send_group(group) { function sortMessageGroup(group) {
var key = key_is_exp var promise;
? function(msg) {
return eval_jsonata(node, key_exp, msg);
}
: function(msg) {
return RED.util.getMessageProperty(msg, key_prop);
};
var comp = gen_comp(key);
var msgs = group.msgs; var msgs = group.msgs;
try { if (key_is_exp) {
msgs.sort(comp); var evaluatedDataPromises = msgs.map(msg => {
} return new Promise((resolve,reject) => {
catch (e) { RED.util.evaluateJSONataExpression(key_exp, msg, (err, result) => {
return; // not send when error resolve({
} item: msg,
for (var i = 0; i < msgs.length; i++) { sortValue: result
var msg = msgs[i]; })
msg.parts.index = i; });
node.send(msg); })
} });
} promise = Promise.all(evaluatedDataPromises).then(evaluatedElements => {
// Once all of the sort keys are evaluated, sort by them
function sort_payload(msg) { var comp = generateComparisonFunction(elem=>elem.sortValue);
var data = RED.util.getMessageProperty(msg, target_prop); return evaluatedElements.sort(comp).map(elem=>elem.item);
if (Array.isArray(data)) { });
var key = key_is_exp } else {
? function(elem) { var key = function(msg) {
return eval_jsonata(node, key_exp, elem); return ;
} }
: function(elem) { return elem; }; var comp = generateComparisonFunction(msg => RED.util.getMessageProperty(msg, key_prop));
var comp = gen_comp(key);
try { try {
data.sort(comp); msgs.sort(comp);
} }
catch (e) { catch (e) {
return false; return; // not send when error
} }
return true; promise = Promise.resolve(msgs);
} }
return false; return promise.then(msgs => {
for (var i = 0; i < msgs.length; i++) {
var msg = msgs[i];
msg.parts.index = i;
node.send(msg);
}
});
} }
function check_parts(parts) { function sortMessageProperty(msg) {
if (parts.hasOwnProperty("id") && var data = RED.util.getMessageProperty(msg, target_prop);
parts.hasOwnProperty("index")) { if (Array.isArray(data)) {
return true; if (key_is_exp) {
// key is an expression. Evaluated the expression for each item
// to get its sort value. As this could be async, need to do
// it first.
var evaluatedDataPromises = data.map(elem => {
return new Promise((resolve,reject) => {
RED.util.evaluateJSONataExpression(key_exp, elem, (err, result) => {
resolve({
item: elem,
sortValue: result
})
});
})
})
return Promise.all(evaluatedDataPromises).then(evaluatedElements => {
// Once all of the sort keys are evaluated, sort by them
// and reconstruct the original message item with the newly
// sorted values.
var comp = generateComparisonFunction(elem=>elem.sortValue);
data = evaluatedElements.sort(comp).map(elem=>elem.item);
RED.util.setMessageProperty(msg, target_prop,data);
return true;
})
} else {
var comp = generateComparisonFunction(elem=>elem);
try {
data.sort(comp);
} catch (e) {
return Promise.resolve(false);
}
return Promise.resolve(true);
}
} }
return false; return Promise.resolve(false);
} }
function clear_pending() { function removeOldestPending() {
var oldest;
var oldest_key;
for(var key in pending) { for(var key in pending) {
node.log(RED._("sort.clear"), pending[key].msgs[0]); if (pending.hasOwnProperty(key)) {
delete pending[key]; var item = pending[key];
} if((oldest === undefined) ||
pending_count = 0; (oldest.seq_no > item.seq_no)) {
} oldest = item;
oldest_key = key;
function remove_oldest_pending() { }
var oldest = undefined;
var oldest_key = undefined;
for(var key in pending) {
var item = pending[key];
if((oldest === undefined) ||
(oldest.seq_no > item.seq_no)) {
oldest = item;
oldest_key = key;
} }
} }
if(oldest !== undefined) { if(oldest !== undefined) {
@ -166,16 +178,18 @@ module.exports = function(RED) {
} }
return 0; return 0;
} }
function process_msg(msg) { function processMessage(msg) {
if (target_is_prop) { if (target_is_prop) {
if (sort_payload(msg)) { sortMessageProperty(msg).then(send => {
node.send(msg); if (send) {
} node.send(msg);
return; }
}).catch(err => {
});
} }
var parts = msg.parts; var parts = msg.parts;
if (!check_parts(parts)) { if (!parts.hasOwnProperty("id") || !parts.hasOwnProperty("index")) {
return; return;
} }
var gid = parts.id; var gid = parts.id;
@ -195,23 +209,29 @@ module.exports = function(RED) {
pending_count++; pending_count++;
if (group.count === msgs.length) { if (group.count === msgs.length) {
delete pending[gid] delete pending[gid]
send_group(group); sortMessageGroup(group);
pending_count -= msgs.length; pending_count -= msgs.length;
} } else {
var max_msgs = max_kept_msgs_count(node); var max_msgs = max_kept_msgs_count(node);
if ((max_msgs > 0) && (pending_count > max_msgs)) { if ((max_msgs > 0) && (pending_count > max_msgs)) {
pending_count -= remove_oldest_pending(); pending_count -= removeOldestPending();
node.error(RED._("sort.too-many"), msg); node.error(RED._("sort.too-many"), msg);
}
} }
} }
this.on("input", function(msg) { this.on("input", function(msg) {
process_msg(msg); processMessage(msg);
}); });
this.on("close", function() { this.on("close", function() {
clear_pending(); for(var key in pending) {
}) if (pending.hasOwnProperty(key)) {
node.log(RED._("sort.clear"), pending[key].msgs[0]);
delete pending[key];
}
}
pending_count = 0; })
} }
RED.nodes.registerType("sort", SortNode); RED.nodes.registerType("sort", SortNode);