Skip to main content

Switch Control Node

Overview

The Switch control node is a multi-branch conditional processing node that routes data to different branches for execution based on the result of an expression evaluation. It supports complex condition evaluation and multiple matching modes, making it useful for implementing multi-way business logic branching.

note

The Switch node is supported by the workflow engine but is not offered as a draggable node in the console builder palette — only the If-Else and ForEach control nodes appear there. Switch is documented here for completeness and for workflows defined directly via configuration; in the visual builder, achieve multi-way branching by chaining If-Else nodes.

Node Configuration

Basic Properties

{
"id": "switch-1",
"type": "control",
"config": {
"identifier": "switch",
"title": "Condition Router",
"description": "Multi-branch conditional processing"
}
}

Input Parameters

ParameterTypeRequiredDescription
dataObject/AnyInput data used for expression evaluation
expressionStringCondition expression, supports JavaScript expressions
casesArrayArray of branch cases, each containing a value and port
defaultCaseObjectDefault branch, executed when no case matches

Branch Case Structure

{
"cases": [
{
"value": "A", // Match value
"port": "gradeA" // Output port name
},
{
"value": ["VIP", "GOLD"], // Supports array matching
"port": "premium"
}
],
"defaultCase": {
"port": "default" // Default port
}
}

Expression Evaluation

The Switch node uses ConditionEvaluator to evaluate expressions, supporting:

Basic Expressions

// Simple property access
"data.score"
"data.user.level"

// Numeric comparison
"data.score >= 90"
"data.amount > 1000"

// String matching
"data.status === 'active'"
"data.type"

Compound Expressions

// Ternary operator
"data.score >= 90 ? 'A' : data.score >= 80 ? 'B' : 'C'"

// Logical operations
"data.amount > 1000 && data.customer.vip"
"data.type === 'URGENT' || data.priority === 'HIGH'"

// Complex condition combinations
"data.amount > 1000 && data.customer.vip ? 'VIP_LARGE' : data.type === 'URGENT' ? 'URGENT' : 'NORMAL'"

Matching Modes

The Switch node supports three matching modes:

1. Exact Match

{
"cases": [
{ "value": "A", "port": "excellent" },
{ "value": "B", "port": "good" },
{ "value": 90, "port": "highScore" }
]
}

2. String Match (Case-Insensitive)

{
"cases": [
{ "value": "pending", "port": "waitingHandler" },
{ "value": "RUNNING", "port": "activeHandler" }
]
}
// Both "PENDING" and "pending" will match waitingHandler

3. Array Inclusion Match

{
"cases": [
{ "value": ["DIAMOND", "PLATINUM", "GOLD"], "port": "premium" },
{ "value": ["SILVER", "BRONZE"], "port": "standard" }
]
}
// When data.level is "GOLD", it will match the premium port

Context Management

The Switch node saves execution results in the context:

// Context structure
context['switch-1'] = {
[matchedPort]: inputData, // Data corresponding to the matched port
'_switch_result': {
expression: "data.score >= 90 ? 'A' : 'B'",
expressionValue: "A", // Expression evaluation result
matchedCase: "A", // The matched case's value (a scalar, not the case object)
matchedPort: "gradeA" // Matched port name
}
}
note

_switch_result.matchedCase is the value of the matched case (for example "A"), not the whole { value, port } case object. Use matchedPort for the port that was selected. When no case matches and there is no defaultCase, the node produces no result for that run.

Usage Examples

Example 1: Grade Evaluation System

