Sort node NOT broken - labelling perhaps confusing

Hi There,

I've just created a test flow for testing the functionality of the sort node:

[{"id":"68dcdddf9a7ee3cd","type":"inject","z":"5f6929bb3374b782","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[2,3,1,5]","payloadType":"json","x":336.5,"y":925.5,"wires":[["aa81c2f54b39c43a"]]},{"id":"aa81c2f54b39c43a","type":"split","z":"5f6929bb3374b782","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","property":"payload","x":462,"y":991,"wires":[["5948a955bd6357a0","959be4a3354c98a1"]]},{"id":"0d9827ef535d0dd3","type":"delay","z":"5f6929bb3374b782","name":"","pauseType":"delayv","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"100","randomLast":"500","randomUnits":"milliseconds","drop":false,"allowrate":false,"outputs":1,"x":765,"y":1170,"wires":[["3c70b3f97b8d63a0"]]},{"id":"3c70b3f97b8d63a0","type":"sort","z":"5f6929bb3374b782","name":"","order":"ascending","as_num":true,"target":"","targetType":"seq","msgKey":"payload","msgKeyType":"elem","seqKey":"payload","seqKeyType":"msg","x":940,"y":1088,"wires":[["baf71f9b1453486c"]]},{"id":"5948a955bd6357a0","type":"change","z":"5f6929bb3374b782","name":"","rules":[{"t":"set","p":"delay","pt":"msg","to":"(30 - $$.payload) * 100","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":616,"y":1072,"wires":[["0d9827ef535d0dd3"]]},{"id":"959be4a3354c98a1","type":"debug","z":"5f6929bb3374b782","name":"debug 446","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"$$.payload & \" => \" & $$.parts.index","targetType":"jsonata","statusVal":"","statusType":"auto","x":763,"y":948,"wires":[]},{"id":"baf71f9b1453486c","type":"debug","z":"5f6929bb3374b782","name":"debug 447","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"$$.payload & \" => \" & $$.parts.index","targetType":"jsonata","statusVal":"","statusType":"auto","x":1152,"y":1006,"wires":[]}]

What this does is sort the array of values [2,3,1,5] when this goes through the split node, the 2 gets index 0, the 3 gets index 1 ... as shown in the first four values in the debug log:

What I don't understand are the last four outputs. I configured the sort node to sort by "message sequence" so I would have expected the same order, i.e. [2,3,1,5] but instead the order becomes 1.2.3.5 with the index values modified to match that ordering, i.e. index of 2 becomes 1 (not zero), 3 becomes 2 (not 1).

What I don't understand is why the sort node is doing that?

The configuration of the sort node is:

I would have thought that message sequence is the sorting mechanism and not the payload even though it is the key.

The documentation states:

The only thing between the split and sort nodes is a change & delay nodes to ensure that the messages arrive in reverse order - the change sets the delay according to inverse payload value.

I can fix and get the expected result by setting the key to be a JSONata expression of "$$.parts.index" and then it's sorted correctly but that shouldn't be case - surely.

Even though you set the option to work with a sequence, you are still asking it to sort on the "key" msg.payload (which it is doing)

If you change the key to msg.parts.index it sorts by msg.parts.index

By asking it to work with a sequence it stores the input values up until the last part index is received THEN does the sorting based on your key. This is implied in the help text "This node internally keeps messages for its operation. In order to prevent unexpected memory usage, maximum number of messages kept can be specified. Default is no limit on number of messages."

For me that sentence is about that it stores messages - ok it stores messages but sorting is done by .... the key? That isn't clear in that sentence.

When sorting a message sequence, the sort node relies on the received messages to have msg.parts set. The split node generates this property, but can be manually created. It has the following properties:

Ok, rereading that, all it says that it requires the parts field to be set but that sorting is done ... still using the key value.

But ok, I understand that the sequence part is only used so the sort node knows when to stop listening for more messages - it uses the sequence to know when it's done and when it can send out its sorted list.

Perhaps the labelling could be adjusted:

The label is confusing: "Sort" and "Key" -- hm ... "sort message sequence" --> ok sort by message sequence is what I read.

