在 n8n 工作流程中,我遇到了一個看似簡單卻讓人困惑的問題:我想合併兩個 Google Sheets 的資料,直覺上想用 Code 節點處理,結果卻失敗了。
這篇文章記錄我踩坑的過程,以及正確的解決方案。
問題一:為什麼 Code 節點無法合併資料?
我的錯誤嘗試
我的工作流程很簡單:
[Google Sheets A] → [Code Node] ← [Google Sheets B]我想在 Code 節點中把兩個 Sheets 的資料合併成一個陣列,於是寫了這樣的程式碼:
// ❌ 錯誤嘗試:想要同時取得兩個來源並合併const dataA = $input.all(); // 只能取得其中一個來源!const dataB = ???; // 無法取得另一個來源
// 原本想這樣合併return [...dataA, ...dataB];結果:失敗!
$input.all() 只能取得「主要輸入」,當有兩條線連入時,我根本無法用這個方法同時取得兩個資料源。
嘗試用 $('節點名稱') 解決
看了文件後,我發現可以用 $('節點名稱').all() 來明確指定節點:
// ❌ 還是不建議這樣做const sheetsA = $('Google Sheets A').all();const sheetsB = $('Google Sheets B').all();
// 手動合併return [...sheetsA, ...sheetsB];雖然這樣「技術上」可行,但這不是正確的做法!
正確解法:使用 Merge 節點
n8n 提供了專門用來合併資料的節點:Merge 節點。
正確的工作流程應該是:
[Google Sheets A] → [Merge Node] ← [Google Sheets B] ↓ [Code Node]Merge 節點的設定方式
-
新增 Merge 節點
- 在工作流程中新增「Merge」節點
- 將兩個資料源分別連接到 Merge 節點
-
選擇合併模式 (Mode)
常用的合併模式:
模式 說明 使用時機 範例 Append 單純串接 兩個清單合併成一個 合併兩個 Google Sheets 的所有資料 Combine 依索引配對 兩個清單依順序一對一合併 訂單資料 + 對應的物流資料 Merge By Key 依欄位值合併 類似 SQL JOIN 用戶 ID 合併訂單和用戶資料 Choose Branch 選擇性輸出 只保留其中一個分支 A/B 測試選擇執行路徑 -
實際範例
模式 (最常用) 假設你有兩個 Google Sheets,想合併所有資料:
設定步驟:1. Mode: Append2. 不需要額外設定3. 輸出: [來源 A 的所有資料] + [來源 B 的所有資料]輸入 1 (Google Sheets A):
[{ "name": "產品 A", "price": 100 },{ "name": "產品 B", "price": 200 }]輸入 2 (Google Sheets B):
[{ "name": "產品 C", "price": 300 },{ "name": "產品 D", "price": 400 }]Merge 輸出:
[{ "name": "產品 A", "price": 100 },{ "name": "產品 B", "price": 200 },{ "name": "產品 C", "price": 300 },{ "name": "產品 D", "price": 400 }] -
實際範例
By Key 模式 當你需要依據某個欄位值合併資料 (類似 SQL JOIN):
設定步驟:1. Mode: Merge By Key2. Merge By Key: userId (指定要比對的欄位)3. Output Data: Both Inputs (輸出合併後的完整資料)輸入 1 (用戶資料):
[{ "userId": 1, "name": "張三" },{ "userId": 2, "name": "李四" }]輸入 2 (訂單資料):
[{ "userId": 1, "orderAmount": 500 },{ "userId": 2, "orderAmount": 800 }]Merge 輸出:
[{ "userId": 1, "name": "張三", "orderAmount": 500 },{ "userId": 2, "name": "李四", "orderAmount": 800 }]
Merge 節點的優勢
- ✅ 原生支援:不需要寫程式碼,透過視覺化介面設定
- ✅ 多種合併模式
、Combine、Merge By Key、Choose Branch - ✅ 視覺化清楚:工作流程更易維護和理解
- ✅ 效能更好:原生實作比 JavaScript 迴圈更快
- ✅ 錯誤處理:自動處理資料型別不符等問題
重要觀念Code 節點適合用於「參考」其他節點的值來處理資料,而不是用來「合併」多個資料源。如果需要合併資料,請使用 Merge 節點。
經驗法則:
- 需要合併兩個清單 → 用 Merge 節點
- 需要用另一個節點的值來過濾資料 → 用 Code 節點 +
$('節點名稱')
何時用 Code 節點的 $('節點名稱') ?
$('節點名稱') 語法適合用於這種情境:
// ✅ 正確使用情境:參考另一個節點的「單一值」來處理資料const products = $input.all(); // 主要資料
// 從另一個節點取得「要排除的分類」const excludeCategory = $('設定節點').first().json.category;
// 用這個參考值來過濾資料const filtered = products.filter(item => { return item.json.category !== excludeCategory;});
return filtered;使用場景對比:
| 需求 | 應使用的方法 | 說明 |
|---|---|---|
| 合併兩個資料源成單一清單 | Merge 節點 | ✅ 推薦做法 |
| 參考另一個節點的單一值來處理資料 | $('節點名稱').first() | ✅ Code 節點適用 |
| 依條件比對兩個清單 | $('節點A').all() + $('節點B').all() | ⚠️ 可行但複雜 |
問題二 節點的資料為什麼變成 1 筆?
問題現象
除了合併資料,我還遇到另一個困惑:當我用 Aggregate 節點處理資料後,明明輸入有 5 筆資料,為什麼在 Code 節點中只看到 1 個項目?
// Aggregate 前:5 筆資料// Aggregate 後:只有 1 筆?
const data = $('Aggregate').all();console.log(data.length); // 輸出:1 (而不是預期的 5)原因 會「打包」資料
Aggregate 節點的運作方式:
- 將多筆項目「打包」成單一項目
- 真正的資料陣列被放在
.json.欄位名稱裡面
資料結構變化:
// Aggregate 前 (5 筆)[ { json: { name: "產品 A", price: 100 } }, { json: { name: "產品 B", price: 200 } }, { json: { name: "產品 C", price: 300 } }, // ...]
// Aggregate 後 (1 筆包裹)[ { json: { products: [ // ← 真正的資料在這裡! { name: "產品 A", price: 100 }, { name: "產品 B", price: 200 }, { name: "產品 C", price: 300 }, // ... ] } }]錯誤 vs 正確的處理方式
// ❌ 錯誤:直接迭代只會執行一次const data = $('Aggregate').all();
data.forEach(item => { // 這裡只會執行一次! console.log(item);});// ✅ 正確:先「解包」取出內部陣列const aggregatedRaw = $('Aggregate').all();const actualData = aggregatedRaw[0].json.products || [];
// 現在可以正常迭代每一筆資料actualData.forEach(item => { console.log(item.name);});
// 過濾範例const filtered = actualData.filter(item => item.price > 300);return filtered.map(item => ({ json: item }));重要提醒
aggregatedRaw[0].json.products中的products要改成你在 Aggregate 節點「Put Output in Field」設定的欄位名稱。
簡潔寫法:使用解構賦值
如果你熟悉 JavaScript 解構語法,可以一步到位:
// ✅ 直接解構取出資料陣列const [{ json: { products: items } }] = $('Aggregate').all();
// items 現在就是真正的資料陣列return items .filter(item => item.price > 300) .map(item => ({ json: item }));完整實戰範例:結合 Aggregate 和參考節點
假設你的工作流程是:
- 資料經過 Aggregate 打包
- 需要參考另一個節點的值來過濾資料
// 步驟 1:解包 Aggregate 資料const aggregatedRaw = $('Aggregate').all();const sourceData = aggregatedRaw[0].json.allData || [];
// 步驟 2:取得參考值const excludeKeyword = $('參考節點').first().json.targetName;
// 步驟 3:過濾資料const result = sourceData.filter(item => { if (!item) return false; const itemKey = Object.keys(item)[0]; return itemKey !== excludeKeyword;});
// 步驟 4:回傳結果return result.map(item => ({ json: item }));除錯技巧使用
console.log(JSON.stringify(data, null, 2))可以清楚看到資料的完整結構,幫助你快速找到問題。
常見陷阱與解決方案
| 陷阱 | 解決方案 |
|---|---|
| 用 Code 節點合併資料 | 使用 Merge 節點 |
| 忘記解包 Aggregate | 記得存取 .json.欄位名稱 |
| 節點名稱拼錯 | 檢查左側節點列表的確切名稱 |
| 未檢查空值 | 加入 if (!item) 檢查 |
| 欄位名稱錯誤 | 確認 Aggregate「Put Output in Field」設定 |
重要只有當 Code Node 設定為「Run Once for All Items」模式時,才能使用
$('節點名稱').all()語法。
總結:何時用 Code / Merge / Aggregate?
經過這次踩坑,我整理出這三個節點的正確使用時機:
Merge 節點
- ✅ 合併兩個資料源成單一清單
- ✅ 依 key 值合併資料 (像 SQL JOIN)
- ✅ 選擇性保留某個分支的資料
Aggregate 節點
- ✅ 將多筆資料打包成單一項目
- ✅ 準備資料傳給 HTTP Request 等節點
- ✅ 彙總資料
Code 節點 + $('節點名稱')
- ✅ 參考其他節點的單一值來處理資料
- ✅ 複雜的條件判斷和資料轉換
- ❌ 不要用來合併資料 (請用 Merge)
記住這些原則,就能避免像我一樣踩坑了!
參考資料:
回報錯字、失效連結,或告訴我你想看的延伸主題。