{
"nodes": {
"score-processor": {
"type": "operator",
"config": {
"operator": "jsexecutor",
"code": "const student = { name: 'Alice', score: 95, subject: 'Math' }; return student;"
}
},
"grade-switch": {
"type": "control",
"config": {
"identifier": "switch"
}
},
"excellent-handler": {
"type": "operator",
"config": {
"operator": "jsexecutor",
"code": "return { result: 'excellent', message: `Congratulations ${parameters.name}! Excellent score (${parameters.score} points)`, grade: 'A' };"
}
},
"good-handler": {
"type": "operator",
"config": {
"operator": "jsexecutor",
"code": "return { result: 'good', message: `${parameters.name} scored well (${parameters.score} points)`, grade: 'B' };"
}
},
"pass-handler": {
"type": "operator",
"config": {
"operator": "jsexecutor",
"code": "return { result: 'pass', message: `${parameters.name} passed (${parameters.score} points)`, grade: 'C' };"
}
},
"fail-handler": {
"type": "operator",
"config": {
"operator": "jsexecutor",
"code": "return { result: 'fail', message: `${parameters.name} did not pass (${parameters.score} points)`, grade: 'D' };"
}
}
},
"edges": [
{
"source": "score-processor:result",
"target": "grade-switch:data"
},
{
"source": "grade-switch:gradeA",
"target": "excellent-handler:parameters"
},
{
"source": "grade-switch:gradeB",
"target": "good-handler:parameters"
},
{
"source": "grade-switch:gradeC",
"target": "pass-handler:parameters"
},
{
"source": "grade-switch:default",
"target": "fail-handler:parameters"
}
],
"inputs": {
"grade-switch": {
"expression": "data.score >= 90 ? 'A' : data.score >= 80 ? 'B' : data.score >= 60 ? 'C' : 'D'",
"cases": [
{ "value": "A", "port": "gradeA" },
{ "value": "B", "port": "gradeB" },
{ "value": "C", "port": "gradeC" }
],
"defaultCase": { "port": "default" }
}
}
}

Example 2: Customer Tier Service Routing

{
"nodes": {
"customer-analyzer": {
"type": "operator",
"config": {
"operator": "jsexecutor",
"code": "const customer = { id: 'C001', name: 'John', level: 'DIAMOND', region: 'CN', spending: 50000 }; return customer;"
}
},
"service-router": {
"type": "control",
"config": {
"identifier": "switch"
}
},
"vip-service": {
"type": "operator",
"config": {
"operator": "jsexecutor",
"code": "return { type: 'premium_service', message: `Dear ${parameters.name}, you enjoy exclusive VIP service`, level: parameters.level, benefits: ['Dedicated support', 'Priority handling', 'Special discounts'] };"
}
},
"standard-service": {
"type": "operator",
"config": {
"operator": "jsexecutor",
"code": "return { type: 'standard_service', message: `Dear ${parameters.name}, thank you for your support`, level: parameters.level, benefits: ['Standard service'] };"
}
},
"basic-service": {
"type": "operator",
"config": {
"operator": "jsexecutor",
"code": "return { type: 'basic_service', message: `${parameters.name}, welcome to our service` };"
}
}
},
"edges": [
{
"source": "customer-analyzer:result",
"target": "service-router:data"
},
{
"source": "service-router:premium",
"target": "vip-service:parameters"
},
{
"source": "service-router:standard",
"target": "standard-service:parameters"
},
{
"source": "service-router:default",
"target": "basic-service:parameters"
}
],
"inputs": {
"service-router": {
"expression": "data.level",
"cases": [
{ "value": ["DIAMOND", "PLATINUM", "GOLD"], "port": "premium" },
{ "value": ["SILVER", "BRONZE"], "port": "standard" }
],
"defaultCase": { "port": "default" }
}
}
}

Example 3: Order Processing Dispatch

{
"nodes": {
"order-classifier": {
"type": "operator",
"config": {
"operator": "jsexecutor",
"code": "const order = { id: 'O001', amount: 1500, type: 'URGENT', priority: 'HIGH', customer: { vip: true } }; return order;"
}
},
"order-switch": {
"type": "control",
"config": {
"identifier": "switch"
}
},
"vip-order-handler": {
"type": "operator",
"config": {
"operator": "jsexecutor",
"code": "return { type: 'vip_large_order', message: `VIP large order ${parameters.id} prioritized`, amount: parameters.amount, fast_track: true };"
}
},
"urgent-order-handler": {
"type": "operator",
"config": {
"operator": "jsexecutor",
"code": "return { type: 'urgent_order', message: `Urgent order ${parameters.id} expedited`, priority: parameters.priority };"
}
},
"normal-order-handler": {
"type": "operator",
"config": {
"operator": "jsexecutor",
"code": "return { type: 'normal_order', message: `Order ${parameters.id} processed normally` };"
}
}
},
"edges": [
{
"source": "order-classifier:result",
"target": "order-switch:data"
},
{
"source": "order-switch:vipLarge",
"target": "vip-order-handler:parameters"
},
{
"source": "order-switch:urgent",
"target": "urgent-order-handler:parameters"
},
{
"source": "order-switch:normal",
"target": "normal-order-handler:parameters"
}
],
"inputs": {
"order-switch": {
"expression": "data.amount > 1000 && data.customer.vip ? 'VIP_LARGE' : data.type === 'URGENT' ? 'URGENT' : 'NORMAL'",
"cases": [
{ "value": "VIP_LARGE", "port": "vipLarge" },
{ "value": "URGENT", "port": "urgent" },
{ "value": "NORMAL", "port": "normal" }
]
}
}
}

