diff --git a/nodes/core/locales/en-US/messages.json b/nodes/core/locales/en-US/messages.json
index 3d07b0c33..e524b78f3 100644
--- a/nodes/core/locales/en-US/messages.json
+++ b/nodes/core/locales/en-US/messages.json
@@ -871,55 +871,55 @@
"complete":"After a message with the msg.complete
property set",
"tip":"This mode assumes this node is either paired with a split node or the received messages will have a properly configured msg.parts
property.",
"too-many" : "too many pending messages in join node",
- "merge": {
- "topics-label":"Merged Topics",
- "topics":"topics",
+ "merge": {
+ "topics-label":"Merged Topics",
+ "topics":"topics",
"topic" : "topic",
"on-change":"Send merged message on arrival of a new topic"
- },
+ },
"reduce": {
- "exp": "Reduce exp",
- "exp-value": "exp",
- "init": "Initial value",
- "right": "Evaluate in reverse order (right to left)",
- "fixup": "Fixup exp"
+ "exp": "Reduce exp",
+ "exp-value": "exp",
+ "init": "Initial value",
+ "right": "Evaluate in reverse order (right to left)",
+ "fixup": "Fixup exp"
},
"errors": {
"invalid-expr": "Invalid JSONata expression: __error__"
}
},
"sort" : {
- "key-type" : "Key type",
- "payload" : "payload or element",
- "exp" : "expression",
- "key-exp" : "Key exp.",
- "order" : "Order",
- "ascending" : "ascending",
- "descending" : "descending",
- "as-number" : "as number",
+ "target" : "Sort",
+ "seq" : "message sequence",
+ "key" : "Key",
+ "elem" : "element value",
+ "order" : "Order",
+ "ascending" : "ascending",
+ "descending" : "descending",
+ "as-number" : "as number",
"invalid-exp" : "invalid JSONata expression in sort node",
"too-many" : "too many pending messages in sort node",
"clear" : "clear pending message in sort node"
},
"batch" : {
- "mode": {
- "label" : "Mode",
- "num-msgs" : "number of messages",
- "interval" : "interval in seconds",
+ "mode": {
+ "label" : "Mode",
+ "num-msgs" : "number of messages",
+ "interval" : "interval in seconds",
"concat" : "concatenate sequences"
- },
- "count": {
- "label" : "Number of msgs",
- "overwrap" : "Overwrap",
- "count" : "count",
+ },
+ "count": {
+ "label" : "Number of msgs",
+ "overwrap" : "Overwrap",
+ "count" : "count",
"invalid" : "Invalid count and overwrap"
- },
- "interval": {
- "label" : "Interval (sec)",
- "seconds" : "seconds",
- "sec" : "sec",
+ },
+ "interval": {
+ "label" : "Interval (sec)",
+ "seconds" : "seconds",
+ "sec" : "sec",
"empty" : "send empty message when no message arrives"
- },
+ },
"concat": {
"topics-label": "Topics",
"topic" : "topic"
diff --git a/nodes/core/logic/18-sort.html b/nodes/core/logic/18-sort.html
index 3aacd46a1..5ce1a9f82 100644
--- a/nodes/core/logic/18-sort.html
+++ b/nodes/core/logic/18-sort.html
@@ -19,17 +19,24 @@
@@ -86,9 +93,13 @@
defaults: {
name: { value:"" },
order: { value:"ascending" },
- as_num : { value:false },
- keyType : { value:"payload" },
- key : { value:"" }
+ as_num: { value:false },
+ target: { value:"payload" },
+ targetType: { value:"msg" },
+ msgKey: { value:"payload" },
+ msgKeyType: { value:"elem" },
+ seqKey: { value:"payload" },
+ seqKeyType: { value:"msg" }
},
inputs:1,
outputs:1,
@@ -100,13 +111,37 @@
return this.name ? "node_label_italic" : "";
},
oneditprepare: function() {
- $("#node-input-key").typedInput({default:'jsonata', types:['jsonata']});
- $("#node-input-keyType").change(function(e) {
- var val = $(this).val();
- $(".node-row-sort-key").toggle(val === 'exp');
+ var seq = {
+ value: "seq",
+ label: RED._("node-red:sort.seq"),
+ hasValue: false
+ };
+ var elem = {
+ value: "elem",
+ label: RED._("node-red:sort.elem"),
+ hasValue: false
+ };
+ $("#node-input-target").typedInput({
+ default:'msg',
+ typeField: $("#node-input-targetType"),
+ types:['msg', seq]
});
- $("#node-input-keyType").change();
- $("#node-input-order").change();
+ $("#node-input-msgKey").typedInput({
+ default:'elem',
+ typeField: $("#node-input-msgKeyType"),
+ types:[elem, 'jsonata']
+ });
+ $("#node-input-seqKey").typedInput({
+ default:'msg',
+ typeField: $("#node-input-seqKeyType"),
+ types:['msg', 'jsonata']
+ });
+ $("#node-input-target").change(function(e) {
+ var val = $("#node-input-target").typedInput('type');
+ $(".node-row-sort-msg-key").toggle(val === "msg");
+ $(".node-row-sort-seq-key").toggle(val === "seq");
+ });
+ $("#node-input-target").change();
}
});
diff --git a/nodes/core/logic/18-sort.js b/nodes/core/logic/18-sort.js
index 529e83f37..65d8e6c45 100644
--- a/nodes/core/logic/18-sort.js
+++ b/nodes/core/logic/18-sort.js
@@ -21,7 +21,7 @@ module.exports = function(RED) {
function max_kept_msgs_count(node) {
if (_max_kept_msgs_count === undefined) {
- var name = "sortMaxKeptMsgsCount";
+ var name = "maxKeptMsgsCount";
if (RED.settings.hasOwnProperty(name)) {
_max_kept_msgs_count = RED.settings[name];
}
@@ -60,11 +60,15 @@ module.exports = function(RED) {
var pending_id = 0;
var order = n.order || "ascending";
var as_num = n.as_num || false;
- var key_is_payload = (n.keyType === 'payload');
- var key_exp = undefined;
- if (!key_is_payload) {
+ var target_prop = n.target || "payload";
+ var target_is_prop = (n.targetType === 'msg');
+ var key_is_exp = target_is_prop ? (n.msgKeyType === "jsonata") : (n.seqKeyType === "jsonata");
+ var key_prop = n.seqKey || "payload";
+ var key_exp = target_is_prop ? n.msgKey : n.seqKey;
+
+ if (key_is_exp) {
try {
- key_exp = RED.util.prepareJSONataExpression(n.key, this);
+ key_exp = RED.util.prepareJSONataExpression(key_exp, this);
}
catch (e) {
node.error(RED._("sort.invalid-exp"));
@@ -87,10 +91,12 @@ module.exports = function(RED) {
}
function send_group(group) {
- var key = key_is_payload
- ? function(msg) { return msg.payload; }
- : function(msg) {
+ var key = key_is_exp
+ ? 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;
@@ -108,16 +114,16 @@ module.exports = function(RED) {
}
function sort_payload(msg) {
- var payload = msg.payload;
- if (Array.isArray(payload)) {
- var key = key_is_payload
- ? function(elem) { return elem; }
- : function(elem) {
+ var data = RED.util.getMessageProperty(msg, target_prop);
+ if (Array.isArray(data)) {
+ var key = key_is_exp
+ ? function(elem) {
return eval_jsonata(node, key_exp, elem);
- };
+ }
+ : function(elem) { return elem; };
var comp = gen_comp(key);
try {
- payload.sort(comp);
+ data.sort(comp);
}
catch (e) {
return false;
@@ -162,7 +168,7 @@ module.exports = function(RED) {
}
function process_msg(msg) {
- if (!msg.hasOwnProperty("parts")) {
+ if (target_is_prop) {
if (sort_payload(msg)) {
node.send(msg);
}
diff --git a/settings.js b/settings.js
index a3eef19e2..eca108e96 100644
--- a/settings.js
+++ b/settings.js
@@ -49,10 +49,7 @@ module.exports = {
// The maximum number of messages kept internally in nodes.
// Zero or undefined value means not restricting number of messages.
- //sortMaxKeptMsgsCount: 0,
- //switchMaxKeptMsgsCount: 0,
- //joinMaxKeptMsgsCount: 0,
- //batchMaxKeptMsgsCount: 0,
+ //maxKeptMsgsCount: 0,
// To disable the option for using local files for storing keys and certificates in the TLS configuration
// node, set this to true
diff --git a/test/nodes/core/logic/18-sort_spec.js b/test/nodes/core/logic/18-sort_spec.js
index 1695a7439..98e299f6f 100644
--- a/test/nodes/core/logic/18-sort_spec.js
+++ b/test/nodes/core/logic/18-sort_spec.js
@@ -27,10 +27,11 @@ describe('SORT node', function() {
afterEach(function() {
helper.unload();
+ RED.settings.maxKeptMsgsCount = 0;
});
it('should be loaded', function(done) {
- var flow = [{id:"n1", type:"sort", order:"ascending", as_num:false, keyType:"payload", name: "SortNode", wires:[["n2"]]},
+ var flow = [{id:"n1", type:"sort", order:"ascending", as_num:false, name: "SortNode", wires:[["n2"]]},
{id:"n2", type:"helper"}];
helper.load(sortNode, flow, function() {
var n1 = helper.getNode("n1");
@@ -39,16 +40,58 @@ describe('SORT node', function() {
});
});
- function check_sort0(flow, data_in, data_out, done) {
+ function check_sort0(flow, target, key, key_type, data_in, data_out, done) {
+ var sort = flow[0];
+ sort.target = target;
+ sort.targetType = "msg";
+ sort.msgKey = key;
+ sort.msgKeyType = key_type;
+ helper.load(sortNode, flow, function() {
+ var n1 = helper.getNode("n1");
+ var n2 = helper.getNode("n2");
+ n2.on("input", function(msg) {
+ msg.should.have.property(target);
+ var data = msg[target];
+ data.length.should.equal(data_out.length);
+ for(var i = 0; i < data_out.length; i++) {
+ data[i].should.equal(data_out[i]);
+ }
+ done();
+ });
+ var msg = {};
+ msg[target] = data_in;
+ n1.receive(msg);
+ });
+ }
+
+ function check_sort0A(flow, data_in, data_out, done) {
+ check_sort0(flow, "payload", "", "elem", data_in, data_out, done);
+ }
+
+ function check_sort0B(flow, data_in, data_out, done) {
+ check_sort0(flow, "data", "", "elem", data_in, data_out, done);
+ }
+
+ function check_sort0C(flow, exp, data_in, data_out, done) {
+ check_sort0(flow, "data", exp, "jsonata", data_in, data_out, done);
+ }
+
+ function check_sort1(flow, key, key_type, data_in, data_out, done) {
+ var sort = flow[0];
+ var prop = (key_type === "msg") ? key : "payload";
+ sort.targetType = "seq";
+ sort.seqKey = key;
+ sort.seqKeyType = key_type;
helper.load(sortNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
var count = 0;
n2.on("input", function(msg) {
- msg.should.have.property("payload");
+ msg.should.have.property(prop);
msg.should.have.property("parts");
msg.parts.should.have.property("count", data_out.length);
- var index = data_out.indexOf(msg.payload);
+ var data = msg[prop];
+ var index = data_out.indexOf(data);
msg.parts.should.have.property("index", index);
count++;
if (count === data_out.length) {
@@ -58,111 +101,132 @@ describe('SORT node', function() {
var len = data_in.length;
for(var i = 0; i < len; i++) {
var parts = { id: "X", index: i, count: len };
- n1.receive({payload:data_in[i], parts: parts});
+ var msg = {parts: parts};
+ msg[prop] = data_in[i];
+ n1.receive(msg);
}
});
}
- function check_sort1(flow, data_in, data_out, done) {
- helper.load(sortNode, flow, function() {
- var n1 = helper.getNode("n1");
- var n2 = helper.getNode("n2");
- n2.on("input", function(msg) {
- msg.should.have.property("payload");
- msg.payload.length.should.equal(data_out.length);
- for(var i = 0; i < data_out.length; i++) {
- msg.payload[i].should.equal(data_out[i]);
- }
- done();
- });
- n1.receive({payload:data_in});
- });
+ function check_sort1A(flow, data_in, data_out, done) {
+ check_sort1(flow, "payload", "msg", data_in, data_out, done);
}
+ function check_sort1B(flow, data_in, data_out, done) {
+ check_sort1(flow, "data", "msg", data_in, data_out, done);
+ }
+
+ function check_sort1C(flow, exp, data_in, data_out, done) {
+ check_sort1(flow, exp, "jsonata", data_in, data_out, done);
+ }
+
+
(function() {
- var flow = [{id:"n1", type:"sort", order:"ascending", as_num:false, keyType:"payload", wires:[["n2"]]},
+ var flow = [{id:"n1", type:"sort", order:"ascending", as_num:false, wires:[["n2"]]},
{id:"n2", type:"helper"}];
var data_in = [ "200", "4", "30", "1000" ];
var data_out = [ "1000", "200", "30", "4" ];
- it('should sort message group (payload, not number, ascending)', function(done) {
- check_sort0(flow, data_in, data_out, done);
+ it('should sort payload (elem, not number, ascending)', function(done) {
+ check_sort0A(flow, data_in, data_out, done);
});
- it('should sort payload (payload, not number, ascending)', function(done) {
- check_sort1(flow, data_in, data_out, done);
+ it('should sort msg prop (elem, not number, ascending)', function(done) {
+ check_sort0B(flow, data_in, data_out, done);
});
- })();
-
- (function() {
- var flow = [{id:"n1", type:"sort", order:"descending", as_num:false, keyType:"payload", wires:[["n2"]]},
- {id:"n2", type:"helper"}];
- var data_in = [ "200", "4", "30", "1000" ];
- var data_out = [ "4", "30", "200", "1000" ];
- it('should sort message group (payload, not number, descending)', function(done) {
- check_sort0(flow, data_in, data_out, done);
+ it('should sort message group/payload (not number, ascending)', function(done) {
+ check_sort1A(flow, data_in, data_out, done);
});
- it('should sort payload (payload, not number, descending)', function(done) {
- check_sort1(flow, data_in, data_out, done);
- });
- })();
-
- (function() {
- var flow = [{id:"n1", type:"sort", order:"ascending", as_num:true, keyType:"payload", wires:[["n2"]]},
- {id:"n2", type:"helper"}];
- var data_in = [ "200", "4", "30", "1000" ];
- var data_out = [ "4", "30", "200", "1000" ];
- it('should sort message group (payload, number, ascending)', function(done) {
- check_sort0(flow, data_in, data_out, done);
- });
- it('should sort payload (payload, number, ascending)', function(done) {
- check_sort1(flow, data_in, data_out, done);
- });
- })();
-
- (function() {
- var flow = [{id:"n1", type:"sort", order:"descending", as_num:true, keyType:"payload", wires:[["n2"]]},
- {id:"n2", type:"helper"}];
- var data_in = [ "200", "4", "30", "1000" ];
- var data_out = [ "1000", "200", "30", "4" ];
- it('should sort message group (payload, number, descending)', function(done) {
- check_sort0(flow, data_in, data_out, done);
- });
- it('should sort payload (payload, number, descending)', function(done) {
- check_sort1(flow, data_in, data_out, done);
+ it('should sort message group/prop (not number, ascending)', function(done) {
+ check_sort1B(flow, data_in, data_out, done);
});
})();
(function() {
+ var flow = [{id:"n1", type:"sort", order:"descending", as_num:false, wires:[["n2"]]},
+ {id:"n2", type:"helper"}];
+ var data_in = [ "200", "4", "30", "1000" ];
+ var data_out = [ "4", "30", "200", "1000" ];
+ it('should sort payload (elem, not number, descending)', function(done) {
+ check_sort0A(flow, data_in, data_out, done);
+ });
+ it('should sort msg prop (elem, not number, descending)', function(done) {
+ check_sort0B(flow, data_in, data_out, done);
+ });
+ it('should sort message group/payload (not number, descending)', function(done) {
+ check_sort1A(flow, data_in, data_out, done);
+ });
+ it('should sort message group/prop (not number, descending)', function(done) {
+ check_sort1B(flow, data_in, data_out, done);
+ });
+ })();
+
+ (function() {
+ var flow = [{id:"n1", type:"sort", order:"ascending", as_num:true, wires:[["n2"]]},
+ {id:"n2", type:"helper"}];
+ var data_in = [ "200", "4", "30", "1000" ];
+ var data_out = [ "4", "30", "200", "1000" ];
+ it('should sort payload (elem, number, ascending)', function(done) {
+ check_sort0A(flow, data_in, data_out, done);
+ });
+ it('should sort msg prop (elem, number, ascending)', function(done) {
+ check_sort0B(flow, data_in, data_out, done);
+ });
+ it('should sort message group/payload (number, ascending)', function(done) {
+ check_sort1A(flow, data_in, data_out, done);
+ });
+ it('should sort message group/prop (number, ascending)', function(done) {
+ check_sort1B(flow, data_in, data_out, done);
+ });
+ })();
+
+ (function() {
+ var flow = [{id:"n1", type:"sort", order:"descending", as_num:true, wires:[["n2"]]},
+ {id:"n2", type:"helper"}];
+ var data_in = [ "200", "4", "30", "1000" ];
+ var data_out = [ "1000", "200", "30", "4" ];
+ it('should sort payload (elem, number, descending)', function(done) {
+ check_sort0A(flow, data_in, data_out, done);
+ });
+ it('should sort msg prop (elem, number, descending)', function(done) {
+ check_sort0B(flow, data_in, data_out, done);
+ });
+ it('should sort message group/payload (number, descending)', function(done) {
+ check_sort1A(flow, data_in, data_out, done);
+ });
+ it('should sort message group/prop (number, descending)', function(done) {
+ check_sort1B(flow, data_in, data_out, done);
+ });
+ })();
+
+ (function() {
+ var flow = [{id:"n1", type:"sort", order:"ascending", as_num:false, wires:[["n2"]]},
+ {id:"n2", type:"helper"}];
var data_in = [ "C200", "A4", "B30", "D1000" ];
var data_out = [ "D1000", "C200", "B30", "A4" ];
- it('should sort message group (exp, not number, ascending)', function(done) {
- var flow = [{id:"n1", type:"sort", order:"ascending", as_num:false, keyType:"exp", key:"$substring(payload, 1)", wires:[["n2"]]},
- {id:"n2", type:"helper"}];
- check_sort0(flow, data_in, data_out, done);
- });
it('should sort payload (exp, not number, ascending)', function(done) {
- var flow = [{id:"n1", type:"sort", order:"ascending", as_num:false, keyType:"exp", key:"$substring($, 1)", wires:[["n2"]]},
- {id:"n2", type:"helper"}];
- check_sort1(flow, data_in, data_out, done);
+ check_sort0C(flow, "$substring($,1)", data_in, data_out, done);
+ });
+ it('should sort message group (exp, not number, ascending)', function(done) {
+ check_sort1C(flow, "$substring(payload,1)", data_in, data_out, done);
});
})();
+ return;
+
(function() {
+ var flow = [{id:"n1", type:"sort", order:"descending", as_num:false, wires:[["n2"]]},
+ {id:"n2", type:"helper"}];
var data_in = [ "C200", "A4", "B30", "D1000" ];
var data_out = [ "A4", "B30", "C200", "D1000" ];
it('should sort message group (exp, not number, descending)', function(done) {
- var flow = [{id:"n1", type:"sort", order:"descending", as_num:false, keyType:"exp", key:"$substring(payload, 1)", wires:[["n2"]]},
- {id:"n2", type:"helper"}];
- check_sort0(flow, data_in, data_out, done);
+ check_sort0C(flow, "$substring($,1)", data_in, data_out, done);
});
it('should sort payload (exp, not number, descending)', function(done) {
- var flow = [{id:"n1", type:"sort", order:"descending", as_num:false, keyType:"exp", key:"$substring($, 1)", wires:[["n2"]]},
- {id:"n2", type:"helper"}];
- check_sort1(flow, data_in, data_out, done);
+ check_sort1C(flow, "$substring(payload,1)", data_in, data_out, done);
});
})();
it('should handle JSONata script error', function(done) {
- var flow = [{id:"n1", type:"sort", order:"ascending", as_num:false, keyType:"exp", key:"$unknown()", wires:[["n2"]]},
+ var flow = [{id:"n1", type:"sort", order:"ascending", as_num:false, target:"payload", targetType:"seq", seqKey:"$unknown()", seqKeyType:"jsonata", wires:[["n2"]]},
{id:"n2", type:"helper"}];
helper.load(sortNode, flow, function() {
var n1 = helper.getNode("n1");
@@ -183,12 +247,14 @@ describe('SORT node', function() {
});
});
+ return;
+
it('should handle too many pending messages', function(done) {
- var flow = [{id:"n1", type:"sort", order:"ascending", as_num:false, keyType:"payload", wires:[["n2"]]},
+ var flow = [{id:"n1", type:"sort", order:"ascending", as_num:false, target:"payload", targetType:"seq", seqKey:"payload", seqKeyType:"msg", wires:[["n2"]]},
{id:"n2", type:"helper"}];
helper.load(sortNode, flow, function() {
var n1 = helper.getNode("n1");
- RED.settings.sortMaxKeptMsgsCount = 2;
+ RED.settings.maxKeptMsgsCount = 2;
setTimeout(function() {
var logEvents = helper.log().args.filter(function (evt) {
return evt[0].type == "sort";
@@ -208,7 +274,7 @@ describe('SORT node', function() {
});
it('should clear pending messages on close', function(done) {
- var flow = [{id:"n1", type:"sort", order:"ascending", as_num:false, keyType:"payload", wires:[["n2"]]},
+ var flow = [{id:"n1", type:"sort", order:"ascending", as_num:false, target:"payload", targetType:"seq", seqKey:"payload", seqKeyType:"msg", wires:[["n2"]]},
{id:"n2", type:"helper"}];
helper.load(sortNode, flow, function() {
var n1 = helper.getNode("n1");