Set-if-not-Set action for the change node

Hi There,

Just thinking about extending a flow to be more reusable.

I have flow that contains is a change node that sets a bunch of values. I now want to add a link-in node in front of that change node and set those same values. Unfortunately the change node would overwrite any values I send in from outside - being after the link-in node.

What I want to do is send in values and have them overwrite the values I set in the change node but that does work since the change node uses 'Set' and the change node comes after the link-in node, so the change node will overwrite any values already set on the msg.

What I was thinking was why isn't there a "set-if-not-set" rule? I.e. something similar to the a ||= 'foo' operator.

If a value is set already, the change node would have no affect for that attribute, else a default value is set for the attribute.

Is this possible already (without using a function node) or have a I missed something?

Yes, with JSONata - but I agree with your ask. I've been doing a lot of work in NR recently and sorely miss a conditional set kinda set.

For JSONata its the ternary operator syntax e.g. foo ? foo : "fallback value" (not clean, but it works)

Ah thanks for that, I didn't think of using JSONata :+1:

I would still prefer a set-if-not-set operator simply because its cleaner and secondly I could use that for all my change nodes and then never have to alter them. Using a jsonata expression would be more clumsy than a single operator - for general usage.

Perhaps others will give their ideas too.

1 Like

I think that using JSONata, you can do this set-if-not-set, fairly cleanly.

$type(foo) != null ? foo : myoldvalue

example
[
    {
        "id": "51c289111ac136a7",
        "type": "inject",
        "z": "eef23a5f7afcdb5c",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 400,
        "y": 1100,
        "wires": [
            [
                "7cbf3552503329b1"
            ]
        ]
    },
    {
        "id": "99c1c3fb7425b81b",
        "type": "debug",
        "z": "eef23a5f7afcdb5c",
        "name": "debug 2581",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 830,
        "y": 1140,
        "wires": []
    },
    {
        "id": "7cbf3552503329b1",
        "type": "change",
        "z": "eef23a5f7afcdb5c",
        "name": "",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "$type(payload) != null ? payload : myoldvalue",
                "tot": "jsonata"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 640,
        "y": 1140,
        "wires": [
            [
                "99c1c3fb7425b81b"
            ]
        ]
    },
    {
        "id": "082111feaa47f180",
        "type": "inject",
        "z": "eef23a5f7afcdb5c",
        "name": "",
        "props": [
            {
                "p": "topic",
                "vt": "str"
            },
            {
                "p": "myoldvalue",
                "v": "7890",
                "vt": "num"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "x": 410,
        "y": 1160,
        "wires": [
            [
                "7cbf3552503329b1"
            ]
        ]
    }
]

super obvious, far simpler than a dropdown option :wink:

Well, JSONata is an acquired taste... Some may not find it clean :wink:

I'm gonna bite :wink:

For me, its not about taste, I have structured reasons.

I know if i get stuck in JS (or other lang) I can find masses of help/resources online. A quick search on www reveals there are easily over 10 million JS devs. I cant find a number for JSONata users but based on some v quick research that number is estimated to be in the 10s of thousands.

So, being it is a niche language, with funky syntax that barely anyone uses (comparatively speaking), often slower to execute than JS itself, has less editor support (yes there are basic completions (I added them) but no real inline help, no error checking, etc) and is just not as capable. It hardly seems worth the effort (to me).

I will concede; It would be a great feature if JS wasn't an option - then I would use it, learn it, run with it. But it is. So i dont (except for super basic stuff like ternarys or setting a value to null in a change node - something else the low-code side of Node-RED should do).

2 Likes

Herein lies the question: is set-if-not-set a functionality that is so "popular" that it deserves a place in the drop down or so niche that a bit of jsonata also does the job.

What functionality should be part of the "low code" and what functionality would only add clutter to the UI and would rarely get used and therefore should not be part of the low-code UI.

I believe that the collection of Unix tools were - eventually - standardised in a POSIX standard so that any Unix OS has a collection of basic commands (e.g. ls, wc, ed, cat, rm, mv, cp, ... etc) but the same standardisation hasn't yet happened in a visual flow environment such as Node-RED.

Hence we no one can say for sure what is low-code functionality and what is an JS/JSONata high-code extension.

I can see a place for this certainly. Must admit I don't tend to seem to need to add defaults part way along a flow, and tend to initially create any complicated objects in a function node up front... but yes I can certainly see it being used.
#notajsonatafan

Purely for historical interest:

Once upon a time I suggested that the change node might benefit from an additional option OS. The value would be passed through to the OS for evaluation as a single line command.

A couple of example usages on a Linux machine (or WSL?)

The idea was of course met with widespread scorn and misunderstanding but I still think it could be of value for people who know OS commands but dislike Jsonata.

I think that would expand the scope of the change node too far. We already have the exec node that explicitly handles calling out to the OS. Adding it in here makes change nodes then possibly be non-cross platform (so would need finding and changing if imported to a different platform) - also lots of error handling and other possible failure modes would need to be handled (long running processes, timeouts, etc) - so no thanks.

I was going to say the same thing, having a OS callout would imply duplicating the code in the exec node.

But it does beg the question what does do-one-task-and-only-one-task-well for a change node mean? The task is fairly clear: change the attributes on the msg object. But how far does the verb change go? Is retrieving a value from the OS still called change? JSONata - I assume because of its statelessness - is ok for a change? An OS call could definitely have stateful interaction with the underlying operating system.

I'm purely wondering how to clearly define what the change node does and doesn't do.

Sometimes it would be nice to have a POSIX standard that one could follow ...