Example 4: State Machine Processing

{
"nodes": {
"task-monitor": {
"type": "operator",
"config": {
"operator": "jsexecutor",
"code": "const task = { id: 'task-001', status: 'pending', priority: 'high' }; return task;"
}
},
"status-router": {
"type": "control",
"config": {
"identifier": "switch"
}
},
"pending-handler": {
"type": "operator",
"config": {
"operator": "jsexecutor",
"code": "return { result: 'pending_handled', message: `Task ${parameters.id} awaiting processing`, nextStatus: 'running' };"
}
},
"running-handler": {
"type": "operator",
"config": {
"operator": "jsexecutor",
"code": "return { result: 'running_handled', message: `Task ${parameters.id} is executing`, progress: 50 };"
}
},
"completed-handler": {
"type": "operator",
"config": {
"operator": "jsexecutor",
"code": "return { result: 'completed_handled', message: `Task ${parameters.id} has been completed`, completedAt: new Date().toISOString() };"
}
},
"error-handler": {
"type": "operator",
"config": {
"operator": "jsexecutor",
"code": "return { result: 'error_handled', message: `Task ${parameters.id} has an abnormal status`, needsReview: true };"
}
}
},
"edges": [
{
"source": "task-monitor:result",
"target": "status-router:data"
},
{
"source": "status-router:pending",
"target": "pending-handler:parameters"
},
{
"source": "status-router:running",
"target": "running-handler:parameters"
},
{
"source": "status-router:completed",
"target": "completed-handler:parameters"
},
{
"source": "status-router:default",
"target": "error-handler:parameters"
}
],
"inputs": {
"status-router": {
"expression": "data.status",
"cases": [
{ "value": "pending", "port": "pending" },
{ "value": "running", "port": "running" },
{ "value": "completed", "port": "completed" }
],
"defaultCase": { "port": "default" }
}
}
}

Error Handling

Fallback Mechanism on Expression Evaluation Failure

// 1. First attempts to evaluate the expression using ConditionEvaluator
// 2. If that fails, attempts to use the expression as a property name to retrieve a value from inputData
// 3. Finally falls back to using the expression itself as the value

Handling When No Branch Matches

// If no case matches and there is no defaultCase, returns null and no branch is executed
if (!matchedPort) {
console.log('No matching branch to execute');
return null;
}

Best Practices

1. Expression Design Recommendations

// ✅ Recommended: Clear logical structure
"data.score >= 90 ? 'A' : data.score >= 80 ? 'B' : 'C'"

// ✅ Recommended: Combining multiple conditions
"data.amount > 1000 && data.customer.vip"

// ✅ Recommended: Simple property access
"data.status"
"data.user.level"

2. Branch Design Patterns

// Tiered processing pattern
{
"cases": [
{ "value": ["LEVEL_1", "LEVEL_2"], "port": "highLevel" },
{ "value": ["LEVEL_3", "LEVEL_4"], "port": "midLevel" },
{ "value": ["LEVEL_5"], "port": "lowLevel" }
],
"defaultCase": { "port": "defaultLevel" }
}

// State machine pattern
{
"cases": [
{ "value": "INIT", "port": "initialize" },
{ "value": "PROCESSING", "port": "process" },
{ "value": "COMPLETE", "port": "finalize" }
],
"defaultCase": { "port": "error" }
}

3. Debugging Tips

// Access Switch decision information within the sub-workflow
const processor = `
const switchResult = context['switch-1']._switch_result;
console.log('Switch decision info:', {
expression: switchResult.expression,
value: switchResult.expressionValue,
matchedCase: switchResult.matchedCase,
port: switchResult.matchedPort
});

return {
processed: parameters,
decision: switchResult
};
`;

4. Performance Optimization

// Place the most frequently matched cases first
{
"cases": [
{ "value": "COMMON_STATUS", "port": "commonHandler" }, // 90% of cases
{ "value": "RARE_STATUS", "port": "rareHandler" }, // 10% of cases
{ "value": "VERY_RARE", "port": "specialHandler" } // 1% of cases
]
}
Important Note

The Switch node is a pure control node. After selecting an execution path based on the condition, it halts subsequent execution of the main workflow. All business logic is completed within the selected branch sub-workflow.