
大家好,欢迎来到这篇 n8n 深度教程。许多朋友在使用 n8n 搭建自动化工作流时,会遇到一个常见的问题:当数据从一个节点流向下个节点时,如何根据不同的情况进行处理?比如,怎样只处理符合特定条件的数据?如何让工作流根据数据内容“兵分两路”执行不同操作?怎样重复处理一批项目?又或者,如何将来自不同流程分支的数据合并到一起?
翔宇在帮助众多用户实现自动化需求的过程中发现,掌握 n8n 中的逻辑控制节点是解决这些问题的关键。它们就像工作流的“大脑”和“调度中心”,能让简单的自动化流程变得智能和灵活。缺少了它们,工作流往往只能进行简单的线性处理,难以应对复杂多变的实际业务场景。这四个核心节点——Filter (筛选器), If (如果), Loop Over Items (循环处理项目), 和 Merge (合并)——正是翔宇在其自动化方案中频繁使用的利器,它们能极大地提升工作流的实用价值。
本教程旨在为零代码基础的读者提供一份关于这四个节点的详尽指南。无论你是刚刚接触 n8n 的新手,还是希望深化理解这些核心逻辑节点的用户,都能从中受益。我们将逐一深入探讨每个节点:
- Filter (筛选器): 学习如何根据条件过滤数据流,只保留你需要的部分。
- If (如果): 掌握如何设置条件,让工作流根据判断结果走向不同的分支。
- Loop Over Items (循环处理项目): 了解何时以及如何使用循环来逐个或分批处理项目,特别是在对接 API 或处理特殊节点时。
- Merge (合并): 学习如何将来自不同流程路径的数据流重新汇合,以及不同的合并策略。
每一章都会详细介绍节点的功能定位、核心价值、输入输出、参数配置(结合翔宇的实战理解)、数据映射与表达式技巧、常用应用场景、常见报错及解决方案,以及使用时的注意事项和版本兼容性。教程将使用通俗易懂的语言,配合恰当的比喻和翔宇的实战心得,确保即使没有编程背景的你也能轻松理解并应用这些强大的节点。
现在,就让我们一起开始这段 n8n 逻辑节点的探索之旅吧!
第一章:n8n 中文教程:筛选器 (Filter)
在构建 n8n 工作流时,我们经常会遇到这样的情况:从上一个节点(比如 Webhook 触发器、数据库查询或 API 调用)获取到了一批数据,但并非所有数据都是我们下一步处理所需要的。这时,Filter (筛选器) 节点就派上了用场。
1.1 节点概览
节点功能定位与核心价值
Filter 节点的核心功能非常明确:根据你设定的条件,检查每一个流经它的数据项 (item),然后决定是让这个数据项通过,还是将其丢弃 。
你可以把它想象成一个工厂流水线上的质量控制检查站。只有那些符合特定标准(也就是你设置的条件)的产品才能通过检查站,继续流向下一个工序;不符合标准的产品则会被直接从流水线上移除 。
这个节点的核心价值在于数据清洗和聚焦。它能够帮助你:
- 移除无关数据: 过滤掉那些对后续步骤没有意义或不符合要求的数据项。
- 提高效率: 确保只有相关的数据项进入后续的处理环节,减少不必要的计算和 API 调用。
- 提升准确性: 避免错误或不相关的数据干扰后续流程的正确执行。
例如,你可能只想处理状态为“已完成”的订单,或者只关心来自特定地区的客户信息,Filter 节点就能帮你轻松实现这些筛选 。
输入 (Input) 与输出 (Output) 数据结构 (Input/Output Data Structure)
- 输入 (Input): Filter 节点接收来自上一个节点的数据流。这个数据流通常是一个包含多个数据项 (items) 的列表(在 n8n 中表现为 JSON 对象数组) 。每个数据项都包含了若干字段和对应的值。
- 输出 (Output): Filter 节点只有一个输出口 。
- 如果一个输入的数据项满足你在节点中设置的所有条件(根据 AND/OR 逻辑判断),那么这个数据项会原封不动地(保持其原始数据结构)从这个输出口流出,进入下一个连接的节点 。
- 如果一个数据项不满足条件,它就会被丢弃,不会出现在输出流中 。
这里需要特别注意 Filter 节点与后面将要介绍的 If 节点的区别。Filter 节点只有一个输出,它的目的是包含或排除 (inclusion/exclusion) 数据项。而 If 节点有两个输出(True 和 False),它的目的是根据条件将数据项路由 (routing) 到不同的处理路径 。如果你需要根据条件执行不同的后续操作,应该使用 If 节点;如果只是想简单地去掉不符合条件的数据,Filter 节点是更合适的选择。
1.2 参数与配置
配置 Filter 节点的核心就是设置筛选条件。让我们逐一了解它的配置项。
配置项含义
打开 Filter 节点的配置面板,你会看到主要的区域是 Conditions (条件) 。
- 添加条件 (Add Condition): 点击此按钮可以添加一条或多条筛选规则。默认通常会有一条初始条件。
- 条件设置区域: 每一条条件规则包含以下几个部分:
- 数据类型 (Data Type Dropdown): 这是设置条件的第一步,你需要在这里选择你要比较的数据属于哪种类型。n8n 提供了多种类型选项,常见的有:
String(文本)Number(数字)Date & Time(日期与时间)Boolean(布尔值,即 true 或 false)Array(数组/列表)Object(对象)- 翔宇的实战理解: 选择正确的数据类型至关重要。这不仅决定了后续可以选择哪些比较操作,也直接影响比较结果的准确性。例如,如果你想比较日期,务必选择
Date & Time类型,而不是当作String来比较 。
- 操作 (Operator Dropdown): 根据你选择的数据类型,这里会列出所有可用的比较操作符。例如 :
- 对于
String:is equal to(等于),contains(包含),starts with(开头是),matches regex(匹配正则表达式) 等。 - 对于
Number:is greater than(大于),is less than(小于),is equal to(等于),is between(介于两者之间) 等。 - 对于
Boolean:is true(是真的),is false(是假的),exists(存在),is empty(是空的) 等。 - 对于
Date & Time:is after(晚于),is before(早于),is equal to(等于) 等。 - 对于
Array:contains(包含某个值),is empty(是空的),has item matching criteria(包含满足特定条件的项目) 等。 - 对于
Object:exists(存在),is empty(是空的),has key(包含某个键) 等。
- 对于
- 值 (Value Fields): 在这里输入你要比较的目标值。这个区域的输入框会根据你选择的操作符动态变化。例如,选择
is equal to时通常是一个输入框,而选择is between时则会有两个输入框。你可以在这里输入固定的值,也可以使用表达式来动态生成比较值(详见 1.3 节)。
- 数据类型 (Data Type Dropdown): 这是设置条件的第一步,你需要在这里选择你要比较的数据属于哪种类型。n8n 提供了多种类型选项,常见的有:
- 组合条件 (Combining Conditions – AND/OR): 当你添加了多条条件规则后,规则之间会出现一个下拉框,用于选择这些条件之间的逻辑关系 :
- AND (所有条件): 选择
AND意味着所有列出的条件都必须为true,该数据项才能通过筛选。这就像一个严格的门禁,需要同时满足多个要求(比如,既要刷卡也要按指纹)才能进入。 - OR (任一条件): 选择
OR意味着只要至少一个条件为true,该数据项就能通过筛选。这就像一个有多把钥匙的锁,任何一把正确的钥匙都能打开。 - 翔宇的实战理解: Filter 节点内不能混合使用 AND 和 OR 逻辑 。例如,你不能设置 “条件A AND 条件B OR 条件C”。如果确实需要这种复杂的混合逻辑,通常的解决方案是:
- 使用多个 Filter 节点串联:第一个 Filter 处理 AND 部分,其输出再进入第二个 Filter 处理 OR 部分(或者反之)。
- 使用 Code 节点:通过编写 JavaScript 代码来实现复杂的逻辑判断(但这超出了零代码基础的范围)。 对于初学者,建议保持逻辑清晰,尽量使用单个 AND 或单个 OR,或者拆分成多个 Filter 节点。
- AND (所有条件): 选择
- 节点选项 (Node Options): 在条件设置区域下方,还有两个全局选项:
- Ignore Case (忽略大小写): 这是一个开关选项,仅对
String(文本) 类型的比较有效。- 开启 (On): 进行文本比较时不区分大小写。例如,”apple” 和 “Apple” 会被视为相等。
- 关闭 (Off): 进行严格的大小写区分比较。
- 翔宇的实战理解: 这个选项在处理来自用户输入或不同系统的数据时非常有用,因为这些数据的大小写格式可能不统一。建议在需要模糊匹配文本时开启它 。
- Less Strict Type Validation (宽松类型验证): 这也是一个开关选项。
- 开启 (On): n8n 会尝试根据你选择的操作符进行数据类型转换。例如,如果你用数字操作符比较一个值为
"123"(文本) 的字段和一个数字123,开启此选项后 n8n 会尝试将文本"123"转换为数字再进行比较。 - 关闭 (Off): n8n 进行严格的数据类型匹配,类型不符通常会导致比较失败或报错。
- 翔宇的实战理解: 当你遇到 “wrong type” (类型错误) 的报错,并且你怀疑输入数据的类型不一致(比如数字被存成了文本格式)时,可以尝试开启此选项作为一种快速解决方案。但需要注意,这可能会掩盖潜在的数据质量问题。如果可能,最好还是在上游节点(如 Set 或 Code 节点)中先统一数据类型。谨慎使用此选项 。
- 开启 (On): n8n 会尝试根据你选择的操作符进行数据类型转换。例如,如果你用数字操作符比较一个值为
- Ignore Case (忽略大小写): 这是一个开关选项,仅对
可选值
- 默认设置: 通常,Filter 节点会默认添加一个条件框。组合条件的默认逻辑通常是
AND。Ignore Case和Less Strict Type Validation默认是关闭 (Off) 的。 - 可选值:
- 数据类型:
String,Number,Date & Time,Boolean,Array,Object是最常用的几种。 - 常用操作符示例:
String:is equal to,is not equal to,contains,does not contain,starts with,ends with,matches regex(正则表达式匹配),is empty,is not empty,exists,does not exist.Number:is equal to,is not equal to,is greater than(>),is less than(<),is greater than or equal to(>=),is less than or equal to(<=),is between,is not between,is empty,is not empty,exists,does not exist.Date & Time:is after,is before,is equal to,is between,is not between,is empty,is not empty,exists,does not exist.Boolean:is true,is false,is empty,is not empty,exists,does not exist.Array:contains,does not contain,is empty,is not empty,has item matching criteria,length is equal to,length is greater than, etc.Object:is empty,is not empty,exists,does not exist,has key,does not have key.
- 数据类型:
不同场景的推荐配置
以下是翔宇根据实战经验总结的一些常见场景及其推荐配置:
- 场景 1: 只保留主题包含“紧急”或“Urgent”的邮件 (忽略大小写)
- 条件 1:
- 数据类型:
String - 字段: (假设邮件主题字段名为
subject){{ $json.subject }} - 操作:
contains - 值:
紧急
- 数据类型:
- 逻辑:
OR - 条件 2:
- 数据类型:
String - 字段:
{{ $json.subject }} - 操作:
contains - 值:
Urgent
- 数据类型:
- 节点选项:
Ignore Case= On
- 条件 1:
- 场景 2: 只保留特定日期之后创建的订单
- 条件 1:
- 数据类型:
Date & Time - 字段: (假设订单创建日期字段名为
createdAt){{ $json.createdAt }} - 操作:
is after - 值: (输入或选择一个具体的日期,例如
2024-01-01T00:00:00.000Z)
- 数据类型:
- 节点选项: 保持默认
- 条件 1:
- 场景 3: 移除那些已经被标记为“已处理” (processed 字段为 true) 的任务
- 条件 1:
- 数据类型:
Boolean - 字段: (假设处理状态字段名为
processed){{ $json.processed }} - 操作:
is false(或者is not equal totrue, 或does not exist如果未处理时该字段不存在)
- 数据类型:
- 节点选项: 保持默认
- 条件 1:
- 场景 4: 筛选出来自“北京”地区的客户,或者是在 2023 年之前注册的老客户
- 条件 1:
- 数据类型:
String - 字段: (假设地区字段名为
city){{ $json.city }} - 操作:
is equal to - 值:
北京
- 数据类型:
- 逻辑:
OR - 条件 2:
- 数据类型:
Date & Time - 字段: (假设注册日期字段名为
registeredAt){{ $json.registeredAt }} - 操作:
is before - 值:
2023-01-01T00:00:00.000Z
- 数据类型:
- 节点选项: 保持默认
- 条件 1:
1.3 数据映射与表达式
虽然 Filter 节点主要是通过下拉菜单和固定值来设置条件,但在“值 (Value Fields)”区域使用表达式 (Expressions) 可以让筛选条件变得更加动态和灵活。
表达式写法
- 基本语法: 在 n8n 中,表达式需要用双大括号
{{ }}包裹起来 。只有这样,n8n 才会将其识别为需要计算的代码,而不是普通的文本。 - 引用当前项数据: 在 Filter 节点中,最常用的就是引用当前正在被检查的数据项的字段值。这通过内置变量
$json来实现 。- 语法:
{{ $json.字段名 }}或者{{ $json['字段名'] }}。 - 示例: 如果输入数据项是
{ "name": "Alice", "score": 95 },你想筛选出分数大于 90 的项,可以在条件的第一个值字段(通常是左侧字段)输入{{ $json.score }},然后选择操作符is greater than,在第二个值字段(右侧)输入90。
- 语法:
- 引用其他节点数据: 虽然不常用在 Filter 的条件值字段中,但了解一下总是有益的。你可以使用
$node["节点名称"].json来引用另一个特定节点的输出数据 。- 示例:
{{ $node.json.threshold }}可以获取名为 “Read Config” 节点输出的threshold值。 - 翔宇建议: 对于初学者,在 Filter 节点中,尽量专注于使用
$json来引用当前项的数据。如果需要与来自其他节点的值比较,通常是在条件的第二个值字段中使用表达式引用那个值。
- 示例:
- 使用内置变量和函数: 表达式中还可以使用 n8n 提供的一些内置变量和函数。
$now: 获取当前的日期和时间对象,常用于日期比较 。例如,筛选出dueDate字段晚于当前时间的任务:条件字段为{{ $json.dueDate }},操作为is after,比较值为{{ $now }}。- 其他函数: n8n 还提供了很多内置函数用于文本处理、数学计算等,可以在表达式中使用,例如
{{ $json.name.toUpperCase() }}将名字转为大写后再比较。
映射技巧与常见易错点
- 技巧1: 使用表达式编辑器: 点击值字段旁边的“目标”图标或 “fx” 图标,可以打开表达式编辑器。编辑器左侧会列出所有可用变量(包括来自前面节点的数据),你可以直接从中拖拽或点击插入,能有效避免手动输入时的拼写错误 。
- 易错点1: 字段名错误: 这是最常见的错误。表达式中引用的字段名 (
$json.fieldName) 必须与输入数据中的字段名完全一致,包括大小写。{{ $json.email }}和{{ $json.Email }}是不同的。务必在节点的 INPUT/OUTPUT 面板中检查实际的字段名。 - 易错点2: 表达式结果的类型不匹配: 当你在比较值字段中使用表达式时,要确保表达式计算结果的数据类型与你为该条件选择的数据类型相符。例如,选择了
Date & Time类型,那么表达式{{ $now }}是合适的,但如果表达式返回的是一个文本字符串(如"2024-08-20"),在未开启“宽松类型验证”时可能会比较失败。 - 易错点3: 忘记
{{ }}: 有时候会忘记给表达式套上双大括号,直接写了$json.field,这样 n8n 会把它当作普通文本处理,而不是执行计算 。 - 易错点4: 在表达式中写复杂逻辑: Filter 节点的表达式主要是用来获取值的。复杂的
if...else或多重判断逻辑不应该写在表达式里,而应该通过节点本身的 AND/OR 条件组合来实现,或者拆分成多个节点。
1.4 应用场景
Filter 节点是 n8n 中最基础也最常用的逻辑节点之一。翔宇在实践中经常在以下场景使用它:
节点常用应用场景
- 数据清洗 (Data Cleaning):
- 移除不完整的记录:例如,过滤掉联系人列表中没有提供电子邮件地址的条目。
- 配置: 数据类型
String, 字段{{ $json.email }}, 操作is not empty(或者exists) 。
- 配置: 数据类型
- 移除测试数据:例如,过滤掉邮箱地址包含
@test.com的用户记录。- 配置: 数据类型
String, 字段{{ $json.email }}, 操作does not contain, 值test.com。
- 配置: 数据类型
- 移除不完整的记录:例如,过滤掉联系人列表中没有提供电子邮件地址的条目。
- 目标处理 (Targeted Processing):
- 只处理特定类型的订单:例如,一个工作流只处理“订阅续费”类型的订单,以便执行特殊的续费逻辑。
- 配置: 数据类型
String, 字段{{ $json.orderType }}, 操作is equal to, 值Subscription Renewal。
- 配置: 数据类型
- 只处理来自特定渠道的线索:例如,只处理通过“官网注册”渠道来的销售线索。
- 配置: 数据类型
String, 字段{{ $json.leadSource }}, 操作is equal to, 值Website Signup。
- 配置: 数据类型
- 只处理特定类型的订单:例如,一个工作流只处理“订阅续费”类型的订单,以便执行特殊的续费逻辑。
- 事件过滤 (Event Filtering):
- 处理特定的 Webhook 事件:当一个 Webhook 可能接收多种事件类型时,使用 Filter 节点只让关心的事件(如
user_created)流向下游。- 配置: 数据类型
String, 字段{{ $json.eventType }}(假设事件类型字段名), 操作is equal to, 值user_created。
- 配置: 数据类型
- 过滤掉机器人产生的事件:例如,根据 User-Agent 字段过滤掉已知的爬虫或监控服务的请求。
- 配置: 数据类型
String, 字段{{ $json.headers['user-agent'] }}, 操作does not contain, 值BotIdentifier。
- 配置: 数据类型
- 处理特定的 Webhook 事件:当一个 Webhook 可能接收多种事件类型时,使用 Filter 节点只让关心的事件(如
- 基于日期的筛选 (Date-Based Filtering):
- 处理本周到期的任务:筛选出
dueDate在本周范围内的任务项。这可能需要结合表达式使用$now和日期函数来计算本周的起止日期。 - 筛选特定时间段的日志:例如,只保留过去 24 小时内产生的日志记录。
- 配置: 数据类型
Date & Time, 字段{{ $json.timestamp }}, 操作is after, 值{{ $now.minus({days: 1}) }}(使用 Luxon 日期库函数)。
- 配置: 数据类型
- 处理本周到期的任务:筛选出
- 排除已处理项 (Exclusion):
- 避免重复处理:如果数据源中包含已处理和未处理的项,可以通过检查状态字段(如
status或processed)来过滤掉已处理的项 。- 配置: 数据类型
String, 字段{{ $json.status }}, 操作is not equal to, 值Done。
- 配置: 数据类型
- 过滤掉无需操作的项:例如,库存同步流程中,过滤掉库存数量未发生变化的商品。
- 避免重复处理:如果数据源中包含已处理和未处理的项,可以通过检查状态字段(如
- 其他社区常见用法:
- 网页抓取后筛选特定信息:如从 Google Maps 抓取数据后,只保留包含有效邮箱地址的商家信息 。
- 检查数据唯一性:结合其他节点或逻辑,过滤掉重复的记录 ID 或数值 。
- 基于关键词列表过滤:从数据库或 HTTP 请求获取一个关键词列表,然后用 Filter 过滤掉包含这些关键词的消息或内容 。
1.5 常见报错及解决方案
在使用 Filter 节点时,可能会遇到一些常见的错误。了解这些错误的原因和解决方法,能帮你更快地调试工作流。
错误提示解析与排错思路
- 错误: “Wrong Type:” 或类型不匹配 (Type Mismatch)
- 错误分析: 这个错误通常意味着你为条件选择的“数据类型”与实际输入数据中该字段的类型不符,或者你输入的比较值的类型也不匹配 。例如,你选择了
Number类型来比较price字段,但实际上输入的price值是文本"99.9"。 - 排错思路:
- 检查输入数据: 在 Filter 节点的 INPUT 面板中,切换到 JSON 视图,仔细查看导致错误的那个数据项,确认你要筛选的字段 (
price) 到底是什么类型(是数字99.9还是文本"99.9")。 - 核对节点配置: 确认你在 Filter 条件中为该字段选择的“数据类型”是否正确。
- 检查比较值: 确认你输入的比较值的类型是否合适。
- 尝试宽松验证: 作为临时方案或在确认数据源类型确实混乱的情况下,可以尝试开启节点选项中的 Less Strict Type Validation 。
- 上游处理: 最稳妥的方法是在 Filter 节点之前,使用 Set 或 Code 节点将数据类型统一转换好。
- 检查输入数据: 在 Filter 节点的 INPUT 面板中,切换到 JSON 视图,仔细查看导致错误的那个数据项,确认你要筛选的字段 (
- 错误分析: 这个错误通常意味着你为条件选择的“数据类型”与实际输入数据中该字段的类型不符,或者你输入的比较值的类型也不匹配 。例如,你选择了
- 错误: 表达式错误 (Expression Error) / “Can’t get data for expression” / “Invalid Syntax”
- 错误分析: 这类错误表明你在条件的某个“值”字段中使用的表达式 (
{{... }}) 存在问题 。Invalid Syntax: 表达式本身的语法有误,比如括号不匹配、运算符错误、遗漏了必要的符号等。Can't get data for expression: 表达式尝试引用的数据(比如$json.nonExistentField或来自$node[...]的数据)在当前执行上下文中找不到。可能是字段名拼写错误,或者引用的前置节点尚未执行或未产生预期的输出 。
- 排错思路:
- 检查表达式语法: 点击表达式字段旁的图标打开表达式编辑器。仔细检查
{{ }}是否完整,括号是否配对,函数名和变量名是否正确,点号.是否用对地方 。 - 验证数据引用: 确认表达式中引用的所有
$json.fieldName或$node["Node Name"].json.fieldName都是真实存在的。在 INPUT 面板中检查输入数据结构。 - 检查前置节点: 如果引用了其他节点的数据 (
$node[...]),确保那个节点在工作流中确实先于 Filter 节点执行,并且已经成功产生了输出数据。可以先单独测试前置节点 。 - 使用编辑器: 再次强调,使用表达式编辑器的变量选择功能可以大大减少这类错误 。
- 检查表达式语法: 点击表达式字段旁的图标打开表达式编辑器。仔细检查
- 错误分析: 这类错误表明你在条件的某个“值”字段中使用的表达式 (
- 错误: 没有输出 / 筛选结果不符合预期 (No Output / Unexpected Filtering)
- 错误分析: Filter 节点运行成功(显示绿色),但输出的数据项比预期的少(甚至没有输出),或者比预期的多。这说明条件逻辑的判断结果与你的设想不符。
- 排错思路:
- 审视条件逻辑: 仔细检查你设置的所有条件。特别是当有多个条件时,确认 AND/OR 逻辑是否设置正确。是不是本该用 OR 的地方用了 AND?
- 核对比较值: 确认你输入的比较值是否准确无误。一个微小的拼写错误或数字错误都可能导致筛选失败。
- 检查忽略大小写: 如果是文本比较,检查
Ignore Case选项是否是你想要的状态。 - 测试节点: 使用 Test step 功能,传入一些你知道应该通过和不应该通过的样本数据,观察节点的实际输出,与预期进行对比 。
- 检查输入数据: 仔细检查 Filter 节点的 INPUT 数据,确认数据本身是否符合你设置条件时的假设。
调试方法与日志定位技巧
- 节点内测试 (Test Step): 这是调试 Filter 节点最直接有效的方法。点击节点上的 “Test step” (播放按钮图标),n8n 会使用上一个节点缓存的输出作为输入来运行 Filter 节点。运行后,你可以在节点的 OUTPUT 面板看到哪些数据项通过了筛选,哪些被过滤掉了 。
- 输入/输出面板 (Input/Output Panel): 在节点编辑器中,INPUT 面板显示节点接收到的数据,OUTPUT 面板显示节点处理后输出的数据。仔细比对这两个面板的数据,是理解 Filter 节点行为的关键。你还可以在这两个面板中使用顶部的搜索框来快速定位包含特定关键词的数据行(注意这只是视觉查找,不改变数据) 。
- 执行列表 (Workflow Executions List): 对于已保存并激活的工作流,你可以在 n8n 主界面的 “Executions” 区域查看历史运行记录。点击某次执行记录,可以回溯整个工作流的执行过程,查看每个节点在当时的输入和输出数据,这对于诊断生产环境中出现的问题非常有帮助 。
- 简化条件: 如果你的 Filter 节点设置了多个复杂的条件,可以尝试暂时禁用一部分条件,只保留一两个,然后测试。逐步增加条件,看是哪个条件导致了非预期的结果。
- 日志: Filter 节点本身通常不会产生特定的日志文件来记录其筛选逻辑。主要的调试信息来源就是上述的节点内测试结果和工作流执行历史中的数据快照。
1.6 注意事项
在使用 Filter 节点时,牢记以下几点,可以帮助你更有效地构建工作流。
使用注意事项
- Filter vs. If 的选择: 再次强调这个关键区别。Filter 节点用于移除不符合条件的数据项,它只有一个输出通道。If 节点用于分流,根据条件将数据项导向
true或false两个不同的输出通道。如果你需要对满足条件和不满足条件的数据执行不同的后续操作,请使用 If 节点 。 - 避免过度复杂化: 虽然可以在一个 Filter 节点中设置多个 AND 或 OR 条件,但过于复杂的逻辑组合可能会让工作流难以理解和维护。如果筛选逻辑非常复杂,可以考虑将其拆分成多个连续的 Filter 节点,每个节点负责一部分筛选逻辑,这样更清晰。
- 处理可能缺失的字段: 如果你筛选所依据的字段在某些输入数据项中可能不存在,直接使用
is equal to或contains等操作可能会出错或产生意外结果。在这种情况下,应该优先使用exists(存在) 或is not empty(非空) 操作符来确保字段存在且有值,然后再进行更具体的比较,或者在后续节点处理。例如,先用一个 Filter 检查email字段exists,通过后再用另一个 Filter 检查email是否包含@。 - 性能考虑: 虽然 Filter 节点本身执行速度很快,但如果它处理的数据量非常巨大(成千上万条记录),并且筛选条件涉及到复杂的正则表达式或需要大量计算的表达式,也可能会对工作流的整体性能产生一定影响。不过对于大多数常见场景,这通常不是问题。
节点版本兼容性与历史演变
- 核心节点稳定性: Filter 作为一个基础的核心节点,其核心功能通常是非常稳定的。n8n 团队会确保这些基础节点的可靠性 。
- 版本更新: 随着 n8n 的版本迭代,Filter 节点可能会有一些微小的改进或变化,例如:
- 新增操作符: 可能会增加新的比较操作符,提供更丰富的筛选能力 。
- UI/UX 调整: 配置界面的外观或交互方式可能会有细微调整。
- 性能优化: 后端实现可能会进行优化。
- 一般而言,这些更新都是向后兼容的,不会破坏现有工作流。重大的、可能导致不兼容的变更非常少见,并且通常会在 n8n 的主要版本更新(如从 0.x 到 1.x)时发生,并有相应的迁移指南 。
- 检查版本变化: 你不需要手动管理 Filter 节点的版本 。如果你想了解 n8n 各个版本中具体节点的更新细节,最好的方式是查阅官方的发布说明 (Release Notes) 。虽然 Filter 节点的具体变更不一定会每次都详细列出,但重要的更新通常会提及。
- 社区节点: n8n 社区也可能开发提供类似或增强筛选功能的自定义节点 。例如,有社区节点专门用于检测数据项相较于上次运行是否发生了变化 。但在绝大多数情况下,内置的 Filter 节点已经足够满足需求,并且是标准、可靠的选择。
掌握了 Filter 节点,你就拥有了清理和聚焦数据的基本能力。接下来,我们将学习另一个强大的逻辑控制节点——If 节点,它将赋予你的工作流做出决策的能力。
第二章:n8n 中文教程:如果 (If)
当你的工作流需要根据不同的数据情况执行不同的操作路径时,If (如果) 节点就成为了你的得力助手。它不像 Filter 节点那样只是简单地放行或丢弃数据,而是像一个岔路口,根据你设定的“路标”(条件),将数据引导到不同的方向。
2.1 节点概览
节点功能定位与核心价值
If 节点的核心功能是实现工作流的条件分支 。它会对每一个流入的数据项进行评估,判断其是否满足你所设定的一个或多个条件。然后,根据评估结果是 true (真) 还是 false (假),将该数据项发送到两个不同的输出路径之一。
可以把它想象成一个道路分岔口。每个到来的车辆(数据项)都会被检查是否符合某个通行规则(条件)。符合规则的车辆会被引导到标有 “True” 的道路上,不符合规则的则被引导到标有 “False” 的道路上 。
If 节点的核心价值在于赋予工作流决策能力。它使得自动化流程不再是死板的线性执行,而是能够:
- 响应数据变化: 根据输入数据的具体内容或状态,执行不同的处理逻辑 。
- 实现动态流程: 让工作流能够适应多种可能性,处理不同的业务场景。
- 增强智能性: 使工作流看起来更“聪明”,能够根据情况做出判断和选择。
例如,你可以用 If 节点判断一个新注册用户是否选择了接收营销邮件,然后决定是将其加入营销列表还是普通列表。
输入 (Input) 与输出 (Output) 数据结构 (Input/Output Data Structure)
- 输入 (Input): If 节点接收来自上一个节点的数据流,这通常是一个包含多个数据项 (items) 的列表(JSON 对象数组) 。
- 输出 (Output): If 节点具有两个明确的输出锚点 :
true输出: 如果一个输入的数据项满足你在节点中设置的条件(根据 AND/OR 逻辑判断),该数据项会从这个输出口流出。false输出: 如果一个数据项不满足条件,它会从这个输出口流出。- 数据结构不变: 流经 If 节点的数据项,其内部的数据结构(字段和值)不会被改变,只是被路由到了不同的路径。
这里再次强调与 Filter 节点的关键区别:Filter 节点会丢弃不满足条件的数据项 ,而 If 节点会保留所有的数据项,只是将它们分配到 true 或 false 两个不同的出口 。这个根本性的差异决定了它们各自的应用场景。如果你需要对不满足条件的数据执行另一种操作(而不是简单丢弃),那么 If 节点是必需的。
2.2 参数与配置
配置 If 节点的界面和逻辑与 Filter 节点非常相似,核心也是围绕着设置条件。
每个配置项含义
打开 If 节点的配置面板,主要配置区域同样是 Conditions (条件) 。
- 添加条件 (Add Condition): 与 Filter 节点一样,点击按钮添加条件规则。
- 条件设置区域: 每条规则的设置也与 Filter 相同:
- 数据类型 (Data Type Dropdown): 选择要比较的数据类型 (
String,Number,Date & Time,Boolean,Array,Object等) 。 - 操作 (Operator Dropdown): 根据所选数据类型,选择合适的比较操作符 (如
is equal to,contains,is greater than,is true,exists等) 。 - 值 (Value Fields): 输入用于比较的目标值,可以是固定值或表达式。
- 数据类型 (Data Type Dropdown): 选择要比较的数据类型 (
- 组合条件 (Combining Conditions – AND/OR): 当有多条条件时,同样使用下拉框选择
AND(所有条件) 或OR(任一条件) 来组合它们。这个组合逻辑的结果将决定数据项最终是被判定为true还是false。- 翔宇的实战理解: If 节点的条件逻辑直接决定了工作流的走向。在设置多条 OR 条件时尤其要小心,确保覆盖了所有预期的情况,避免数据流向非预期的分支。仔细规划和测试条件组合至关重要。
- 节点选项 (Node Options): 与 Filter 节点不同,标准的 If 节点没有提供
Ignore Case(忽略大小写) 或Less Strict Type Validation(宽松类型验证) 这两个全局选项 。- 翔宇的实战理解: 这意味着 If 节点的条件判断更为严格。如果需要忽略大小写,你可能需要在表达式中处理,例如,在比较值字段使用
{{ $json.someString.toLowerCase() }}将输入值转换为小写,然后与一个小写的目标值进行比较。同样,如果存在类型不一致的问题,最好在上游节点(如 Set)中进行数据清洗和类型转换,或者确保比较操作符能正确处理(例如,某些文本比较操作符可能隐式处理不同类型)。这种设计上的差异表明,If 节点更侧重于纯粹的逻辑判断,依赖于上游数据的准确性和一致性。
- 翔宇的实战理解: 这意味着 If 节点的条件判断更为严格。如果需要忽略大小写,你可能需要在表达式中处理,例如,在比较值字段使用
默认值或者可选值具体介绍
- 默认设置: 与 Filter 类似,通常默认带有一个条件框,组合逻辑默认为
AND。 - 可选值: 可用的数据类型和操作符与 Filter 节点基本一致,提供了丰富的比较能力来定义
true或false的判断标准 。
不同场景的推荐配置
以下是一些使用 If 节点的常见场景及推荐配置:
- 场景 1: 根据用户选择的语言(如 “en” 或 “zh”)发送不同内容的欢迎邮件
- 条件 1:
- 数据类型:
String - 字段:
{{ $json.languagePreference }}(假设字段名) - 操作:
is equal to - 值:
en
- 数据类型:
true分支: 连接发送英文邮件的节点。false分支: 可以连接另一个 If 节点判断是否为zh,或者直接连接发送默认语言(如中文)邮件的节点。
- 条件 1:
- 场景 2: 检查数据库中是否已存在某个客户记录,存在则更新,不存在则创建
- (假设前一个节点,如数据库查询节点,返回了一个包含客户数量的字段
customerCount) - 条件 1:
- 数据类型:
Number - 字段:
{{ $json.customerCount }} - 操作:
is greater than - 值:
0
- 数据类型:
true分支: 连接更新客户记录的节点 。false分支: 连接创建新客户记录的节点 。
- (假设前一个节点,如数据库查询节点,返回了一个包含客户数量的字段
- 场景 3: 将高优先级的工单(如 priority 字段为 “High”)路由到紧急处理流程
- 条件 1:
- 数据类型:
String - 字段:
{{ $json.priority }} - 操作:
is equal to - 值:
High
- 数据类型:
true分支: 连接发送紧急通知、分配给特定团队等操作的节点。false分支: 连接常规处理流程的节点。
- 条件 1:
- 场景 4: 判断 Webhook 收到的数据中是否包含某个可选的备注字段
notes- 条件 1:
- 数据类型:
String(如果 notes 是文本) 或Object(如果 notes 本身是对象) - 字段:
{{ $json.notes }} - 操作:
exists
- 数据类型:
true分支: 连接需要处理notes字段的节点。false分支: 连接跳过notes处理的节点 。
- 条件 1:
2.3 数据映射与表达式
和 Filter 节点一样,If 节点主要在条件的“值 (Value Fields)”区域使用表达式,以实现动态判断。
表达式写法
- 主要用途: 在条件的比较值字段中使用,或者在条件的第一个字段中引用当前项的某个属性进行比较 。
- 基本语法: 仍然是
{{ }}包裹 。 - 引用数据:
$json: 引用当前正在被 If 节点判断的数据项的属性。例如{{ $json.status }}。$node["节点名称"].json: 引用上游特定节点的输出数据 。$now: 获取当前时间,常用于日期/时间条件的比较 。
- 翔宇的实战技巧: 当你需要将当前数据项的某个值与上游某个只输出单个固定结果的节点(例如,一个读取配置的节点或计算阈值的 Code 节点)的输出进行比较时,务必在引用该单结果节点的表达式中使用
.first()或索引 “。这是因为 If 节点会对每个流入的数据项执行一次判断。如果不指定.first(),当 If 处理第二个、第三个流入项时,它会尝试从那个单结果节点获取第二个、第三个输出项(但实际并不存在),从而导致错误。- 正确示例: 判断当前订单金额
{{ $json.orderValue }}是否大于配置节点 “Config Loader” 输出的阈值threshold:- 条件字段1:
{{ $json.orderValue }} - 操作:
is greater than - 条件字段2 (表达式):
{{ $node["Config Loader"].first().json.threshold }}
- 条件字段1:
- 这种模式在社区讨论中也经常出现,是处理多对一比较的关键 。
- 正确示例: 判断当前订单金额
映射技巧与常见易错点
- 技巧1: 使用表达式编辑器: 依然推荐使用编辑器来选择变量,减少错误 。
- 易错点1: 字段名错误/拼写/大小写: 和 Filter 一样常见。
- 易错点2: 表达式结果类型不匹配: 由于 If 节点没有宽松类型验证选项,确保表达式结果类型与条件设置的数据类型严格一致更为重要。
- 易错点3: 忘记
{{ }}: 表达式必须用双大括号包裹。 - 易错点4: 引用后续节点的数据: 表达式只能引用当前项 (
$json) 或之前节点 ($node[...]) 的数据。 - 易错点5 (If 节点特有): 误解
$json的作用域: 对于流入 If 节点的每一个数据项,If 节点都会独立执行一次条件判断。在判断第 N 个数据项时,$json指向的就是这第 N 个数据项的内容,而不是永远指向第一个数据项 。这是一个非常重要的概念,初学者容易混淆,以为 If 节点只基于第一个到达的数据项做一次决定,然后将所有数据项都发送到同一路径。实际情况是逐项判断,逐项分流。
2.4 应用场景
If 节点是构建具有逻辑判断能力的自动化流程的核心。翔宇在实践中广泛应用 If 节点于以下场景:
节点的常用应用场景
- 条件分支 (Conditional Branching): 这是最核心的用途。根据数据的不同情况,执行完全不同的后续步骤 。
- 示例:如果订单金额超过 1000 元,则触发一个需要人工审核的流程 (
true分支);否则,自动处理订单 (false分支)。
- 示例:如果订单金额超过 1000 元,则触发一个需要人工审核的流程 (
- 数据验证检查 (Data Validation Check): 在处理数据之前,检查其有效性 。
- 示例:如果收到的表单数据中,
email字段格式有效 (true分支),则继续处理注册;否则 (false分支),记录错误日志或通知提交者修改。可以使用String操作符如matches regex来进行格式验证。
- 示例:如果收到的表单数据中,
- A/B 路径测试 (A/B Path Testing): 想测试两种不同的处理方式哪个效果更好时,可以用 If 节点(可能结合随机数或特定条件)将一部分数据导入 A 路径,另一部分导入 B 路径,最后比较结果。
- 存在性检查 (Existence Check): 判断某个资源(如文件、数据库记录、用户账户)是否存在,然后决定是创建还是更新,或是执行其他相关操作 。
- 示例:在上传文件到云存储前,先检查同名文件是否存在。如果存在 (
true分支),可能需要重命名或覆盖;如果不存在 (false分支),则直接上传。
- 示例:在上传文件到云存储前,先检查同名文件是否存在。如果存在 (
- 循环终止条件 (Loop Termination): 在一些需要手动构建循环的场景(例如,使用旧版 n8n 或处理不支持自动分页的 API 时,将后续节点连接回前面的节点形成循环),通常会使用 If 节点来判断是否满足退出循环的条件 。
- 示例:在一个手动分页的循环中,If 节点检查 API 返回结果中是否还有下一页的标识 (
nextPageTokenexists?)。如果没有 (true分支),则跳出循环;如果有 (false分支),则继续下一次循环 。不过,对于标准的分页和迭代,更推荐使用后面将介绍的 Loop Over Items 节点。
- 示例:在一个手动分页的循环中,If 节点检查 API 返回结果中是否还有下一页的标识 (
- 错误处理逻辑 (Error Handling Logic): 如果工作流配置了错误处理(例如,在节点设置中选择 “Continue (using error output)”),错误信息会作为数据传递给下一个节点。可以使用 If 节点检查是否有错误数据传入,然后将流程导向专门的错误处理分支 。
- 示例:If 节点检查输入数据中是否存在
error字段 (errorexists?)。如果存在 (true分支),发送告警通知;如果不存在 (false分支),按正常流程继续。
- 示例:If 节点检查输入数据中是否存在
2.5 常见报错及解决方案
使用 If 节点时遇到的问题通常与条件设置或对数据流的理解有关。
错误提示解析与排错思路
- 错误: 表达式错误 (Expression Error) / “Can’t get data for expression” / “Invalid Syntax”
- 错误分析: 与 Filter 节点中的同类错误原因一致,问题出在条件值字段中的表达式 。
- 排错思路: 检查表达式语法 (
{{ }}, 变量名, 函数调用),确认引用的字段或节点数据确实存在且已执行 。使用表达式编辑器辅助检查。
- 错误: 数据流向错误的分支 (Unexpected Branch Execution)
- 错误分析: 数据项本应进入
true分支却进入了false分支,或者反之。这表明条件逻辑的评估结果与预期不符。 - 排错思路:
- 仔细检查条件: 逐一核对每个条件的数据类型、操作符和比较值是否设置正确。
- 检查 AND/OR 逻辑: 确认多条件组合方式是否符合预期。
- 检查输入数据: 在 INPUT 面板查看那些流向错误分支的具体数据项,确认它们的值是否确实应该导致那样的判断结果。是不是数据本身就有问题?
- 使用 Test Step: 在节点内使用 Test step 功能,观察样本数据项被路由到了哪个分支,这有助于快速定位问题 。
- 注意数据类型: 再次确认比较双方的数据类型是否一致,因为 If 节点默认进行严格类型比较。
- 错误分析: 数据项本应进入
- 错误: 后续节点报错,提示缺少预期数据 (Downstream Node Error due to Missing Data)
- 错误分析: 这通常是 If 节点路由错误的结果,而不是 If 节点本身的错误。因为数据被错误地引导到了某个分支,导致该分支后续的节点没有接收到它们所期望处理的数据字段。
- 排错思路: 不要只关注报错的后续节点,根源在于 If 节点的判断逻辑。回到 If 节点,按照上一条“数据流向错误的分支”的思路进行排查。
调试方法与日志定位技巧
- 节点内测试 (Test Step): 对于 If 节点来说极其重要。它不仅运行节点,还会清晰地显示出样本数据项最终被分配到了
true还是false输出,让你立刻知道判断结果 。 - 输入/输出面板 (Input/Output Panel):INPUT 面板显示所有进入的数据。OUTPUT 面板会分成
true和false两部分,分别列出被路由到各自路径的数据项列表 。这是观察分流结果最直观的地方。 - 执行列表 (Execution View): 在工作流的历史执行记录中,你可以看到数据流实际通过了哪个分支(连接线会高亮),帮助你可视化地追踪流程路径 。
- 简化条件: 如果条件复杂,可以暂时禁用部分条件,只保留一个进行测试,逐步排查是哪个条件或组合导致了问题。
2.6 注意事项
理解并注意以下几点,能让你更稳健地使用 If 节点。
使用注意事项
- If vs. Switch: 如果你的逻辑需要超过两个分支(例如,根据状态字段的值是 “Pending”, “Processing”, “Completed” 执行三种不同的操作),那么应该使用 Switch 节点,而不是嵌套多个 If 节点。Switch 节点专门用于处理多路分支,通常更清晰 。If 节点严格限定为二元(True/False)判断。
- 处理多个输入项的行为: 这是初学者最需要理解清楚的一点。默认情况下,当有多个数据项(一个列表)输入 If 节点时,If 节点会对列表中的每一个数据项独立地执行一次条件判断。然后,根据每个项各自的判断结果,将其路由到
true或false分支。它不会只根据第一个数据项做决定,然后把整个列表都送到一个分支 。- 重要性: 理解这种“逐项处理”模式至关重要。如果你希望基于整个批次的数据(例如,批次中是否有任何项满足条件)来做一次性决策,你需要先进行聚合操作(如使用 Item Lists 节点或 Code 节点),然后再将聚合结果送入 If 节点。
- 合并分支 (Merging Branches): 如果从 If 节点的
true和false分支出来的流程最终需要汇合到一起,执行共同的后续步骤,那么你通常需要在汇合点放置一个 Merge (合并) 节点 。Merge 节点会等待来自其所有上游输入的数据都到达后,再将它们合并(具体合并方式可配置)。 - 与 Merge 节点的历史交互 (Legacy Merge Interaction): 在 n8n 1.0 版本之前的旧执行顺序 (v0 execution order) 下,存在一个特殊情况:如果在 If 节点后紧跟一个 Merge 节点,即使 If 条件只满足一个分支(例如
true),Merge 节点的行为也可能触发false分支上的节点意外执行 。- 翔宇提醒: 现代 n8n 版本默认使用 v1 执行顺序,已经解决了这个问题。大多数用户无需担心。但如果你接手或维护的是非常老旧的工作流,并且遇到了这种奇怪现象,检查一下工作流设置中的 “Execution Order” 是否为 “V0 (Legacy)”,如果是,考虑升级到 “V1 (Recommended)” 或重新设计该部分的逻辑 。
节点版本兼容性与历史演变
- 核心稳定性: If 节点作为基础逻辑构件,核心功能保持稳定 。
- 操作符与数据类型: 随着 n8n 发展,可能会逐步增加支持的数据类型或比较操作符,以增强其判断能力。这些通常是增量添加,保持向后兼容 。
- 执行顺序变更的影响: 对 If 节点影响最大的历史变化是 n8n v1.0 引入的新执行顺序,改变了它与后续 Merge 节点的交互方式,修复了旧版本中可能导致意外分支执行的问题 。
- UI/UX: 节点的外观和配置界面可能会有微调,但基本配置流程(选择数据类型、操作符、值,设置 AND/OR)保持一致。
- 版本管理: 用户通常不需要直接管理 If 节点的版本 。其功能更新随 n8n 整体版本发布。如果你对某个特定版本的 If 节点行为有疑问,查阅对应 n8n 版本的发布说明是最佳途径 。
If 节点为你的工作流带来了判断和选择的能力。接下来,我们将探讨 Loop Over Items 节点,它解决了在特定场景下如何控制数据项迭代处理的问题。
第三章:n8n 中文教程:循环处理项目
在 n8n 中处理多个数据项时,大多数节点天生就懂得如何“循环”——它们会自动接收一批数据项,然后对其中的每一项分别执行操作。然而,在某些特定情况下,这种默认的“隐式循环”行为并不适用,或者我们需要更精细地控制迭代过程。这时,Loop Over Items (循环处理项目) 节点就显得尤为重要。
需要注意的是,这个节点在 n8n 的早期版本中被称为 “Split in Batches” (分批处理)。如果你看到旧的教程或工作流模板中使用了 “Split in Batches”,它指的就是现在这个 Loop Over Items 节点,核心功能是相似的 。
3.1 节点概览
节点功能定位与核心价值
Loop Over Items 节点的核心功能是显式地、可控地迭代处理一批输入数据项 。它与 n8n 中大多数节点的默认行为不同。
- 关键区别: 必须强调的是,大多数 n8n 节点(如 Set, HTTP Request, 大多数 API 节点)默认就会对所有输入的 items 逐一处理 。你通常不需要 Loop Over Items 节点来实现简单的“对每个项目做某事”。只有在遇到特定例外情况时,才需要使用这个节点 。
- 工作方式: 它接收一批输入数据项,将其保存在内部。然后,根据你设置的 Batch Size (批处理大小),每次从这批数据中取出一小部分(一个批次),通过节点的
loop输出口发送出去。连接在loop输出口之后的节点会处理这个批次的数据。处理完成后,流程需要显式地连接回 Loop Over Items 节点的输入口,以触发下一批次的处理。当所有输入数据项都按批次处理完毕后,节点会将所有处理结果(通常是来自每次循环最后一步的输出)合并起来,通过done输出口一次性输出 。 - 类比: 想象你在处理一大堆信件。n8n 的普通节点就像一个超高效的员工,能瞬间处理完所有信件。而 Loop Over Items 节点则像是一个按部就班的员工,他先拿起一小叠(比如 10 封,这就是 Batch Size),处理完这一叠,放到“已处理”堆里,然后再拿起下一叠,重复这个过程,直到所有信件处理完毕。最后,他把所有“已处理”的信件一起交给你(这就是
done输出)。 - 核心价值: 这个节点提供了在特殊场景下控制迭代的能力,主要价值体现在 :
- 处理 API 分页 (API Pagination): 当 API 返回结果是分页的,你需要一页一页地获取数据时。
- 应对速率限制 (Rate Limiting): 当你需要调用某个 API 或服务,但对方有调用频率限制(如每分钟不能超过 50 次)时,可以用循环结合 Wait 节点来控制节奏。
- 使用非迭代节点: 当你需要对每个数据项使用那些本身不会自动迭代处理所有输入项的特殊节点时(后面会列举例子)。
- 分块处理大数据: 当输入数据量极大,一次性处理可能导致内存不足或超时时,可以分批处理。
输入 (Input) 与输出 (Output) 数据结构 (Input/Output Data Structure)
- 输入 (Input): 接收一个包含多个数据项 (items) 的列表(JSON 对象数组),这些是需要被循环处理的数据 。
- 输出 (Output): 具有两个独特的输出锚点 :
loop输出: 在每次循环迭代时,从此输出口发送一个批次的数据项。批次的大小由Batch Size参数决定。连接在这个输出口之后的节点链构成了循环体,负责处理当前批次的数据。done输出: 当所有输入的数据项都已经被分批并通过loop输出口处理完毕后,此输出口会一次性地输出所有循环迭代产生的结果的集合。连接在此输出口之后的节点只会在整个循环结束后执行一次。
- 关键机制:闭合循环: 为了让 Loop Over Items 节点能够进行下一次迭代,循环体中的最后一个节点必须连接回 Loop Over Items 节点的输入锚点 。这是一个手动操作,也是与 n8n 默认的隐式循环最大的不同之处。忘记闭合循环会导致节点只处理第一个批次就结束。
3.2 参数与配置
配置 Loop Over Items 节点相对简单,但理解每个参数的含义和影响非常重要。
配置项含义
- Batch Size (批处理大小): 这个参数定义了每一次通过
loop输出口发送多少个数据项进行处理 。- 设置为
1: 这是非常常用的设置,表示一次只处理一个数据项。适用于需要对每个项目进行精细操作,或者调用的 API 只接受单个项目作为输入的情况 。 - 设置为
N > 1: 表示一次处理N个数据项。适用于需要批量调用 API(例如,一次更新 50 个数据库记录),或者为了控制 API 调用频率(例如,每批处理 50 项,然后在循环体内等待 1 分钟) 。 - 翔宇的实战理解:
Batch Size的选择对性能和结果有直接影响。- 如果处理的节点本身支持批量操作(如某些数据库插入或 API 调用),设置一个合适的批次大小(如 10, 50, 100)通常比设置为 1 更高效。
- 如果需要严格控制 API 调用频率,
Batch Size结合循环体内的Wait节点是关键。例如,限制为每分钟 60 次,可以设置Batch Size = 60,然后在循环体最后加一个Wait节点等待 61 秒。 - 如果不确定,或者处理的节点只接受单项输入,从
Batch Size = 1开始通常是最安全的选择。
- 设置为
- 节点选项 (Node Options):
- Reset (重置): 这是一个布尔开关选项 。
- 开启 (On): 当设置为 On 时,每次循环回到 Loop Over Items 节点时,它会忘记之前处理过的项目状态,将当前循环批次视为一个全新的、独立的数据集来初始化 。
- 关闭 (Off): 这是默认状态。节点会记住它已经处理到哪里,按顺序处理下一批次。
- 翔宇的实战理解:
Reset选项主要用于动态确定循环次数的场景,最典型的就是 API 分页,特别是当你事先不知道总共有多少页时 。- 分页场景示例: 假设一个 API,你调用它获取第一页数据,它会返回数据和一个
nextPageToken。你需要用这个token去获取第二页,以此类推,直到某次调用不再返回nextPageToken。在这种情况下:- Loop Over Items 的
Batch Size通常设为1(因为我们一次处理一个页面请求)。 Reset必须设为ON。- 循环体内包含一个 HTTP Request 节点(使用当前的
pageToken),处理返回数据的节点,以及一个 If 节点。 - If 节点检查 API 返回结果中是否还包含
nextPageToken。 - 如果包含 (
false分支,表示还有下一页),则将这个新的nextPageToken传递下去,并连接回 Loop Over Items 节点的输入。 - 如果不包含 (
true分支,表示已经是最后一页),则这条路径可以连接到done输出之后的节点,或者直接结束。
- Loop Over Items 的
- 重要警告: 使用
Reset = ON时,必须确保你的循环逻辑中有一个可靠的退出条件(通常是通过 If 节点判断)。否则,如果退出条件永远不满足,工作流将陷入无限循环,耗尽资源或被强制终止 。
- 分页场景示例: 假设一个 API,你调用它获取第一页数据,它会返回数据和一个
- Reset Condition (重置条件 – 表达式): 当
Reset开启时,这个选项允许你使用表达式来动态决定何时触发重置。这提供了比简单地每次都重置更精细的控制 。对于初学者来说,通常直接使用Reset = ON配合 If 节点的退出逻辑就足够了。
- Reset (重置): 这是一个布尔开关选项 。
默认值或者可选值具体介绍
- 默认 Batch Size: 可能默认为 1 或 10,具体取决于 n8n 版本,建议总是明确设置它。
- 默认 Reset: 默认是 Off。
不同场景的推荐配置
- 场景 1: 对列表中的每个 URL 单独调用 RSS Read 节点 (RSS Read 节点本身不自动迭代)
- 配置:
Batch Size = 1,Reset = Off。 - 循环路径:
loop->RSS Read(URL 设为{{ $json.url }}) -> (连接回 Loop Input)。 done输出: 连接后续需要处理所有 RSS 结果的节点 。
- 配置:
- 场景 2: 向 API 批量添加用户,每批 50 个,每批之间等待 10 秒以防超速
- 配置:
Batch Size = 50,Reset = Off。 - 循环路径:
loop->HTTP Request(配置为批量添加用户,接收 50 个用户数据) ->Wait(等待 10 秒) -> (连接回 Loop Input)。 done输出: 连接循环完成后的操作,如发送总结报告。
- 配置:
- 场景 3: 获取某个支持 offset/limit 分页的 API 的所有数据 (假设 API 每次最多返回 100 条)
- 准备: 需要一个变量来存储当前的 offset,初始值为 0。可以在 Loop 节点前用 Set 节点设置
offset = 0。 - 配置:
Batch Size = 1(因为我们一次处理一个 API 请求调用),Reset = ON。 - 循环路径:
loop->HTTP Request(URL 中包含limit=100和offset={{ $json.offset }})- -> (处理返回的 100 条数据,例如存入数据库)
- ->
Set节点 (计算下一个 offset:nextOffset = {{ $json.offset + 100 }}) - ->
If节点 (判断本次 API 调用返回的数据量是否小于 100。如果小于 100,说明是最后一页了)true分支 (是最后一页): 不连接任何节点,或连接到done路径后的节点。false分支 (不是最后一页): 将包含nextOffset的数据项连接回 Loop Input。
done输出: 连接循环完成后的操作。- 注意: 这个例子是为了说明原理,实际分页实现可能因 API 不同而异,需要仔细阅读 API 文档。使用
noItemsLeft检查返回数据是否为空通常是更通用的退出判断方式。
- 准备: 需要一个变量来存储当前的 offset,初始值为 0。可以在 Loop 节点前用 Set 节点设置
3.3 数据映射与表达式
Loop Over Items 节点提供了一些特殊的上下文 (context) 变量,可以通过表达式来访问,这对于控制循环流程非常有用。
表达式写法
- 访问上下文变量: 这些变量存储在节点的
context对象中,需要通过$node["节点名称"].context["变量名"]的形式访问 。假设你的 Loop Over Items 节点名为 “Loop Items”。{{ $node["Loop Items"].context["noItemsLeft"] }}: 返回一个布尔值 (true或false)。true表示所有输入的原始数据项都已经被分批处理完毕;false表示还有剩余的数据项待处理。这个变量是判断循环是否应该结束的关键,尤其是在使用Reset = ON的分页场景中 。{{ $node["Loop Items"].context }}: 返回一个数字,表示当前是第几次循环迭代(从 0 开始计数)。例如,第一次通过loop输出口时,它的值是 0;第二次是 1,以此类推。这个变量可以用于日志记录、计算分页的 offset (offset = currentRunIndex * batchSize),或者在循环体内根据迭代次数执行不同操作 。{{ $node["Loop Items"].context["done"] }}: 这个变量在社区讨论中被提及,似乎在处理嵌套循环时可能有用,用于判断内部循环是否完成 。但它可能不是官方文档明确推荐的标准用法,使用时需谨慎测试。
- 引用循环批次数据: 在连接到
loop输出口的节点(即循环体内)中,使用$json变量可以引用当前正在处理的批次中的某个数据项的属性。如果Batch Size > 1,后续节点通常会隐式地对批次内的所有项进行迭代处理。 - 引用循环前数据: 在循环体内,你仍然可以使用
$node["节点名称"].json等方式引用 Loop Over Items 节点之前的节点输出的数据。但要注意,如果这些数据在每次循环迭代中应该是变化的(虽然这种情况较少见),直接引用可能会得到旧的值。
映射技巧与常见易错点
- 易错点1: 忘记
.context: 访问上下文变量时,很容易忘记在节点引用后加上.context,例如写成$node["Loop Items"]["noItemsLeft"]是错误的。 - 易错点2: 在错误的位置使用上下文变量:
noItemsLeft和currentRunIndex主要在循环体内部(连接loop输出之后)或用于判断是否继续循环(通常在循环体末尾的 If 节点)时才有意义。在done输出路径之后,noItemsLeft总是true,currentRunIndex的值是最后一次迭代的索引。 - 易错点3: 节点名称错误: 表达式
$node["节点名称"]中的名称必须与你在 n8n 画布上为 Loop Over Items 节点设置的名称完全一致。修改节点名称后,需要同步更新表达式。 - 技巧1: 使用 If 节点检查
noItemsLeft: 在循环体的末尾(连接回 Loop Input 之前),放置一个 If 节点,条件设置为{{ $node["Loop Items"].context["noItemsLeft"] }}is equal totrue。将 If 节点的true分支留空或连接到done路径之后的节点,将false分支连接回 Loop Input。这样可以清晰地控制循环的退出 。 - 技巧2: 传递循环状态: 如果循环中需要维护状态(比如分页的 token 或 offset),确保在循环路径的最后一步将更新后的状态值传递回 Loop Input。通常可以通过 Set 节点来实现。
3.4 应用场景
如前所述,Loop Over Items 节点并非普遍需要,但在以下特定场景中,它是不可或缺的。翔宇总结了最常见的几种:
节点的常用应用场景
- API 分页 (API Pagination): 这是最经典的应用场景。当 API 不一次性返回所有数据,而是分成多页返回时(通过页码
page、偏移量offset、或游标cursor/token),你需要使用 Loop Over Items 来迭代获取所有页面,直到最后一页 。- 通常需要设置
Reset = ON。 - 循环体内需要更新分页参数(页码+1,offset 增加,使用新的 token)。
- 需要用 If 节点判断是否到达最后一页(检查返回数据是否为空,或下一页标识是否存在)。
- 通常需要设置
- 速率限制 (Rate Limiting): 当外部服务限制了你在单位时间内的请求次数时,可以使用 Loop Over Items 将请求分批,并在每批处理后加入
Wait节点来控制请求频率,避免被封禁 。Batch Size根据限制调整。- 循环体内包含 API 调用节点和
Wait节点。
- 处理非迭代节点 (Processing with Non-Iterating Nodes): 有些 n8n 节点(尤其是旧节点或某些特定操作)被设计为只处理它们收到的第一个输入项,即使输入了多个项,后续的也会被忽略。如果你需要对列表中的每一项都执行这类节点的操作,就必须用 Loop Over Items (通常
Batch Size = 1) 来强制逐个处理 。- 官方文档提到的例子包括:
RSS Read, 某些数据库节点的insert/update操作 ,HTTP Request(如果你需要对每个 item 的 URL 单独发请求且需要控制过程),Code节点 (当设置为Run Once for All Items模式时,它只执行一次,但如果你想对每个 item 执行某段代码并分别获取结果,可能需要配合 Loop),Execute Workflow节点 (同样有执行模式选择) 。
- 官方文档提到的例子包括:
- 分块处理大型数据集 (Chunking Large Datasets): 如果上游节点返回了非常多的数据项(例如,几十万条数据库记录),一次性让后续节点处理可能导致 n8n 实例内存耗尽或执行超时。这时可以用 Loop Over Items 将数据分成较小的批次(如每批 1000 条)进行处理 。
- 需要严格顺序依赖的处理: 在极少数情况下,处理第 N+1 个数据项必须依赖第 N 个数据项的处理结果。这时可能需要使用
Batch Size = 1的循环,并在循环体内管理和传递这种依赖状态。(这种情况通常更复杂,可能需要 Code 节点辅助)。
3.5 常见报错及解决方案
Loop Over Items 节点虽然强大,但也容易因配置不当或理解偏差而出错。
错误提示解析与排错思路
- 错误: 无限循环 (Infinite Loop)
- 错误分析: 工作流卡在循环中不停地执行,永远不进入
done路径。最常见的原因是使用了Reset = ON但缺少有效的退出条件,或者退出条件逻辑错误导致永远无法满足 。也可能是循环路径没有正确连接回 Loop Input。 - 排错思路:
- 检查退出逻辑: 仔细检查循环体末尾用于判断是否结束的 If 节点(或其他逻辑)。确认它所依赖的条件(如
noItemsLeft == true或 API 返回特定标志)最终一定能够达成。 - 验证 Reset 用途: 确认你是否真的需要
Reset = ON。如果只是简单地处理一批固定数据,Reset = Off通常是正确的。 - 检查循环连接: 确保循环体的最后一个节点确实连接回了 Loop Over Items 节点的输入端。
- 跟踪状态变量: 如果循环依赖于某个状态变量(如页码、offset),在循环体内添加 Set 或 Code 节点打印该变量的值,观察它是否按预期变化并趋向于满足退出条件。
- 检查退出逻辑: 仔细检查循环体末尾用于判断是否结束的 If 节点(或其他逻辑)。确认它所依赖的条件(如
- 错误分析: 工作流卡在循环中不停地执行,永远不进入
- 错误: 循环提前终止 / 未处理所有项目 (Loop Terminates Prematurely)
- 错误分析: 循环在处理完所有预期的输入项之前就停止了,并可能直接跳到了
done路径(或者干脆停止)。这可能是因为循环体内的某个节点执行出错,或者循环体未能将结果正确传回 Loop Input 。 - 排错思路:
- 检查循环体内节点错误: 查看工作流执行历史,确认循环体内的所有节点在每次迭代中是否都成功执行。任何一个节点的失败都可能中断循环。
- 检查循环回传: 确保循环体的最后一个节点不仅连接回 Loop Input,而且确实产生了输出数据。如果某个迭代中,最后一个节点没有输出(例如,一个 If 判断后没有路径执行),Loop 节点可能无法接收到信号来开始下一次迭代 。
- 检查 Batch Size: 确认 Batch Size 设置是否合理。
- 错误分析: 循环在处理完所有预期的输入项之前就停止了,并可能直接跳到了
- 错误:
done输出数据不正确 / 丢失数据 (Incorrect Data Aggregation at ‘Done’ Output)- 错误分析: 从
done输出口得到的数据与预期不符,可能缺少某些迭代的结果,或者数据结构混乱。 - 排错思路:
- 检查循环体输出: 确保循环体中最后连接回 Loop Input 的那个节点输出的数据是你期望在
done时聚合的数据。Loop 节点通常会收集这个“回传”节点在每次迭代的输出。 - 数据转换: 检查循环体内部的数据转换逻辑是否正确。是不是在某个环节丢失或修改了关键数据?
- 理解聚合机制: Loop 节点默认将每次循环回传的数据项收集起来,最后在
done输出时形成一个列表。
- 检查循环体输出: 确保循环体中最后连接回 Loop Input 的那个节点输出的数据是你期望在
- 错误分析: 从
- 错误: 嵌套循环行为异常 (Issues with Nested Loops)
- 错误分析: 当在一个 Loop Over Items 的循环体内再放置一个 Loop Over Items 节点时,行为可能变得难以预测。社区报告提到,内部循环可能过早地将数据发送到外部循环的
done分支,或者显示的 item 计数变得混乱 。 - 排错思路:
- 翔宇强烈建议:避免嵌套 Loop Over Items 节点! 这是导致复杂性和问题的常见根源。
- 替代方案:使用子工作流 (Sub-workflows)。 将内部循环的逻辑封装到一个单独的工作流中,然后在主工作流的外部循环体内使用
Execute Workflow节点来调用这个子工作流。这通常更清晰、更易于管理和调试 。 - 如果必须嵌套: 需要非常小心地管理内外循环的状态和退出条件。可能需要利用
context变量(如前面提到的done变量,但需谨慎 )来同步状态。务必进行大量测试。
- 错误分析: 当在一个 Loop Over Items 的循环体内再放置一个 Loop Over Items 节点时,行为可能变得难以预测。社区报告提到,内部循环可能过早地将数据发送到外部循环的
- 错误: UI 显示的 Item 数量异常 (High Item Counts / Cartesian Product Issue)
- 错误分析: 在某些情况下,尤其是在循环内部生成新数据项或使用嵌套结构时,n8n 界面上显示的已处理 Item 数量可能看起来异常地大,甚至像笛卡尔积一样增长 。
- 排错思路:
- 关注实际输出: 不要过分依赖 UI 上显示的 Item 计数,它有时可能不完全反映实际逻辑。重点检查节点的实际 JSON 输出数据是否符合预期。
- 检查数据生成逻辑: 如果你在循环内部创建了新的数据项(例如,用 Set 或 Code 节点),确保逻辑正确,没有意外地产生过多重复或无关的数据。
- 使用子工作流: 再次推荐使用子工作流来封装内部逻辑,有助于隔离和理解数据流。
调试方法与日志定位技巧
- 节点内测试 (Test Step – 有限): 直接测试 Loop Over Items 节点本身通常意义不大,因为它需要循环体的配合。你需要测试整个循环结构。
- 手动执行 (Manual Executions): 运行整个工作流(或包含循环的部分),观察执行过程。重点关注循环体内的节点在前几次迭代中的输入、输出和执行状态。
- 固定输入数据 (Pinning Data): 在 Loop Over Items 节点之前的节点上固定 (Pin) 输出数据。这样,每次手动执行时,Loop 节点都会接收到完全相同的初始数据列表,方便你反复调试循环逻辑本身,而不受上游数据变化的影响。
- 循环内日志记录 (Logging): 在循环体内(例如,紧跟在
loop输出之后,或者在连接回 Loop Input 之前)加入一个简单的 Set 节点或 Code 节点。用它来输出关键信息,帮助你跟踪循环进度:- 输出当前迭代索引:
{{ $node["Loop Items"].context }} - 输出当前处理的数据项的关键字段:
{{ $json.id }} - 输出循环状态变量: 如
{{ $json.nextPageToken }}或{{ $json.offset }} - Code 节点可以使用
console.log(...)将信息打印到 n8n 的后端日志中。
- 输出当前迭代索引:
- 执行列表 (Execution View): 查看历史执行记录,特别注意 Loop Over Items 节点的执行详情。观察它执行了多少次迭代 (
loop输出),最终done输出的数据是什么 。
1.6 注意事项
最后,总结一些使用 Loop Over Items 节点的关键注意事项。
使用注意事项
- 明确使用场景 (Know When NOT to Use It): 重复强调,n8n 的设计哲学是让大多数节点自动处理多项输入。不要滥用 Loop Over Items 节点。只有在你确定遇到了需要它的特定场景(API 分页、速率限制、非迭代节点、大数据分块)时才使用它 。误用它会使简单的工作流变得不必要地复杂。
- 必须闭合循环 (Closing the Loop): 再次提醒,
loop输出路径上的最后一个节点必须连接回 Loop Over Items 节点的输入锚点,否则循环无法继续 。 - 谨慎使用 Reset (Reset Carefully): 充分理解
Reset选项的作用,尤其是在ON状态下,必须配合可靠的退出条件以防无限循环 。 - 避免嵌套 (Avoid Nesting): 尽量使用子工作流 (Execute Workflow 节点) 来替代嵌套的 Loop Over Items 节点,以获得更好的可读性、可维护性和更可预测的行为 。
- 性能影响 (Performance): 显式循环,特别是
Batch Size = 1且处理大量数据项时,会比 n8n 的内置隐式循环慢。因为它涉及到多次节点间的上下文切换和数据传递。在追求高性能的场景下,优先利用节点的批量处理能力,或者优化Batch Size。
节点版本兼容性与历史演变
- 名称变更 (Name Change): 记住它以前叫 “Split in Batches”。如果你在网上搜索旧资料或看到旧工作流,需要知道这个对应关系 。
- 持续改进中 (Ongoing Improvements): 社区对此节点的关注度很高,提出了不少改进建议(如更好的嵌套支持、更容易访问上下文变量等) 。这意味着 n8n 团队可能会在未来的版本中对其进行优化或功能增强。关注 n8n 的更新日志很重要 。
- 核心稳定性与行为演变: 作为一个核心节点,它会得到持续维护。但由于其逻辑相对复杂,特别是在
Reset和嵌套等边缘情况下,其行为可能会随着版本更新而有细微调整或 Bug 修复 。如果遇到奇怪的行为,除了检查自身逻辑,考虑是否与 n8n 版本有关,并查阅相关版本的 Release Notes 或社区讨论。
Loop Over Items 节点是处理特定迭代问题的强大工具,但需要谨慎使用。接下来,我们将学习最后一个核心逻辑节点——Merge 节点,它负责将分叉的工作流路径重新合并。
第四章:n8n 中文教程:合并 (Merge)
当你的工作流使用 If 或 Switch 节点产生了多个处理分支后,往往需要在某个点将这些分支重新汇合,以便执行共同的后续步骤。或者,你可能需要将来自完全不同源头的两组数据基于某种关联进行合并。这时,Merge (合并) 节点就登场了。
4.1 节点概览
节点功能定位与核心价值
Merge 节点的核心功能是将来自两个或多个独立输入流的数据项合并成一个单一的输出流 。
你可以把它想象成一个高速公路的汇合点。来自不同匝道(输入流)的车辆(数据项)在这里汇入主干道(输出流)。Merge 节点就像是交通控制系统,负责管理这些车辆如何有序地合并 。
Merge 节点的核心价值在于:
- 流程汇合: 它是连接条件分支(如 If/Switch 节点的输出)并使工作流恢复单一路径的关键组件 。
- 数据整合: 允许你将来自不同来源或经过不同处理路径的数据组合在一起。
- 数据丰富: 通过特定的合并模式(如按字段匹配),可以将一个数据源的信息添加到另一个相关联的数据源中,实现数据补充 。
输入 (Input) 与输出 (Output) 数据结构 (Input/Output Data Structure)
- 输入 (Input): Merge 节点可以有两个或多个输入锚点(标记为 Input 1, Input 2 等)。它会等待,直到所有连接到其输入锚点的上游分支都执行完毕并提供了数据(或者明确表示没有数据),然后才会执行合并操作 。
- 重要特性:等待行为。 这个“等待所有输入”的机制是 Merge 节点运作的基础,但也可能成为潜在的瓶颈。如果某个连接到 Merge 节点的上游分支因为某种原因卡住或永远不产生输出,那么 Merge 节点将一直等待,导致后续的工作流无法继续执行 。
- 输出 (Output): Merge 节点只有一个输出锚点。它输出的是合并后的单一数据流。输出数据项的结构和内容会根据所选择的 Mode (模式) 和相关选项而大相径庭 。理解不同的合并模式对输出结果的影响至关重要。
4.2 参数与配置
Merge 节点的配置核心在于选择合适的 Mode (模式),并根据所选模式设置相应的参数和选项。
每个配置项含义
- Mode (模式): 这是决定 Merge 节点如何合并数据的最关键参数 。n8n 提供了多种模式:
- Append (附加):
- 功能: 这是最简单的模式。它只是将 Input 1 的所有数据项、Input 2 的所有数据项(以及 Input 3 等,如果连接了更多输入)按顺序堆叠起来,形成一个更长的数据项列表。每个数据项的内部结构保持不变 。
- 参数:
Number of Inputs(输入数量) – 指定期望接收多少个输入流。 - 翔宇的实战理解:
- 主要用途: 最常用于合并来自 If 或 Switch 节点的不同分支,特别是当你知道只有一个分支会实际执行并产生数据时。在这种情况下,Append 模式实际上起到了“传递”作用,将那个活动分支的数据直接传递下去 。
- 次要用途: 当你需要将几组完全独立的数据集合并成一个大列表以进行后续统一处理时。
- 例子: If 节点判断订单状态,
true分支处理“已付款”订单,false分支处理“待付款”订单。如果后续需要对所有订单(无论状态如何)发送一个通用通知,可以在两个分支后放置一个 Merge 节点,模式设为 Append,然后连接到通知节点。
- Combine (组合):
- 功能: 这个模式旨在将两个输入流 (Input 1 和 Input 2) 的数据项进行内容级别的合并,即合并数据项内部的字段。它提供了多种具体的合并方式 。
- 参数:
Combine By(合并依据) – 选择如何确定哪些项应该合并在一起:- Matching Fields (按字段匹配): 这是 Combine 模式中最常用也最强大的方式。它会查找 Input 1 和 Input 2 中,指定关键字段 (key field) 值相同的数据项,并将它们合并 。这非常类似于数据库中的 JOIN 操作。
- 关键参数:
Input 1 Field,Input 2 Field: 指定用于匹配的关键字段名称(例如email,orderId,userId)。这两个字段的值必须相等,对应的两个数据项才会被视为匹配项。可以使用点表示法指定嵌套字段,如customer.id。注意:这里输入的是字段名文本,不是表达式。Output Type(输出类型): 控制如何处理匹配和不匹配的数据项,提供了类似 SQL JOIN 的选项 :Keep Matches(保留匹配项): 只输出那些在两个输入中都找到匹配项的数据(相当于 INNER JOIN)。Keep Non-Matches(保留不匹配项): 只输出在一个输入中有但在另一个输入中没有匹配项的数据(很少用,类似 ANTI JOIN 的概念)。Keep Everything(保留所有项): 输出所有数据项,匹配的项合并,不匹配的项也保留(相当于 FULL OUTER JOIN)。Enrich Input 1(丰富输入1): 保留 Input 1 的所有项,并将 Input 2 中匹配项的数据合并进来(相当于 LEFT JOIN)。这是数据丰富场景最常用的选项 。Enrich Input 2(丰富输入2): 保留 Input 2 的所有项,并将 Input 1 中匹配项的数据合并进来(相当于 RIGHT JOIN)。
- 翔宇的实战理解: 这是实现数据关联和补充的核心。例如,你有订单列表 (Input 1, 含
customerId) 和客户列表 (Input 2, 含customerId和customerName,customerAddress),可以用此模式,通过customerId匹配,选择Enrich Input 1,将客户的姓名和地址信息合并到对应的订单数据项中 。
- 关键参数:
- Position (按位置): 将 Input 1 的第 1 个数据项与 Input 2 的第 1 个数据项合并,第 2 个与第 2 个合并,以此类推 。
- 翔宇的实战理解: 这种模式非常依赖两个输入流的数据项数量完全相等且顺序严格对应。在少数情况下有用,例如,你分别抓取了姓名列表和对应的邮箱列表,并且确信它们的顺序是一致的。但大多数情况下,这种依赖顺序的方式比较脆弱,容易出错。如果数量不匹配,默认会丢弃没有对应位置的项(除非开启
Include Any Unpaired Items选项) 。
- 翔宇的实战理解: 这种模式非常依赖两个输入流的数据项数量完全相等且顺序严格对应。在少数情况下有用,例如,你分别抓取了姓名列表和对应的邮箱列表,并且确信它们的顺序是一致的。但大多数情况下,这种依赖顺序的方式比较脆弱,容易出错。如果数量不匹配,默认会丢弃没有对应位置的项(除非开启
- All Possible Combinations (所有可能组合): 将 Input 1 中的每一个数据项与 Input 2 中的每一个数据项进行配对和合并 。如果 Input 1 有 M 项,Input 2 有 N 项,输出将是 M * N 项。
- 翔宇的实战理解: 这种模式会产生笛卡尔积,输出量可能非常大。用途相对较少,可能用于生成测试数据、排列组合场景等 。
- Matching Fields (按字段匹配): 这是 Combine 模式中最常用也最强大的方式。它会查找 Input 1 和 Input 2 中,指定关键字段 (key field) 值相同的数据项,并将它们合并 。这非常类似于数据库中的 JOIN 操作。
- SQL Query (SQL 查询):
- 功能: 允许高级用户直接编写 SQL 查询语句(使用内置的 AlaSQL 库)来定义合并逻辑。输入数据流会被视为名为
input1,input2等的表 。 - 翔宇的实战理解: 提供了最大的灵活性,可以实现非常复杂的合并、过滤和转换逻辑,但前提是你需要熟悉 SQL 语法。对于零代码基础的用户不推荐。
- 功能: 允许高级用户直接编写 SQL 查询语句(使用内置的 AlaSQL 库)来定义合并逻辑。输入数据流会被视为名为
- Choose Branch (选择分支):
- 功能: 节点会等待两个输入都到达,然后根据你的选择,只输出其中一个输入流的数据(或者输出一个空项) 。
- 参数:
Output(输出) – 选择Input 1 Data,Input 2 Data, 或A Single, Empty Item。 - 翔宇的实战理解: 适用于这样的场景:你需要确保两个并行的流程都执行完毕(可能它们各自有副作用,如写入数据库),但后续流程只需要其中一个流程的结果数据。
- Append (附加):
- Combine 模式选项 (Combine Mode Options): 这些选项只在
Mode = Combine时可用,用于微调合并行为 :- Clash Handling (冲突处理): 当 Input 1 和 Input 2 中被合并的两个数据项都包含同名字段时,如何处理?
Prioritize Input 1 / Input 2: 保留指定输入源的字段值,丢弃另一个。Always Add Input Number to Field Names: 保留两个字段,并在字段名后附加_1或_2以区分来源。Deep MergevsShallow Merge: 对于嵌套的对象字段,是递归地合并内部属性 (Deep),还是只合并顶层属性 (Shallow)?- 翔宇建议: 默认通常是
Prioritize Input 2+Shallow Merge。根据你的数据结构和需求选择。如果需要合并嵌套对象的内容,务必选择Deep Merge。
- Fuzzy Compare (模糊比较): 在
Matching Fields模式下,进行匹配比较时是否忽略数据类型差异(例如,将文本"3"和数字3视为相等)?默认Off(不忽略,严格比较) 。 - Disable Dot Notation (禁用点表示法): 阻止在字段名中使用
parent.child的形式访问嵌套属性。极少需要开启 。 - Multiple Matches (多重匹配): (仅用于
Matching Fields模式) 如果 Input 1 中的一个项匹配了 Input 2 中的多个项,是输出所有匹配结果(每个匹配产生一个输出项),还是只保留第一个匹配结果?默认Include First Match Only。 - Include Any Unpaired Items (包含未配对项目): (仅用于
Position模式) 是否保留那些在另一个输入流中没有对应位置的数据项?默认Off(丢弃未配对项) 。
- Clash Handling (冲突处理): 当 Input 1 和 Input 2 中被合并的两个数据项都包含同名字段时,如何处理?
默认值或者可选值具体介绍
- 默认 Mode: 可能默认为
Append或Combine>Matching Fields。 - 默认 Combine 选项: 通常是
Prioritize Input 2on clash,Fuzzy CompareOff,Include First Match Only,Discard Unpaired Items(for Position mode)。
不同场景的推荐配置
- 场景 1: 简单合并 If 节点的 True/False 分支 (假设只有一个分支会执行)
- 配置:
Mode = Append,Number of Inputs = 2。
- 配置:
- 场景 2: 将客户姓名和邮箱 (来自 Input 2, 字段
name,email) 添加到对应的订单列表 (来自 Input 1, 字段orderId,customerId),基于customerId匹配- 配置:
Mode = CombineCombine By = Matching FieldsInput 1 Field = customerIdInput 2 Field = customerIdOutput Type = Enrich Input 1(保留所有订单,添加匹配的客户信息) 。- (根据需要调整 Clash Handling)
- 配置:
- 场景 3: 合并两个顺序对应的列表,例如姓名列表 (Input 1) 和职位列表 (Input 2)
- 配置:
Mode = CombineCombine By = Position- (确保两个列表长度一致,或根据需要开启
Include Any Unpaired Items) 。
- 配置:
- 场景 4: 生成所有用户 (Input 1) 和所有产品 (Input 2) 的可能购买组合
- 配置:
Mode = CombineCombine By = All Possible Combinations。
- 配置:
4.3 数据映射与表达式
与其他逻辑节点不同,Merge 节点的配置很少直接使用表达式。
表达式写法
- 主要配置方式: Merge 节点的模式选择、合并依据、字段匹配等核心配置,通常是通过下拉菜单选择和直接输入字段名称(作为文本) 来完成的 。
- 不适用场景: 你不能在
Input 1 Field或Input 2 Field中使用像{{ $json.id }}这样的表达式来动态指定匹配字段。你必须输入字段的字面名称,如id或customer.id。 - 潜在用途:
SQL Query模式: 表达式(或更准确地说是 SQL 语法)是这种模式的核心。- 未来可能: n8n 可能会在未来版本中增加表达式在其他模式中的应用场景,但目前不是主流用法。
- 重点: 对于初学者,关键是理解不需要在 Merge 节点的主要配置中使用
{{ }}表达式,而是要准确地输入字段名。
映射技巧与常见易错点
- 易错点1: 字段名错误: 在
Matching Fields模式下,输入的Input 1 Field和Input 2 Field名称必须精确匹配两个输入流中对应的字段名,包括大小写。这是导致匹配失败的最常见原因 。 - 易错点2: 在字段名处使用表达式: 如上所述,不要在指定匹配字段的地方尝试使用
{{ }}表达式 。 - 易错点3: 模式选择错误: 选择了错误的
Mode或Combine By方式。例如,想按 Key 合并却选了Append,或者想按位置合并却选了Matching Fields。仔细阅读每个模式的说明。 - 易错点4: 输入项数量不匹配 (对 Position 模式):
Combine By Position模式对输入项的数量和顺序非常敏感。如果两个输入流的项目数不同,默认会丢失数据 。 - 易错点5: 数据类型不匹配 (对 Matching Fields 模式): 用于匹配的关键字段,其值的数据类型应该一致或可比较。例如,用数字
123和文本"123"作为 Key 进行匹配,在默认Fuzzy Compare = Off的情况下会失败。可以考虑开启Fuzzy Compare或在上游统一数据类型 。 - 技巧1: 上游数据准备: 在数据到达 Merge 节点之前,使用 Set 或 Code 节点确保用于匹配的关键字段名称统一、数据类型一致,可以大大提高合并成功率和可预测性 。
- 技巧2: 检查输入数据: 在配置 Merge 节点前,务必检查 Input 1 和 Input 2 的数据结构,明确要用于匹配的字段名和它们的类型。
4.4 应用场景
Merge 节点是构建复杂、多分枝工作流的粘合剂。翔宇在以下场景中频繁使用它:
节点的常用应用场景
- 汇合条件分支 (Rejoining Conditional Branches): 这是 Merge 最基础也是最常见的用途。当使用 If 或 Switch 节点将流程分成不同路径后,如果这些路径最终需要执行相同的后续步骤,就需要用 Merge 节点将它们汇合起来。通常使用
Mode = Append。 - 数据丰富/关联 (Data Enrichment): 从一个数据源(如订单列表)出发,根据其中的某个 ID (如
customerId),从另一个数据源(如 CRM 系统、数据库表、或另一个 API 端点)查找相关信息(如客户详情),然后使用 Merge 节点 (Mode = Combine,Combine By = Matching Fields,Output Type = Enrich Input 1) 将查找到的信息合并回原始数据项中 。 - 数据同步准备 (Data Synchronization Prep): 在需要在两个系统间同步数据时,Merge 节点常用于比较两个系统的数据。例如,从系统 A 获取所有用户列表 (Input 1),从系统 B 获取所有用户列表 (Input 2),然后使用 Merge 节点 (
Mode = Combine,Combine By = Matching Fields基于 email 或 ID) 进行比较 :- 使用
Output Type = Keep Matches可以得到两边都存在的用户(可能需要更新)。 - 使用
Output Type = Enrich Input 1并检查 Input 2 数据是否为空,可以找到只在系统 A 中存在的用户(可能需要在系统 B 中创建)。 - 使用
Output Type = Enrich Input 2并检查 Input 1 数据是否为空,可以找到只在系统 B 中存在的用户(可能需要在系统 A 中创建或删除)。 - (注意: n8n 还有一个专门的
Compare Datasets节点,有时更适合复杂的比较和同步场景 )。
- 使用
- 合并并行处理结果 (Combining Parallel Operations): 如果你的工作流设计为并行执行几个不同的数据获取或处理任务(例如,同时查询天气信息和日历事件),并且这些任务的结果需要组合在一起(可能基于时间或其他关联),可以使用 Merge 节点来实现 。
4.5 常见报错及解决方案
Merge 节点由于其“等待”行为和多样的合并模式,也可能出现一些特定的问题。
错误提示解析与排错思路
- 错误: 节点卡住不执行 / 工作流在此停止 (Node Never Finishes / Workflow Stalls)
- 错误分析: 这是 Merge 节点最独特的潜在问题。由于它需要等待所有连接的输入都提供数据后才执行,如果其中任何一个上游分支因为错误、无限循环、或者逻辑上就不会产生输出,Merge 节点就会一直等待下去,导致工作流卡住 。
- 排错思路:
- 检查所有上游分支: 仔细检查连接到 Merge 节点每一个输入锚点的完整路径。
- 确认分支能完成: 确保每个分支的逻辑最终都能执行到连接 Merge 节点的地方。
- 确认分支有输出: 确保每个分支在预期情况下确实会产生数据输出。如果某个分支在某些条件下可能不产生输出(例如,If 分支后没有节点,或者 Filter 过滤掉了所有数据),你需要考虑这种可能性。
- 处理无输出分支: 如果某个分支可能无输出,可以考虑:
- 在该分支连接 Merge 之前,使用一个设置了
Always Output Data = ON的节点(如 Set 节点)来确保即使没有实际数据,也会有一个空 item 传递给 Merge 。但这需要小心,因为它可能影响后续逻辑。 - 重新设计逻辑,可能不需要 Merge,或者使用不同的合并策略。
- 在该分支连接 Merge 之前,使用一个设置了
- 检查上游错误: 确认所有上游分支都没有未处理的错误。
- 错误: 输出结果不符合预期 (Incorrect Output / Missing Data / Duplicated Data)
- 错误分析: 合并后的数据不是你想要的,字段丢失、数据重复、或者结构混乱。这通常是由于选择了错误的
Mode或Combine By方式,或者相关选项(如Clash Handling,Multiple Matches)配置不当 。 - 排错思路:
- 重新审视模式选择: 确认你选择的
Mode(Append, Combine 等) 和Combine By(Matching Fields, Position 等) 是否真的符合你的合并目标。是不是应该用Matching Fields而误用了Append? - 核对匹配字段: 如果使用
Matching Fields,再次确认Input 1 Field和Input 2 Field的名称是否完全正确,且两个输入流中确实存在这些字段。 - 检查 Combine 选项: 仔细检查
Clash Handling,Multiple Matches,Include Unpaired Items等选项的设置是否符合预期。例如,是否因为Clash Handling覆盖了重要数据?是否因为Multiple Matches产生了不必要的重复? - 使用 Test Step: 在 Merge 节点上使用
Test step,传入样本数据,观察输出结果,与预期对比 。 - 检查输入数据: 仔细检查 Input 1 和 Input 2 的数据结构和内容,确认它们是否是你进行合并时所假设的那样。
- 重新审视模式选择: 确认你选择的
- 错误分析: 合并后的数据不是你想要的,字段丢失、数据重复、或者结构混乱。这通常是由于选择了错误的
- 错误: 输出中出现意外的类型错误 (Type Errors in Output – Less Common)
- 错误分析: 合并操作本身(尤其是在处理冲突或使用模糊比较时)可能导致输出字段的数据类型与预期不符。
- 排错思路: 检查输出数据的 JSON 结构和类型。调整
Clash Handling或Fuzzy Compare选项。考虑在上游节点统一数据类型。
- 历史遗留问题: If 节点双分支执行 (Legacy Issue: Both If Branches Execute)
- 错误分析: 在使用 n8n v0 执行顺序的老工作流中,Merge 节点的存在可能导致其前面的 If 节点的两个分支都被执行,即使条件只满足一个 。
- 排错思路: 确认工作流设置中的
Execution Order。如果是V0 (Legacy),并且遇到了此问题,最佳方案是将其改为V1 (Recommended)。如果不能更改,则需要重新设计 If 和 Merge 周围的逻辑,可能需要避免直接将 If 的两个输出都连到同一个 Merge 节点 。
调试方法与日志定位技巧
- 节点内测试 (Test Step): 点击 Merge 节点上的 “Test step”,n8n 会尝试使用上游节点缓存的数据作为输入来执行合并。你可以在 OUTPUT 面板看到合并结果的预览 。
- 输入/输出面板 (Input/Output Panel): 这是诊断 Merge 问题的核心区域。仔细检查 INPUT 1, INPUT 2 等面板中的数据,确认它们是你期望合并的内容。然后,对比 OUTPUT 面板中的结果,看合并是否符合预期。
- 隔离输入: 如果怀疑某个输入流有问题,可以尝试暂时断开其他输入流的连接(但这可能导致 Merge 节点因等待输入而无法执行)。或者,用 Code 节点替换某个输入流,注入简单的、已知的数据,来测试 Merge 节点的行为 。
- 使用简单数据测试: 创建一个简单的工作流,使用两个 Code 节点分别输出简单的、结构化的 JSON 数据(如 中的示例),然后连接到一个 Merge 节点。通过改变 Merge 节点的模式和选项,并观察输出,可以快速理解不同设置的效果。
- 执行列表 (Execution View): 查看历史执行记录,观察数据是如何从不同分支流入 Merge 节点的,以及 Merge 节点最终输出了什么 。
4.6 注意事项
掌握 Merge 节点的关键在于理解其模式和等待行为。
使用注意事项
- 理解模式差异:
Append,Combine by Matching Fields,Combine by Position,Combine by All Possible Combinations这几种模式的输出结果差异巨大。务必根据你的具体需求(是简单汇合,还是需要关联合并)来选择最合适的模式 。 - 注意等待行为: 时刻记住 Merge 节点会等待所有输入。在设计工作流时,要确保所有通向 Merge 节点的路径都能正常结束并提供数据(或明确无数据)。避免创建会“饿死”Merge 节点的流程分支 。
- Combine 模式是二元的:
Mode = Combine下的所有子模式 (Matching Fields,Position,All Possible Combinations) 都设计为合并两个输入流 (Input 1 和 Input 2)。如果需要合并三个或更多流的数据内容,你需要使用多个 Merge 节点(例如,先合并 1 和 2,再将结果与 3 合并)或使用Append模式(如果只是简单堆叠列表) 。 - 关注冲突处理: 在使用
Combine模式时,仔细考虑Clash Handling选项。默认设置可能不总是你想要的,尤其是在合并具有复杂嵌套结构或同名字段的数据时,不当的设置可能导致数据丢失 。 - Position 模式的风险:
Combine by Position模式非常依赖输入数据的顺序和数量一致性。除非你非常有把握,否则优先考虑使用Combine by Matching Fields,因为它基于数据内容进行关联,通常更健壮 。
节点版本兼容性与历史演变
- 重大改版 (0.194.0): Merge 节点经历过一次重大的功能和界面改版(大约在 n8n 0.194.0 版本)。如果你参考的是这个版本之前的教程或示例,可能会发现模式名称、选项或行为有所不同。当前的文档和本教程描述的是现代版本的功能 。
- 执行顺序影响: 如前所述,n8n v1.0 引入的执行顺序变更,显著改变了 Merge 节点与上游 If 节点的交互方式,解决了旧版本中的一个潜在问题 。
- 核心稳定性与演进: 作为数据流汇合的基础节点,Merge 的核心功能是稳定的。但由于其模式和选项较多,n8n 团队可能会根据用户反馈和需求,在未来版本中对其进行优化、增加新模式或调整选项。关注官方发布说明是了解这些变化的最佳途径 。
Merge 节点是完成复杂工作流逻辑闭环、整合数据的关键。熟练掌握它,你的 n8n 自动化能力将更上一层楼。
教程总结与后续学习
我们已经深入探讨了 n8n 中四个至关重要的逻辑节点:Filter, If, Loop Over Items, 和 Merge。从翔宇的视角来看,这四个节点构成了 n8n 工作流中实现智能控制和数据调度的“四大支柱”。
- Filter (筛选器): 它是你的数据“质检员”,帮你剔除杂质,只保留精华,确保后续流程处理的是干净、相关的数据。记住,它的目标是包含或排除。
- If (如果): 它是工作流的“决策者”,赋予流程根据数据情况“兵分两路”的能力。理解它的逐项判断机制和与 Switch 节点的区别是关键。
- Loop Over Items (循环处理项目): 它是处理特定迭代场景的“专家”,尤其擅长应对 API 分页、速率限制和非迭代节点。但切记,不要滥用它,n8n 的大部分节点天生就会循环。使用它时,务必理解
Batch Size,Reset选项,并确保闭合循环和设置退出条件。 - Merge (合并): 它是流程的“汇合点”,将不同的数据流重新统一。掌握
Append和Combine(尤其是Matching Fields和Position) 模式的区别,并注意其等待所有输入的行为特性。
翔宇认为,对于零代码基础的用户来说,真正掌握这些节点意味着你能够将 n8n 从一个简单的任务执行器,转变为一个能够应对复杂业务逻辑的强大自动化平台。不要害怕在实践中尝试这些节点,动手搭建、测试、调试是最好的学习方式。遇到问题时,回顾本教程中的示例、常见错误和注意事项,或者查阅 n8n 的官方文档和活跃的社区论坛。
最后,如果你希望看到更多关于这些节点以及 n8n 其他功能的实战应用案例和进阶技巧,欢迎关注 “翔宇工作流” YouTube 频道。翔宇会在那里分享更多真实场景下的自动化解决方案,帮助你将 n8n 的潜力发挥到极致。
感谢你的阅读,希望这篇深度教程能为你的 n8n 学习之旅提供坚实的基础和清晰的指引!祝你在自动化构建的道路上越走越远!
