Switch node - jsonata - magic?

Example object:

{
  "time_utc": 1562311572,
  "Temperature": 21,
  "CO2": 434,
  "Humidity": 56,
  "Noise": 40,
  "Pressure": 1016.7,
  "AbsolutePressure": 1016.4,
  "min_temp": 20.9,
  "max_temp": 21.7,
  "date_min_temp": 1562289154,
  "date_max_temp": 1562277636,
  "temp_trend": "stable",
  "pressure_trend": "down"
}

Is it possible to use jsonata in the switch node to create ("filtered") outputs for each key in the object ?

If yes, how ?
If not, this would be a great addition on the switch node.

How is jsonata used in the switch node ?

Specifically how is jsonata used in the property field ? I could not really find examples of jsonata usage in combination with the switch node.

No. The switch node is not used to change the message. That is the job of the Change node.

The switch node checks each rule to see which evaluate to true and then passes the message to the corresponding output.

The JSONata expression will pass if it evaluates to a true value. This allows for more complex rules to be created that check multiple things.

Makes sense thanks. Perhaps an opportunity for myself to create a node that switches based on key.

I like to use a JSONata expression in a switch node's "Property", to essentially simulate a set of switch/case programming statements. For instance, to branch a flow based on how many objects are in the payload array (e.g. 0, 1, or multiple), I would use the expression $count(payload), then have two tests against the numbers 0 and 1 on ports #1 and 2, then use the Otherwise test for port #3.

Here is another example that I've used in the past, to provide a "round-robin" routing node:

image

Every time a new msg enters this flow, it increments the flow counter, and routes it to a different path (e.g. different http server endpoints) based on the results of the "modulo" operator %. Typically, either the Property OR the tests would be an expression, not both -- but that's just because I've not had a case where I needed to check part of a payload against other properties of the msg object.

Hope this gives you a few new ideas!

7 Likes

that's quite a nice "trick" !

That round-robin trick is quite interesting thanks for sharing.

Re-reading @knolleary response I am not entirely sure if we are on the same page, as I don't want to "change" the payload, but an option to switch based on key not value. Something like:

This would introduce various advantages and efficiencies.

Do you mean the test would be:

Pass a message to this output if the property is an object and contains a key named XYZ

?

1 Like

Yes indeed. (the pulldown is now based on "value rules"), but yes that is what I mean, a key based switch.

sounds like .hasOwnProperty ?
so if say a payload arrive with {a:1, b:2, c:3}
and it is set to check msg.payload has key a --> port 1 - what comes out of port 1 ?
the whole msg.payload ie {a:1, b:2, c:3} ? as we are testing if msg.payload (as set as the input) contains a key(property) a... or is this now a switch node that changes things as well (and sends only the a property to the output) ? so msg.payload coming out of port1 is now just 1 ?

The whole message. The Switch node does not change the message.

.hasOwnProperty indeed.

I was thinking only send the value associated with the property/key (as payload), and you could consider that a change of the object. But if that is not possible, no problem.

JSONata has a $keys function so using that with Steve's trick would work wouldn't it?

Sounds like a cross between a switch and a change node... a type of "splitter" function that sends parts of an object to different output ports. Of course, this is doable using a function node with a bit of code, like this:

var keys = Object.keys(msg.payload);
var msgs = [];

for (var ii = 0; ii < keys.length; ii++) {
    var key = keys[ii];
    msgs[ii] = {
        "topic": "output-" + ii,
        "payload": msg.payload[key]
    }
}

return msgs;

Note: You would have to know how many outputs are expected, and set that on the function node prior to processing a msg, since the # of ports cannot be set dynamically.

(sample flow 1)

[{"id":"5f9f01e4.cae49","type":"inject","z":"58c8eb7a.5496c4","name":"{ a:1, b:2, c:3, d:4 }","topic":"","payload":"{\"a\":1,\"b\":2,\"c\":3,\"d\":4}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":170,"y":3840,"wires":[["d429c1e0.31fd2"]]},{"id":"d429c1e0.31fd2","type":"function","z":"58c8eb7a.5496c4","name":"splitter","func":"var keys = Object.keys(msg.payload);\nvar msgs = [];\n\nfor (var ii = 0; ii < keys.length; ii++) {\n    var key = keys[ii];\n    msgs[ii] = {\n        \"topic\": \"output-\" + ii,\n        \"payload\": msg.payload[key]\n    }\n}\n\nreturn msgs;","outputs":3,"noerr":0,"x":390,"y":3840,"wires":[["49805183.4177a"],["5431b04a.5dc02"],["5a6d49c9.335638"]],"outputLabels":["\"a\"","\"b\"","\"c\""]},{"id":"49805183.4177a","type":"debug","z":"58c8eb7a.5496c4","name":"port \"a\"","active":true,"tosidebar":false,"console":false,"tostatus":true,"complete":"payload","x":580,"y":3780,"wires":[]},{"id":"5431b04a.5dc02","type":"debug","z":"58c8eb7a.5496c4","name":"port \"b\"","active":true,"tosidebar":false,"console":false,"tostatus":true,"complete":"payload","x":580,"y":3840,"wires":[]},{"id":"5a6d49c9.335638","type":"debug","z":"58c8eb7a.5496c4","name":"port \"c\"","active":true,"tosidebar":false,"console":false,"tostatus":true,"complete":"payload","x":580,"y":3900,"wires":[]}]

