How to get the summary of nodes used in node-red?

Hi,

I saw in one of your posts posting summary of nodes used(Showing number of them are used).

Then I got curious how to do that because I am doing stream analytics using node-red with MQTT and data is coming form OPC.

I have couple of flows like more than 400 to 500 MQTT nodes. So this will be helpful information for me to know how many exactly am I using.

Is there any function I can execute in console to get the summary of nodes used in node-red or that info will be available in exported JSON flow file?

Sorry, I can't remember who first came up with this, I just copied it. Don't forget to change the filename to your own flows file. Mine is in a non-standard location and has a non-standard name.

[
    {
        "id": "c34db4d4.2863a8",
        "type": "file in",
        "z": "9974253c.de8db8",
        "name": "",
        "filename": "/home/pi//node/nrlive/.data/flows.json",
        "format": "utf8",
        "chunk": false,
        "sendError": false,
        "x": 330,
        "y": 480,
        "wires": [
            [
                "113a5dd3.0310f2"
            ]
        ]
    },
    {
        "id": "113a5dd3.0310f2",
        "type": "json",
        "z": "9974253c.de8db8",
        "name": "",
        "pretty": false,
        "x": 550,
        "y": 480,
        "wires": [
            [
                "213eb35d.27e87c"
            ]
        ]
    },
    {
        "id": "213eb35d.27e87c",
        "type": "change",
        "z": "9974253c.de8db8",
        "name": "",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "payload^(type) {\t    \"TOTAL\": $count($$.payload),\t    $substringBefore(type, \":\"): $count([$])\t}",
                "tot": "jsonata"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 720,
        "y": 480,
        "wires": [
            [
                "a8fa7ea8.800bf"
            ]
        ]
    },
    {
        "id": "a8fa7ea8.800bf",
        "type": "debug",
        "z": "9974253c.de8db8",
        "name": "",
        "active": true,
        "console": "false",
        "complete": "false",
        "x": 910,
        "y": 480,
        "wires": []
    },
    {
        "id": "16fe06b3.a34209",
        "type": "inject",
        "z": "9974253c.de8db8",
        "name": "",
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "repeat": "",
        "crontab": "",
        "once": false,
        "x": 100,
        "y": 480,
        "wires": [
            [
                "c34db4d4.2863a8"
            ]
        ]
    },
    {
        "id": "43a5f911.e2c248",
        "type": "comment",
        "z": "9974253c.de8db8",
        "name": "How Many Nodes in my flow?",
        "info": "",
        "x": 160,
        "y": 440,
        "wires": []
    }
]
```
2 Likes

or install node-red-contrib-flow-statistics

1 Like

@TotallyInformation and @cflurin Thanks for the information.

This are my current stats: :joy::rofl::joy:

{
"TOTAL":2358,
"CassandraDatabase":1,
"cassandra":1,
"catch":12,
"change":1,
"comment":300,
"debug":102,
"delay":19,
"file":37,
"file in":1,
"function":1025,
"http request":44,
"inject":72,
"json":1,
"mqtt in":712,
"mqtt-broker":12,
"switch":2,
"tab":15,
"ui_base":1
}

Both solutions work. But can't accept multiple as solution.

1 Like

It's up to you, you always have a choice. Anyway node-red-contrib-flow-statistics takes less then 1 seconds to do the job.

1 Like

Here is a Friday twiddle for you all. An update to the original flow.

In this update, I've corrected a couple of issues and I've added a function node version as well as the JSONata version. In fact, the function node version of the flow is rather more complete and accurate because the original didn't take into account the fact that tabs and subflow definitions were included in the count.

Please remember to change the file name to match your own.

[
    {
        "id": "1dd9f4ac.8347eb",
        "type": "file in",
        "z": "396e6d9.614e192",
        "name": "",
        "filename": "/home/pi/nrlive/.data/flows.json",
        "format": "utf8",
        "chunk": false,
        "sendError": false,
        "x": 350,
        "y": 1900,
        "wires": [
            [
                "7db3c4dc.803e6c"
            ]
        ]
    },
    {
        "id": "51fb56d2.8ca248",
        "type": "inject",
        "z": "396e6d9.614e192",
        "name": "",
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "repeat": "",
        "crontab": "",
        "once": false,
        "x": 140,
        "y": 1900,
        "wires": [
            [
                "1dd9f4ac.8347eb"
            ]
        ]
    },
    {
        "id": "2d29205.cb6abe",
        "type": "comment",
        "z": "396e6d9.614e192",
        "name": "How Many Nodes in my flow?",
        "info": "Don't forget to change the path to your flow file.\n\nNote the oddity regarding subflows. \nThey appear firstly as type \"subflow\" which is the\ndefinition, then again in the form\n\"subflow:<id>\" once for each instance of that\nsubflow `<id>` in your flows.\n\nI show both a JSONata and a function node\napproach to getting the data but note that\nthe JSONata version _is not quite correct_.\nIn the sense that it doesn't subtract the tabs\nand subflows from the overal counts.\n\nSo while the execution time of both approaches\nseems to be pretty identical, the function node\nis more comprehensive.",
        "x": 200,
        "y": 1860,
        "wires": []
    },
    {
        "id": "7db3c4dc.803e6c",
        "type": "json",
        "z": "396e6d9.614e192",
        "name": "",
        "pretty": false,
        "x": 610,
        "y": 1900,
        "wires": [
            [
                "3aeb5629.c2b33a",
                "53264ae7.07a974",
                "9c8f63ea.e8083"
            ]
        ]
    },
    {
        "id": "53264ae7.07a974",
        "type": "debug",
        "z": "396e6d9.614e192",
        "name": "",
        "active": false,
        "console": "false",
        "complete": "false",
        "x": 770,
        "y": 1860,
        "wires": []
    },
    {
        "id": "3aeb5629.c2b33a",
        "type": "change",
        "z": "396e6d9.614e192",
        "name": "",
        "rules": [
            {
                "t": "set",
                "p": "totalNodes",
                "pt": "msg",
                "to": "$count(payload)\t",
                "tot": "jsonata"
            },
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "payload^(type) {\t   `type`: $count([$])\t}",
                "tot": "jsonata"
            },
            {
                "t": "set",
                "p": "nodeTypesCount",
                "pt": "msg",
                "to": "$count($keys(payload))",
                "tot": "jsonata"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 780,
        "y": 1900,
        "wires": [
            [
                "5de6fbf7.a75344"
            ]
        ]
    },
    {
        "id": "9c8f63ea.e8083",
        "type": "function",
        "z": "396e6d9.614e192",
        "name": "",
        "func": "msg.totalNodes = msg.payload.length\n\nlet tabsCount = 0, subflowsCount = 0, nodeTypesCount = 0\nlet totalNodes = 0\nlet tabs = {}, subflows = {}, nodeTypes = {}\n\nmsg.payload.sort(function(a, b) {\n  var typeA = a.type.toUpperCase(); // ignore upper and lowercase\n  var typeB = b.type.toUpperCase(); // ignore upper and lowercase\n  if (typeA < typeB) {\n    return -1;\n  }\n  if (typeA > typeB) {\n    return 1;\n  }\n\n  // names must be equal\n  return 0;\n})\n\nmsg.payload.forEach((el) => {\n    if ( el.type === 'tab' ) {\n        tabsCount++\n        tabs[el.label] = el.id\n    } else if ( el.type === 'subflow' ) {\n        subflowsCount++\n        subflows[el.name] = el.id\n    } else {\n        nodeTypes[el.type] = nodeTypes[el.type]+1 || 1\n        totalNodes++\n    }\n})\n\nmsg.payload = {\n    'totalNodesCount': totalNodes,\n    'tabsCount': tabsCount,\n    'subflowsCount': subflowsCount,\n    'nodeTypesCount': Object.keys(nodeTypes).length,\n    'tabs': tabs,\n    'subflows': subflows,\n    'nodeTypes': nodeTypes,\n}\n\nreturn msg",
        "outputs": 1,
        "noerr": 0,
        "x": 715,
        "y": 1940,
        "wires": [
            [
                "97a2dead.fea94"
            ]
        ],
        "l": false
    },
    {
        "id": "5de6fbf7.a75344",
        "type": "debug",
        "z": "396e6d9.614e192",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "x": 950,
        "y": 1900,
        "wires": []
    },
    {
        "id": "97a2dead.fea94",
        "type": "debug",
        "z": "396e6d9.614e192",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "x": 970,
        "y": 1940,
        "wires": []
    }
]
2 Likes

TotallyInformation,
Thank you!

TotallyInformation,

Here is a Tuesday tweek that shows the subflow name, rather than ID, in the NodeType array.

[
    {
        "id": "ad6b0c8.5cbdf7",
        "type": "tab",
        "label": "Flow 1",
        "disabled": false,
        "info": ""
    },
    {
        "id": "28d1a688.815bf2",
        "type": "file in",
        "z": "ad6b0c8.5cbdf7",
        "name": "",
        "filename": "/home/homemanadmin/.node-red/projects/nodered-homeassistant/flows_homeMan-server.json",
        "format": "utf8",
        "chunk": false,
        "sendError": false,
        "x": 478,
        "y": 122,
        "wires": [
            [
                "add3390.9a420c8"
            ]
        ]
    },
    {
        "id": "db4d5d00.4c5398",
        "type": "inject",
        "z": "ad6b0c8.5cbdf7",
        "name": "",
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "repeat": "",
        "crontab": "",
        "once": false,
        "x": 140,
        "y": 46,
        "wires": [
            [
                "28d1a688.815bf2"
            ]
        ]
    },
    {
        "id": "65c27239.2cc224",
        "type": "comment",
        "z": "ad6b0c8.5cbdf7",
        "name": "How Many Nodes in my flow?",
        "info": "Don't forget to change the path to your flow file.\n\nNote the oddity regarding subflows. \nThey appear firstly as type \"subflow\" which is the\ndefinition, then again in the form\n\"subflow:<id>\" once for each instance of that\nsubflow `<id>` in your flows.\n\nI show both a JSONata and a function node\napproach to getting the data but note that\nthe JSONata version _is not quite correct_.\nIn the sense that it doesn't subtract the tabs\nand subflows from the overal counts.\n\nSo while the execution time of both approaches\nseems to be pretty identical, the function node\nis more comprehensive.",
        "x": 248,
        "y": 230,
        "wires": []
    },
    {
        "id": "add3390.9a420c8",
        "type": "json",
        "z": "ad6b0c8.5cbdf7",
        "name": "",
        "pretty": false,
        "x": 658,
        "y": 270,
        "wires": [
            [
                "605c3b4b.6e01b4"
            ]
        ]
    },
    {
        "id": "9beb91be.19b6a",
        "type": "debug",
        "z": "ad6b0c8.5cbdf7",
        "name": "",
        "active": false,
        "console": "false",
        "complete": "false",
        "x": 892,
        "y": 182,
        "wires": []
    },
    {
        "id": "d947f2a5.e08a8",
        "type": "change",
        "z": "ad6b0c8.5cbdf7",
        "name": "",
        "rules": [
            {
                "t": "set",
                "p": "totalNodes",
                "pt": "msg",
                "to": "$count(payload)\t",
                "tot": "jsonata"
            },
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "payload^(type) {\t   `type`: $count([$])\t}",
                "tot": "jsonata"
            },
            {
                "t": "set",
                "p": "nodeTypesCount",
                "pt": "msg",
                "to": "$count($keys(payload))",
                "tot": "jsonata"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 908,
        "y": 300,
        "wires": [
            [
                "f6b9ee3c.963568"
            ]
        ]
    },
    {
        "id": "605c3b4b.6e01b4",
        "type": "function",
        "z": "ad6b0c8.5cbdf7",
        "name": "",
        "func": "msg.totalNodes = msg.payload.length\n\nlet posColon = 0\nlet typeLen = 0\nlet tabsCount = 0\nlet subflowsCount = 0\nlet nodeTypesCount = 0\nlet totalNodes = 0\nlet tabs = {}\nlet subflows = {}\nlet nodeTypes = {}\nvar typeName\nvar nodeType\n\nfunction getKeyByValue(object, value) {\n  return Object.keys(object).find(key => object[key] === value);\n}\n\n\nmsg.payload.sort(function(a, b) {\n  var typeA = a.type.toUpperCase(); // ignore upper and lowercase\n  var typeB = b.type.toUpperCase(); // ignore upper and lowercase\n  if (typeA < typeB) {\n    return -1;\n  }\n  if (typeA > typeB) {\n    return 1;\n  }\n\n  // names must be equal\n  return 0;\n})\n\nmsg.payload.forEach((el) => \n{\n    if ( el.type === 'tab' ) \n    {\n        tabsCount++\n        tabs[el.label] = el.id\n    } \n    else if ( el.type === 'subflow' ) \n    {\n        subflowsCount++\n        subflows[el.name] = el.id\n    } \n    else \n    {\n        if (el.type.indexOf(\"subflow:\") >= 0)            \n        {\n            typeName = el.type.split(\":\").pop();\n            nodeType = \"subflow \" + getKeyByValue(subflows,typeName);\n            nodeTypes[nodeType] = nodeTypes[nodeType]+1 || 1\n            totalNodes++\n        }\n        else\n        {\n            nodeTypes[el.type] = nodeTypes[el.type]+1 || 1\n            totalNodes++\n        }\n    }\n}\n)\n\nmsg.payload = {\n    'totalNodesCount': totalNodes,\n    'tabsCount': tabsCount,\n    'subflowsCount': subflowsCount,\n    'nodeTypesCount': Object.keys(nodeTypes).length,\n    'tabs': tabs,\n    'subflows': subflows,\n    'nodeTypes': nodeTypes,\n}\n\nreturn msg",
        "outputs": 1,
        "noerr": 0,
        "x": 765,
        "y": 430,
        "wires": [
            [
                "fa0e5742.bbaa58"
            ]
        ]
    },
    {
        "id": "f6b9ee3c.963568",
        "type": "debug",
        "z": "ad6b0c8.5cbdf7",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "x": 1128,
        "y": 300,
        "wires": []
    },
    {
        "id": "fa0e5742.bbaa58",
        "type": "debug",
        "z": "ad6b0c8.5cbdf7",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "x": 1002,
        "y": 432,
        "wires": []
    }
]
1 Like