"Has Anything Changed" Node?

Hello!

I think a useful node to have would be one which takes as input information from the current workflow run and compares it to a previous workflow run. Basically an easy way to tell “did something change?”

I think n8n would be great to use to get alerts from things online that don’t support alerts on their own, but it turns out knowing whether anything has changed is a big part of that. Even something ‘straightforward’ like sending an alert when an RSS feed includes a given search term would end up triggering endlessly so long as the term remains in the feed.

I understand that there are ways to do it in Javascript, but this seems like something that would come up often enough that built-in support might be useful.

I imagine it looking and working similar to the “IF” node’s “string” condition. Perhaps one could even select whether it would compare to “Last run”, “Any time in last x runs” or “any time in last x days”.

Hope that this makes sense and is in the appropriate forum.

It’s almost like you can read my pm’s :expressionless: scary
But yeah i think this is a great and needed idea

@Binks really like the idea behind this. For your usecases, do you envision that the output for each item is a BOOL (i.e. did change or didn’t change) or is actually formatting the data that did change (i.e. like in a DIFF comparison of two text documents).

@maxT I think for my usecase the output would be a BOOL. I envision the node being used near the end of a workflow to stop getting repeated alerts when the source data hasn’t changed.

As perhaps the simplest example, Environment Canada makes XML files available that provide weather alerts for specific cities or regions:
https://weather.gc.ca/rss/battleboard/nu13_e.xml

A simple workflow would be to look at the entry’s title, and if it changes from “No alerts in effect, Iqaluit” to anything else, an alert could be sent.

I guess it gets more confusing if you were monitoring a feed with more than 1 item, and perhaps 2 new items were added between CRON runs. In that circumstance I would imagine the node comparing each item to all previous items, and passing along the new items. The net effect would be something like, “send me all the new news articles”.

Thanks @Binks! Sounds like the above scenario could be achieved with the IF node, where you’d be evaluating if title in XML file is equal to “No alerts in effect, Iqaluit” (would be a single string condition in the IF node).

I’m trying to think of scenarios where an IF node would not work for routing the WF data based on evaluating a conditional statement that has two outcomes (true or false). But at this time I can’t think of a case. Please do let me know if the IF node works for your scenario or if you have some cases where this method would not work.

If text diff output is something anyone would be interested in - please do comment on this thread and from there we could assess if it makes sense as a new mode for the Merge node.

Hi @maxT!

I don’t think I understand - the RSS node isn’t a trigger, so my understanding is that the workflow needs to be triggered by Cron every x minutes.

Every workflow run the RSS feed is pulled and evaluated by the IF, and so long as it remains true the notification fires.

So if you check the feeds for updates every minute, every minute you receive an alert with, “No alerts in effect, Iqaluit”.

How can the IF node suppress these superfluous triggers?

In my case i’m looking for a DIFF Comparison

Hi Binks,

To help visualize I mocked up a quick flow:

I’m using the NoOp nodes to show the different cases (i.e. one for true, one for false). In a real WF, you could keep the “Do nothing” No Op node and replace “continue + do something” with whatever you like (Send an email, post to slack etc).

In the IF node, it would look something like:

Now this is a simplification, but just wanted to check if something wasn’t clear in my original post as I am rather sure the IF node is the solution for your case. Let me know!

Hi @Damian_K,

So abstractly I see the utility in this node but a bit unsure as to the actual implementation. Off the bat, seems like of course the new text itself would be helpful but also maybe the anchor (i.e. where it came from in the file), or what it overwrote. If possible, most useful would be the exact JSON output you’d want to see when sending in two inputs (i.e. a before and after) - short of that though, details on how you’d want to use this data would be equally helpful.

ty!

Hi @maxT Thank you for diagramming it out. That’s exactly the workflow I’ve got; but IF doesn’t solve my problem.

The problem is that, every single time Cron runs, if the RSS feed’s content is “No alerts in effect” the “Continue + do something” node runs. So if you imagine the Cron is checking for updates every minute, every minute “Continue + do something” is triggered for as long as the “IF” evaluates true.

So every minute an email gets sent, or a message gets posted to slack, etc. That behavour is undesirable, and the problem I don’t think is solved with existing nodes.

The “Has Anything Changed” node I’m proposing could go in a few places, but to help visualize, it would sit between the RSS Feed Read and the IF node, and simply evaluate whether the string is the same as it was the last time that the workflow was run. That way the RSS feed gets checked every minute, but the IF node only gets run when the content of the RSS feed is updated.

The example on polling that I linked in my original post solves the problem with javascript; but I am proposing that javascript should be unnecessary for simple workflow like what we’re talking about.

The solution in that blog post is for new data, i’m more interested in changes in the data

I wonder if an expression value for a previous workflow job (like $lastRun.$node[...]['field']) could do the trick? Then you could use the IF statement to compare the current data vs the previous job.

1 Like

This should be addressed in ⚡ Add FilterNew node that reports new or unseen items by fahhem · Pull Request #2310 · n8n-io/n8n · GitHub, please let me know if it doesn’t work for you. You can get the changed items by filtering for either a date field in the JSON, which will be compared to previous runs and other items, or by creating a unique identifier for items (usually by picking one or two fields in the item) and then this node will only allow items through once.

Hello,
I just finished implementing n8n-nodes-changed, This node enables you to detect if the input data has changed between the current execution and the previous one and redirect the stream based on that.
Would this cover your use case?

2 Likes

Woow, would be great!

Thanks

