我在使用 MongoDB 的 n8n 工作流中看到了非常奇怪的行為。
工作流是:
- Webhook 接收
LeadUserID 和 property_id
- Set 節點格式化資料
- MongoDB 節點按
property_id 尋找 Campaign 文件
- 第二個 MongoDB 節點搜尋
Campaign_Members 集合,使用:
- 來自 webhook 的
Customer_ID
- 來自先前 MongoDB 節點的
Campaign_ID(_id)
問題是第二個 MongoDB 節點的行為不一致:
- 自動工作流執行 → 返回 0 項
- 編輯器中首次手動執行 → 返回 0 項
- 第二次手動執行(未更改任何內容)→ 成功返回匹配的文件
查詢為:
={
"Customer_ID": "{{ $('Edit Fields').first().json.body.args.LeadUserID }}",
"Campaign_ID": "{{ $json._id }}"
}
資料已存在於資料庫中,所以這不是最近插入的競爭條件。
令我困惑的是,第一次和第二次執行之間沒有任何變化,但第二次執行卻始終成功。
有人遇到過 MongoDB 節點的這種「首次執行失敗,第二次執行成功」的行為嗎?這可能與資料類型(如 ObjectId 對 string)、運算式求值時機、執行狀態或其他 n8n 特定的怪癖有關嗎?
關於你的 n8n 設定的資訊
- n8n 版本:最新版本
- 資料庫(預設:MongoDB):
- n8n EXECUTIONS_PROCESS 設定(預設:own, main):
- 執行 n8n 方式:n8n cloud
- 作業系統:
Hi @alee_Ostovar
您需要確保 Campaign_ID 是作為 ObjectId 傳遞的。
在 n8n 中處理 ObjectId 最可靠的方法是在 Code 節點中建立查詢物件。這可以防止 n8n 意外將您的 ID 轉換為字串。
- 在您的第二個 MongoDB 節點之前新增一個 Code 節點。
- 使用此程式碼:
const leadUserId = $('Edit Fields').first().json.body.args.LeadUserID;
const campaignId = $json._id;
return {
query: {
Customer_ID: leadUserId,
Campaign_ID: { "$oid": campaignId } // 這會告訴 MongoDB 將其視為 ObjectId
}
};
- 在您的 MongoDB 節點中,不要手動編寫 JSON,而是引用 Code 節點的輸出:
{{ $json.query }}
@alee_Ostovar
我之前在 Postgres 上遇到過非常類似的問題。據我所能判斷,這與觸發器上的固定資料有關,特別是如果觸發器是 webhook 或「由另一個工作流程執行」觸發器的話。雖然不應該發生這種情況,但觸發器的固定資料在生產執行期間被傳遞到下游,有時該資料是錯誤的/只是來自手動測試的空值。然後當你手動開啟失敗的執行時 - 觸發器的固定資料會被覆蓋,它就神奇地正常工作了。
我能夠通過確保在工作流程上線前始終取消固定觸發器的資料來解決這個問題。從那以後就沒有發生過,而之前大約 50% 的時間會出現這個問題。希望這對你有幫助。
感謝你的建議。在我的情況下,Campaign_ID 並非儲存為 MongoDB ObjectId。它在 Campaign_Members 集合中被儲存為字串(它只是一個參考值,不是實際的 ObjectId 欄位)。
因此,將其包裝為 { "$oid": campaignId } 會使查詢搜尋 ObjectId,這不會與儲存的字串值相符。
謝謝,我其實已經試過了。我確保了 Webhook 觸發器沒有被釘住,但不幸的是行為沒有改變。
我找到的一個解決方法是用 Code 節點取代 Edit Fields (Set) 節點,該節點會建立相同的輸出。使用 Code 節點時,工作流程每次都能正確運作。
我還注意到另一個似乎與 Edit Fields 節點相關的問題:當透過它傳遞 MongoDB 文件時,MongoDB 的 _id 有時會被轉換為 Buffer,而不是保持其原始形式。這似乎是 Set/Edit Fields 節點本身的另一個問題。
目前,使用 Code 節點可以解決這兩個問題,但感覺更像是在避開 bug 而不是修復它。我試圖理解為什麼原生的 Set/Edit Fields 節點會有這樣的行為。
我不認為這是數據類型不匹配的問題,因為兩個 variables 都存儲為字符串,而查詢也使用字符串值。如果是類型不匹配,我預期它會持續失敗,而不是只在第一次執行時失敗。
我通過用代碼節點替換**編輯字段(設置)**節點來解決這個問題。這樣做有效,但我在試著理解為什麼原生 Set 節點首先會表現出這種行為。
在 MongoDB 中,_id 欄位不是字串;它是一個名為 ObjectId 的特殊 BSON 類型。
在您的查詢中:
{
"Customer_ID": "{{ $('Edit Fields').first().json.body.args.LeadUserID }}",
"Campaign_ID": "{{ $json._id }}"
}
透過將 {{ $json._id }} 包裹在雙引號中,您明確告訴 n8n 將 Campaign_ID 視為字串。當 MongoDB 收到一個字串用於儲存為 ObjectId 的欄位時,它會傳回零個結果,因為字串不等於 ObjectId,即使字元完全相同。
在第一次手動執行期間,節點可能無法如預期般精確解析表達式,或者 DB 查詢失敗。但是,在第二次執行期間,編輯器通常會使用前一個節點執行狀態的快取輸出。
這是否適用於您的情況?
這正是令人困惑的部分,實際上這是我遇到的第二個問題。
Edit Fields 節點有時會將 _id 傳遞為 [object Object],所以我懷疑這是一個資料型別問題。為了驗證,我在 MongoDB 節點之後(編輯欄位之前)立即使用 Code 節點記錄了該值。以下是輸出:
[
{
"value": "6a579e9014256aa1f68ca592",
"typeof": "string",
"constructor": "String",
"isObject": false,
"proto": "Object"
}
]
表面上看,它似乎是一個原始字串。但是,考慮到我看到的行為,我懷疑可能仍然存在底層 BSON 包裝器或某種內部型別/代理,這些在 Code 節點的輸出中沒有反映出來。這將解釋為什麼 MongoDB 節點稍後會將該值視為物件而不是字串。
若要每次都強制它成為原始字符串(自動或手動),你應該用 JavaScript 字符串構造函數包裝你的表達式。
將你的查詢從這個:
{
"Customer_ID": "{{ $('Edit Fields').first().json.body.args.LeadUserID }}",
"Campaign_ID": "{{ $json._id }}"
}
改成這個:
{
"Customer_ID": "{{ String($('Edit Fields').first().json.body.args.LeadUserID) }}",
"Campaign_ID": "{{ String($json._id) }}"
}
透過用 String() 包裝表達式,你強制 n8n 表達式引擎在值傳遞到 MongoDB 節點之前執行 JavaScript 轉換。這會移除任何 BSON 包裝器、代理或對象元數據,確保 MongoDB 每次都收到一個字面字符串。