If you prefer not to write any code, you can do the same thing with a split node, set to send each property of the payload object as its own message (using the key as the topic)...

image

... followed by a switch node, that maps each msg to its output port by topic, like so:

image

(sample flow 2)

[{"id":"9e4fedc4.a0955","type":"switch","z":"58c8eb7a.5496c4","name":"by topic","property":"topic","propertyType":"msg","rules":[{"t":"eq","v":"a","vt":"str"},{"t":"eq","v":"b","vt":"str"},{"t":"eq","v":"c","vt":"str"}],"checkall":"true","repair":false,"outputs":3,"x":580,"y":4040,"wires":[["d5047a75.c85468"],["d5806858.1b8b18"],["d59aa3aa.67c95"]]},{"id":"d5047a75.c85468","type":"debug","z":"58c8eb7a.5496c4","name":"topic \"a\"","active":true,"tosidebar":false,"console":false,"tostatus":true,"complete":"payload","x":760,"y":3980,"wires":[]},{"id":"d5806858.1b8b18","type":"debug","z":"58c8eb7a.5496c4","name":"topic \"b\"","active":true,"tosidebar":false,"console":false,"tostatus":true,"complete":"payload","x":760,"y":4040,"wires":[]},{"id":"d59aa3aa.67c95","type":"debug","z":"58c8eb7a.5496c4","name":"topic \"c\"","active":true,"tosidebar":false,"console":false,"tostatus":true,"complete":"payload","x":760,"y":4100,"wires":[]},{"id":"7e995afd.888914","type":"inject","z":"58c8eb7a.5496c4","name":"{ a:1, b:2, c:3, d:4 }","topic":"","payload":"{\"a\":1,\"b\":2,\"c\":3,\"d\":4}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":170,"y":4040,"wires":[["941f49cc.beb298"]]},{"id":"941f49cc.beb298","type":"split","z":"58c8eb7a.5496c4","name":"splitter","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"topic","x":390,"y":4040,"wires":[["9e4fedc4.a0955"]]}]
1 Like

Indeed! I thought of that, but it sounded like the OP was looking for a way to route parts of the msg and not the whole object.

As always, there are several ways to route the entire msg, based on the presence of a named property (and some of them don't even need an expression ;*)

image

Perhaps a more "flow-readable" method is to send the original msg object to multiple change nodes -- each of which builds an output msg with just 1 property's value -- something like this:

(sample flow 3)

[{"id":"37ad1987.7b2ff6","type":"inject","z":"58c8eb7a.5496c4","name":"{ a:1, b:2, c:3, d:4 }","topic":"","payload":"{\"a\":1,\"b\":2,\"c\":3,\"d\":4}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":170,"y":4200,"wires":[["6cefe6d3.147298","6997e808.6b7408","c4d106a9.f82868"]]},{"id":"6cefe6d3.147298","type":"change","z":"58c8eb7a.5496c4","name":"payload \"a\"","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.a","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":410,"y":4160,"wires":[["93c0b54c.86e928"]]},{"id":"6997e808.6b7408","type":"change","z":"58c8eb7a.5496c4","name":"payload \"b\"","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.b","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":410,"y":4200,"wires":[["7f626fc4.cf3f7"]]},{"id":"c4d106a9.f82868","type":"change","z":"58c8eb7a.5496c4","name":"payload \"c\"","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.c","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":410,"y":4240,"wires":[["a98d948a.b4e2f8"]]},{"id":"93c0b54c.86e928","type":"debug","z":"58c8eb7a.5496c4","name":"value \"a\"","active":true,"tosidebar":false,"console":false,"tostatus":true,"complete":"payload","x":580,"y":4140,"wires":[]},{"id":"7f626fc4.cf3f7","type":"debug","z":"58c8eb7a.5496c4","name":"value \"b\"","active":true,"tosidebar":false,"console":false,"tostatus":true,"complete":"payload","x":580,"y":4200,"wires":[]},{"id":"a98d948a.b4e2f8","type":"debug","z":"58c8eb7a.5496c4","name":"value \"c\"","active":true,"tosidebar":false,"console":false,"tostatus":true,"complete":"payload","x":580,"y":4260,"wires":[]}]

Ah, right. Well, sometimes a plain old function node is the best approach!

Sounds like a cross between a switch and a change node... a type of "splitter" function that sends parts of an object to different output ports. Of course, this is doable using a function node with a bit of code

Yes a function node will work, but is not dynamic and not as straight forward as a node with dynamic fields :slight_smile:

Your last example would be the idea, but contained within 1 node.

Dynamic ? You mean the key to look for could come from flow context for example ? ( as long as it was a string)

I meant dynamic in the context of a function node where you have to specify the outputs upfront (as those are not dynamic).

The source of the object would be the same as with switch node, but routing the values by key name, not value. This would (in certain cases) eliminate the need for a change node where one has first to change the property to msg.payload (as required input by other nodes)

the split node could be useful... it can take apart an object into separate messages and copy the key to a different property - which can then be used to route them
image

1 Like

Added Pull request to add "Has Key" option to switch - https://github.com/node-red/node-red/pull/2226

3 Likes