Which brings me to another use case I thought would be possible with the sort node: passing in an array (created by the join node) that contains messages and have the sort node sort that array - but it doesn't do that. Which is a pity since that's the use case I'm looking for ...

Are you sure? Can you do a quick simple importable demo showing what it does and what you wished it did?

I agree it is not perfect. If it said

"Sort a" : "message sequence"
"on key" : "msg.payload"

it would read better (to me) but one still needs an understanding of the whole parts thing.

1 Like

No in fact it does when the Sort is set to to msg.payload and key set to whatever. My mistake was setting sort to message sequence, so now it works as expected. :+1:

I would agree with the "on key" but the "sort a" ... hm .. or "sort a msg.payload" makes sense, but "sort a message sequence" makes no sense for me ....

It's the "message sequence" because what that is implying is "create an array based on the parts attribute of the messages, once all messages have arrived, sort on key ....." - that's what it should be - for me :wink:

Btw what does "element value" mean here if I have a payload with an array of messages:

Screenshot 2025-08-10 at 13.00.29

I guess that is when the payload is [1,2,3,4,5] and the JSONata expression is when the members of the array are objects ... as in my case.

Sorry one more question: the sort node does not respect msg.complete = true? I just put this together and the sort node does not generate a message:

[{"id":"5b63b47bd8254522","type":"inject","z":"cffa610c817beb43","name":"","props":[{"p":"payload"},{"p":"total","v":"10","vt":"num"},{"p":"count","v":"0","vt":"num"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"hello world","payloadType":"str","x":388,"y":442,"wires":[["b3e4bcea02be4af1"]]},{"id":"b3e4bcea02be4af1","type":"change","z":"cffa610c817beb43","name":"","rules":[{"t":"set","p":"parts","pt":"msg","to":"{ \"index\": $$.count  }","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":661,"y":624.25,"wires":[["1a9fbcdc20947ea4","291b34c6a1271032"]]},{"id":"1a9fbcdc20947ea4","type":"switch","z":"cffa610c817beb43","name":"","property":"count","propertyType":"msg","rules":[{"t":"lt","v":"$$.total","vt":"jsonata"},{"t":"else"}],"checkall":"false","repair":false,"outputs":2,"x":549,"y":977,"wires":[["a9e13c55fd558934"],["99732d67b34fa6b3"]]},{"id":"a9e13c55fd558934","type":"change","z":"cffa610c817beb43","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"$random()","tot":"jsonata"},{"t":"set","p":"count","pt":"msg","to":"$$.count + 1","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":789,"y":908,"wires":[["b3e4bcea02be4af1"]]},{"id":"99732d67b34fa6b3","type":"change","z":"cffa610c817beb43","name":"","rules":[{"t":"set","p":"complete","pt":"msg","to":"true","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":841,"y":982,"wires":[["291b34c6a1271032"]]},{"id":"768c1b850f87b34e","type":"join","z":"cffa610c817beb43","name":"","mode":"custom","build":"array","property":"","propertyType":"full","key":"topic","joiner":"\\n","joinerType":"str","useparts":true,"accumulate":false,"timeout":"","count":"","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":1217,"y":727,"wires":[["8f5fb33f71e2a090"]]},{"id":"1723ee1fe93de436","type":"sort","z":"cffa610c817beb43","name":"","order":"ascending","as_num":false,"target":"","targetType":"seq","msgKey":"payload","msgKeyType":"elem","seqKey":"payload","seqKeyType":"msg","x":1289,"y":624.25,"wires":[["68803057874fe2c1"]]},{"id":"68803057874fe2c1","type":"debug","z":"cffa610c817beb43","name":"debug 443","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1665,"y":409,"wires":[]},{"id":"8f5fb33f71e2a090","type":"debug","z":"cffa610c817beb43","name":"debug 444","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1661,"y":809,"wires":[]},{"id":"291b34c6a1271032","type":"junction","z":"cffa610c817beb43","x":1047.8775284290314,"y":624.25,"wires":[["768c1b850f87b34e","1723ee1fe93de436"]]}]

where as the join does when the complete message is received.

EDIT: corrected flow definition