Hi There,
So something that has been interesting me for a long time is structuring HN news stories so that its visually easy to see where all the comments are... well this morning I finally had to procrastinate so much that I created a flow.
First off some details:
- The HN Story is about autism, just randomly.
- I use the firebase API for HN
- For alignment I'm using the auto layout plugin from @BartButenaers
- The automatic creating and importing of nodes into Node-RED, I using the clientcode node from my introspection package.
- The generated nodes are from my closed-source mind-map nodes, so this won't work off the shelve. Instead the node types need to be changed in the client code node code to be something like a template node.
Also this is a complete mis-use of Node-RED flow editor, i.e., it's not a dashboard nor graphing tool.
This is the flow. The HN story id is plugged into the inject and the comments are recursively retrieved. One node per comment and one for the story. Total nodes created in this example was 625. Nodes are automatically linked according to hierarchy.
[{"id":"752ea3b8e5986228","type":"inject","z":"9edd1983093396b8","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"45438346","payloadType":"str","x":97,"y":284,"wires":[["4acf4527c64ef2c5"]]},{"id":"4acf4527c64ef2c5","type":"change","z":"9edd1983093396b8","name":"","rules":[{"t":"set","p":"storyid","pt":"msg","to":"payload","tot":"msg"},{"t":"set","p":"url","pt":"msg","to":"\"https://hacker-news.firebaseio.com/v0/item/\" & $$.storyid & \".json?print=pretty\"","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":323,"y":739,"wires":[["18c0f30926c76bc9"]]},{"id":"8846d85de1bcc165","type":"split","z":"9edd1983093396b8","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":1641,"y":737,"wires":[["4acf4527c64ef2c5"]]},{"id":"18c0f30926c76bc9","type":"http request","z":"9edd1983093396b8","name":"","method":"GET","ret":"obj","paytoqs":"ignore","url":"","persist":false,"insecureHTTPParser":false,"authType":"","senderr":false,"headers":[],"x":514,"y":479,"wires":[["98ff12c452745706"]]},{"id":"5de1ce559efcc054","type":"change","z":"9edd1983093396b8","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"children","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1273,"y":379,"wires":[["8846d85de1bcc165"]]},{"id":"6e2bc30d1b919ba5","type":"change","z":"9edd1983093396b8","name":"","rules":[{"t":"set","p":"parentId","pt":"msg","to":"myNodeId","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"children","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1344,"y":710,"wires":[["8846d85de1bcc165"]]},{"id":"98ff12c452745706","type":"switch","z":"9edd1983093396b8","name":"story or comment?","property":"payload.type","propertyType":"msg","rules":[{"t":"eq","v":"story","vt":"str"},{"t":"eq","v":"comment","vt":"str"}],"checkall":"false","repair":false,"outputs":2,"x":725,"y":479,"wires":[["c7e2636763fe5761"],["5fa9e892a808ec51"]]},{"id":"c7e2636763fe5761","type":"ClientCode","z":"9edd1983093396b8","name":"prepare story node.","clientcode":"let parentNodeId = RED.nodes.id();\n\nmsg.children = msg.payload.kids;\n\nmsg.payload = [{\n \"id\": parentNodeId,\n \"type\": \"Blog-Post\",\n \"name\": msg.payload.title,\n \"info\": msg.payload.url + \"\\n\\n\" + msg.payload.text + \"\\n\\n-- https://news.ycombinator.com/user?id=\" + msg.payload.by + \"\\n\\nSource: https://news.ycombinator.com/item?id=\" + msg.payload.id,\n \"sumPass\": false,\n \"sumPassPrio\": 0,\n \"sumPassNodeId\": \"\",\n \"createdAt\": (new Date(msg.payload.time * 1000)).toISOString(), // (new Date()).toISOString(),\n \"updatedAt\": (new Date()).toISOString(),\n \"wires\": [\n []\n ],\n \"icon\": \"@gorenje/node-red-mindmap/hn.svg\",\n \"l\": false\n}]\n\nmsg.parentId = parentNodeId;\n\nnode.send(msg)\n","format":"javascript","x":1013,"y":379,"wires":[["0636d62671a4a782","5de1ce559efcc054"]]},{"id":"ce597d517d3af8fd","type":"ClientCode","z":"9edd1983093396b8","name":"link parent to child","clientcode":"let source = RED.nodes.node(msg.parentId)\nlet target = RED.nodes.node(msg.myNodeId)\n\nRED.nodes.addLink({ source: source, sourcePort: 0, target: target });\nRED.view.select([target, source]);\n\nnode.send(msg)\n","format":"javascript","x":1015,"y":534.5,"wires":[["03302be69c458330","6e2bc30d1b919ba5"]]},{"id":"5fa9e892a808ec51","type":"switch","z":"9edd1983093396b8","name":"not dead","property":"payload.dead","propertyType":"msg","rules":[{"t":"false"},{"t":"null"}],"checkall":"true","repair":false,"outputs":2,"x":725,"y":533,"wires":[["b53467c717790ab8"],["b53467c717790ab8"]]},{"id":"0636d62671a4a782","type":"ClientCode","z":"9edd1983093396b8","name":"import nodes","clientcode":"/*\nlet nde = [\n {\n \"id\": RED.nodes.id(),\n \"type\": \"Bookmark\",\n \"name\": \"Bookmark\",\n \"info\": \"https://hacker-news.firebaseio.com/v0/item/45438346.json?print=pretty\\n\\n\",\n \"sumPass\": false,\n \"sumPassPrio\": 0,\n \"sumPassNodeId\": \"\",\n \"createdAt\": \"2025-10-02T07:00:08.553Z\",\n \"updatedAt\": \"2025-10-02T07:00:08.553Z\",\n \"x\": 588,\n \"y\": 247,\n \"wires\": [\n []\n ],\n \"icon\": \"@gorenje/node-red-mindmap/hn.svg\"\n }\n]\n*/\n\nlet nde = msg.payload;\n\nRED.view.importNodes(Array.isArray(nde) ? nde : [nde])\n\nnode.send(msg)\n","format":"javascript","x":1245,"y":302,"wires":[[]]},{"id":"2a677cc320235746","type":"ClientCode","z":"9edd1983093396b8","name":"import nodes","clientcode":"let nde = msg.payload;\n\nRED.view.importNodes(Array.isArray(nde) ? nde : [nde])\n\nnode.send(msg)\n","format":"javascript","x":1015,"y":587.75,"wires":[["ce597d517d3af8fd"]]},{"id":"03302be69c458330","type":"debug","z":"9edd1983093396b8","name":"debug 346","active":true,"tosidebar":false,"console":false,"tostatus":true,"complete":"true","targetType":"full","statusVal":"","statusType":"counter","x":1293,"y":534.5,"wires":[]},{"id":"b53467c717790ab8","type":"switch","z":"9edd1983093396b8","name":"not deleted","property":"payload.deleted","propertyType":"msg","rules":[{"t":"false"},{"t":"null"}],"checkall":"true","repair":false,"outputs":2,"x":725,"y":587,"wires":[["5a16e03d1724667c"],["5a16e03d1724667c"]]},{"id":"b32ed12fe4aee645","type":"ClientCode","z":"9edd1983093396b8","name":"prepare comment node.","clientcode":"\nlet myNodeId = RED.nodes.id();\n\nmsg.children = msg.payload.kids;\n\nmsg.payload = [{\n \"id\": myNodeId,\n \"type\": \"Comment\",\n \"name\": msg.payload.text.substr(0, 30) + (msg.payload.text.length > 30 ? \"...\" : \"\"),\n \"info\": msg.payload.text + \"\\n\\n-- https://news.ycombinator.com/user?id=\" + msg.payload.by + \"\\n\\nSource: https://news.ycombinator.com/item?id=\" + msg.payload.id,\n \"sumPass\": false,\n \"sumPassPrio\": 0,\n \"sumPassNodeId\": \"\",\n \"createdAt\": (new Date(msg.payload.time * 1000)).toISOString(), // (new Date()).toISOString(),\n \"updatedAt\": (new Date()).toISOString(),\n \"wires\": [\n []\n ],\n \"icon\": \"@gorenje/node-red-mindmap/hn.svg\",\n \"l\": false\n}]\n\nmsg.myNodeId = myNodeId;\n\nnode.send(msg)\n","format":"javascript","x":1015,"y":641,"wires":[["2a677cc320235746"]]},{"id":"5a16e03d1724667c","type":"switch","z":"9edd1983093396b8","name":"not flagged","property":"payload.text","propertyType":"msg","rules":[{"t":"neq","v":"[flagged]","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":725,"y":641,"wires":[["b32ed12fe4aee645"]]}]
Doing the importing, nodes are created without labels for compactness. The import took a minute and half, longer than the gif:
Once the nodes are imported, I can alignment them - automagically - into a tree structure as comments on the story, followed by the comments on comments, etc. Alignment algorithm was "ELKjs Layered Downward":
Also shown is that the content of comments and links to the comments are stored in the node and can be viewed in the info panel. So I can also navigate through the structure reading those comments of interest.
Why?
Because I wanted to include this in my mindmap. So that when I drag a link to a HN story into my mindmap, it would also create all the comments and subcomments of the story. That step isn't shown here.