I think that would cover my use case, yes!

1 Like

I’ve installed this node and can’t get it to function as I think it is intended. I’m very new to n8n, so could be I’m doing something wrong.

I want to get all active projects from Harvest, parse them for new projects, and create respective new cards in Trello and send a messages to a Slack channel.

I’ve gotten the Trello card creation and Slack message to work, but the issue is that it is repeating the projects already created.

  • Creating project 1 = project 1 trello card and slack notification (like it should).
  • Creating project 2 = project 1 AND project 2 trello card and slack notification (duplicates project 1)
  • Creating project 3 = project 1 AND project 2 AND project 3 (duplicates project 1 and 2)
    etc.

I thought the whole point of this node was to “know” that i.e “project 1” already was parsed, but it seems like it takes the complete json output and registers it as new if there is a new project.

I hope this makes sense.

A workaround is to limit the output from the Harvest node to 1. This is mostly fine since I’m polling the Harvest API every 20 seconds and we don’t create that many projects. But if multiple projects are created within the 20-second interval (that happens sometimes), only one will be “approved”.

Here is my workflow (I’ve removed pretty much everything of my custom data that is not relevant.):

{
  "nodes": [
    {
      "parameters": {},
      "name": "Start",
      "type": "n8n-nodes-base.start",
      "typeVersion": 1,
      "position": [
        120,
        120
      ]
    },
    {
      "parameters": {
        "resource": "project",
        "accountId": <removed>,
        "limit": 100,
        "filters": {
          "is_active": true,
          "updated_since": "2022-05-18T09:01:00.000Z"
        }
      },
      "name": "Harvest",
      "type": "n8n-nodes-base.harvest",
      "typeVersion": 1,
      "position": [
        420,
        300
      ],
      "credentials": {
        "harvestApi": {
          "id": "1",
          "name": "Harvest account"
        }
      }
    },
    {
      "parameters": {
        "interval": 30
      },
      "name": "Interval",
      "type": "n8n-nodes-base.interval",
      "typeVersion": 1,
      "position": [
        120,
        300
      ]
    },
    {
      "parameters": {
        "listId": "=<removed>",
        "name": "=<removed>",
        "description": "<removed>",
        "additionalFields": {
          "pos": "=top"
        }
      },
      "name": "Trello",
      "type": "n8n-nodes-base.trello",
      "typeVersion": 1,
      "position": [
        1000,
        200
      ],
      "credentials": {
        "trelloApi": {
          "id": "3",
          "name": "Trello account"
        }
      }
    },
    {
      "parameters": {
        "channel": "<removed>",
        "text": "<removed>,
        "jsonParameters": "=",
        "otherOptions": {
          "icon_url": "<removed>",
          "sendAsUser": "<removed>"
        },
        "attachments": []
      },
      "name": "Slack",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 1,
      "position": [
        1000,
        380
      ],
      "credentials": {
        "slackApi": {
          "id": "2",
          "name": "<removed>"
        }
      }
    },
    {
      "parameters": {
        "defaultValue": "=true"
      },
      "name": "hasChanged",
      "type": "CUSTOM.hasChanged",
      "typeVersion": 1,
      "position": [
        680,
        300
      ]
    },
    {
      "parameters": {},
      "name": "NoOp",
      "type": "n8n-nodes-base.noOp",
      "typeVersion": 1,
      "position": [
        1000,
        560
      ]
    }
  ],
  "connections": {
    "Harvest": {
      "main": [
        [
          {
            "node": "hasChanged",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Interval": {
      "main": [
        [
          {
            "node": "Harvest",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "hasChanged": {
      "main": [
        [
          {
            "node": "Slack",
            "type": "main",
            "index": 0
          },
          {
            "node": "Trello",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "NoOp",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

JSON output from the Harvest node:

[{
        "id": 32913606,
        "name": "Project 1",
        "code": "1234",
        "is_active": true,
        "is_billable": true,
        "is_fixed_fee": false,
        "bill_by": "Project",
        "budget": null,
        "budget_by": "none",
        "budget_is_monthly": false,
        "notify_when_over_budget": false,
        "over_budget_notification_percentage": 80,
        "show_budget_to_all": false,
        "created_at": "2022-05-18T10:16:26Z",
        "updated_at": "2022-05-18T10:16:26Z",
        "starts_on": null,
        "ends_on": null,
        "over_budget_notification_date": null,
        "notes": "",
        "cost_budget": null,
        "cost_budget_include_expenses": false,
        "hourly_rate": 1200,
        "fee": null,
        "client": {
            "id": 8090079,
            "name": "Test",
            "currency": "NOK"
        }
    },
    {
        "id": 32913382,
        "name": "Project 2",
        "code": "2345",
        "is_active": true,
        "is_billable": true,
        "is_fixed_fee": false,
        "bill_by": "Project",
        "budget": null,
        "budget_by": "none",
        "budget_is_monthly": false,
        "notify_when_over_budget": false,
        "over_budget_notification_percentage": 80,
        "show_budget_to_all": false,
        "created_at": "2022-05-18T09:47:54Z",
        "updated_at": "2022-05-18T09:47:54Z",
        "starts_on": null,
        "ends_on": null,
        "over_budget_notification_date": null,
        "notes": "",
        "cost_budget": null,
        "cost_budget_include_expenses": false,
        "hourly_rate": 1200,
        "fee": null,
        "client": {
            "id": 2324327,
            "name": "Test",
            "currency": "NOK"
        }
    }
]

Thanks!