🔗 Node-RED Joinノード ガイド

このガイドでは、Node-REDのJoinノードについて、初心者の方でも理解できるように詳しく説明します。

📚 1. Joinノードとは?

🤔 「Join」って何?

Joinノードは、複数のメッセージを1つのメッセージに結合するノードです。 日常生活で例えると、パズルのピースを組み合わせて1枚の絵を完成させるようなものです。

🧩 パズルの組み立てに例えると:

📦 基本的な動作

Split →→→ 処理 →→→ Join Debug

Joinノードは、複数のメッセージを受け取り、それらを1つのメッセージにまとめて出力します。 Splitノードで分割したメッセージを再結合したり、異なるソースからのデータを統合したりできます。

入力(3メッセージ)
"Apple" → "Orange" → "Banana"
↓ Join ↓
出力(1メッセージ)
["Apple", "Orange", "Banana"]

⚙️ 2. Joinノードの3つのモード

自動 Automatic

Splitノードと連携して自動的に再結合

msg.partsを使用して 元の形式に復元

手動 Manual

メッセージ数やタイムアウトを指定して結合

文字列/配列/オブジェクト 形式を選択可能

Reduce Reduce

JSONata式でメッセージを集計・変換

合計、平均、配列変換 など柔軟な処理

📋 設定項目一覧

設定項目 説明 使用モード
動作 自動/手動/Reduceから選択 全モード共通
出力 文字列/配列/オブジェクト/マージ 手動モード
対象プロパティ 結合対象のメッセージプロパティ 手動/Reduce
連結文字 文字列結合時の区切り文字 手動(文字列)
使用する値 オブジェクト結合時のキーとなるプロパティ 手動(オブジェクト)
合計値 結合するメッセージの数 手動/Reduce
タイムアウト 指定秒数後に強制結合 手動/Reduce
既存のmsg.partsプロパティを使用 既存のmsg.parts情報を使用してシーケンスを認識 手動モード
蓄積モード 各メッセージ受信時に出力を生成 手動モード
Reduce式 JSONata式で集計処理を定義 Reduceモード
初期値 Reduce処理の初期値 Reduceモード
最終処理 結合完了後の変換式 Reduceモード
逆順で評価 メッセージを逆順で処理 Reduceモード

🎯 msg.partsオブジェクト

Splitノードが付与するmsg.parts情報を使って、Joinノードは自動的にメッセージを再結合します。

msg.parts = { id: "abc123", // 分割グループの識別子 index: 0, // このメッセージの順番(0から) count: 3, // 総分割数 type: "array", // 元のデータ型 key: "Apple" // オブジェクト分割時のキー名 }

🎯 3. 実用的な使用パターン

📥 サンプルフローのインポート方法:

  1. 下のサンプルフローJSONをコピー
  2. Node-REDエディタで メニュー → 読み込み を選択
  3. JSONをペーストして「読み込み」をクリック

このサンプルフローには、以下で説明する全パターンの実例が含まれています。

📋 サンプルフロー(クリックで展開)

参照元:NodeREDエディター内サンプルフロー

[{"id":"4775a535c935964a","type":"tab","label":"join","disabled":false,"info":"","env":[]},{"id":"afe74de5.58488","type":"comment","z":"4775a535c935964a","name":"Example: Automatic Mode","info":"Join node can be used to join payload of messages into single message payload. In automatic mode, it uses `parts` property to decide sequence of messages to join.","x":230,"y":120,"wires":[]},{"id":"d0c8c78e.1281e8","type":"inject","z":"4775a535c935964a","name":"","repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":280,"y":240,"wires":[["c59ac70d.bb9c58"]]},{"id":"c59ac70d.bb9c58","type":"template","z":"4775a535c935964a","name":"data","field":"payload","fieldType":"msg","format":"text","syntax":"plain","template":"Apple\nOrange\nBanana\nKiwi","output":"str","x":430,"y":240,"wires":[["21829618.af5b0a"]]},{"id":"d6c9fe0.d34ad","type":"join","z":"4775a535c935964a","name":"","mode":"auto","build":"string","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","useparts":false,"accumulate":"false","timeout":"","count":"","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":710,"y":240,"wires":[["8defdbb8.aa9c08"]]},{"id":"21829618.af5b0a","type":"split","z":"4775a535c935964a","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":570,"y":240,"wires":[["d6c9fe0.d34ad"]]},{"id":"8defdbb8.aa9c08","type":"debug","z":"4775a535c935964a","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":870,"y":240,"wires":[]},{"id":"7bf47d74.b233b4","type":"comment","z":"4775a535c935964a","name":"Example: Manual Mode","info":"","x":260,"y":360,"wires":[]},{"id":"7f104f68.aecff","type":"inject","z":"4775a535c935964a","name":"","repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":280,"y":480,"wires":[["ed0381d3.e25fc"]]},{"id":"ed0381d3.e25fc","type":"template","z":"4775a535c935964a","name":"data","field":"payload","fieldType":"msg","format":"text","syntax":"plain","template":"Apple\nOrange\nBanana\nKiwi","output":"str","x":430,"y":480,"wires":[["12b322b7.78f48d"]]},{"id":"a4f5546e.28b268","type":"join","z":"4775a535c935964a","name":"","mode":"custom","build":"string","property":"payload","propertyType":"msg","key":"topic","joiner":",","joinerType":"str","useparts":false,"accumulate":"false","timeout":"","count":"2","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":710,"y":480,"wires":[["e3e636ab.5c5068"]]},{"id":"12b322b7.78f48d","type":"split","z":"4775a535c935964a","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":570,"y":480,"wires":[["a4f5546e.28b268"]]},{"id":"e3e636ab.5c5068","type":"debug","z":"4775a535c935964a","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":870,"y":480,"wires":[]},{"id":"6a104137.204b3","type":"comment","z":"4775a535c935964a","name":"Join two consecutive messages (count=2)","info":"","x":350,"y":420,"wires":[]},{"id":"9279b993.ba7ed8","type":"comment","z":"4775a535c935964a","name":"Example: Reduce Mode","info":"","x":260,"y":1220,"wires":[]},{"id":"e415861b.d82e38","type":"inject","z":"4775a535c935964a","name":"","repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":280,"y":1340,"wires":[["7c8a4816.500ed8"]]},{"id":"7c8a4816.500ed8","type":"template","z":"4775a535c935964a","name":"data","field":"payload","fieldType":"msg","format":"json","syntax":"plain","template":"[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]","output":"json","x":430,"y":1340,"wires":[["7f64da1f.611014"]]},{"id":"5fde318f.23897","type":"join","z":"4775a535c935964a","name":"","mode":"reduce","build":"string","property":"payload","propertyType":"msg","key":"topic","joiner":",","joinerType":"str","useparts":false,"accumulate":"false","timeout":"","count":"","reduceRight":false,"reduceExp":"$A+payload","reduceInit":"0","reduceInitType":"num","reduceFixup":"$A/$N","x":710,"y":1340,"wires":[["50977eaf.0490b"]]},{"id":"50977eaf.0490b","type":"debug","z":"4775a535c935964a","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":870,"y":1340,"wires":[]},{"id":"57bce4bf.38d02c","type":"comment","z":"4775a535c935964a","name":"Calculate average using Reduce","info":"","x":320,"y":1280,"wires":[]},{"id":"7f64da1f.611014","type":"split","z":"4775a535c935964a","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":570,"y":1340,"wires":[["5fde318f.23897"]]},{"id":"array_comment","type":"comment","z":"4775a535c935964a","name":"Example: Array Mode (パターン4)","info":"配列形式でメッセージを結合","x":280,"y":780,"wires":[]},{"id":"p4_inject","type":"inject","z":"4775a535c935964a","name":"実行","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"Apple,Orange,Banana,Kiwi","payloadType":"str","x":270,"y":900,"wires":[["p4_split"]]},{"id":"p4_split","type":"split","z":"4775a535c935964a","name":"分割","splt":",","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":410,"y":900,"wires":[["p4_join"]]},{"id":"p4_join","type":"join","z":"4775a535c935964a","name":"配列結合","mode":"custom","build":"array","property":"payload","propertyType":"msg","key":"topic","joiner":",","joinerType":"str","useparts":false,"accumulate":"false","timeout":"","count":"2","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":550,"y":900,"wires":[["p4_debug"]]},{"id":"p4_debug","type":"debug","z":"4775a535c935964a","name":"結果","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":710,"y":900,"wires":[]},{"id":"p4_note","type":"comment","z":"4775a535c935964a","name":"Join 2 messages into array","info":"","x":300,"y":840,"wires":[]},{"id":"timeout_comment","type":"comment","z":"4775a535c935964a","name":"Example: Timeout Mode (パターン3)","info":"タイムアウトを使用してメッセージを結合","x":300,"y":580,"wires":[]},{"id":"p3_inject","type":"inject","z":"4775a535c935964a","name":"実行","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"Apple,Orange,Banana,Kiwi","payloadType":"str","x":280,"y":700,"wires":[["p3_split"]]},{"id":"p3_split","type":"split","z":"4775a535c935964a","name":"分割","splt":",","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":420,"y":700,"wires":[["p3_delay"]]},{"id":"p3_delay","type":"delay","z":"4775a535c935964a","name":"1秒/msg","pauseType":"rate","timeout":"1","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":550,"y":700,"wires":[["p3_join"]]},{"id":"p3_join","type":"join","z":"4775a535c935964a","name":"2秒タイムアウト","mode":"custom","build":"string","property":"payload","propertyType":"msg","key":"topic","joiner":",","joinerType":"str","useparts":false,"accumulate":"false","timeout":"2","count":"","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":720,"y":700,"wires":[["p3_debug"]]},{"id":"p3_debug","type":"debug","z":"4775a535c935964a","name":"結果","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":880,"y":700,"wires":[]},{"id":"p3_note","type":"comment","z":"4775a535c935964a","name":"Join with 2 second timeout","info":"","x":310,"y":640,"wires":[]},{"id":"object_comment","type":"comment","z":"4775a535c935964a","name":"Example: Object Mode (パターン5)","info":"オブジェクト形式でメッセージを結合","x":290,"y":1000,"wires":[]},{"id":"p5_inject","type":"inject","z":"4775a535c935964a","name":"実行","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":270,"y":1120,"wires":[["p5_template"]]},{"id":"p5_template","type":"template","z":"4775a535c935964a","name":"商品データ","field":"payload","fieldType":"msg","format":"json","syntax":"plain","template":"[{\"name\": \"Apple\", \"price\": 100}, {\"name\": \"Orange\", \"price\": 80}]","output":"json","x":420,"y":1120,"wires":[["p5_split"]]},{"id":"p5_split","type":"split","z":"4775a535c935964a","name":"分割","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":570,"y":1120,"wires":[["p5_change"]]},{"id":"p5_change","type":"change","z":"4775a535c935964a","name":"topic/payload設定","rules":[{"t":"set","p":"topic","pt":"msg","to":"payload.name","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"payload.price","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":730,"y":1120,"wires":[["p5_join"]]},{"id":"p5_join","type":"join","z":"4775a535c935964a","name":"オブジェクト結合","mode":"custom","build":"object","property":"payload","propertyType":"msg","key":"topic","joiner":",","joinerType":"str","useparts":false,"accumulate":"false","timeout":"","count":"2","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":940,"y":1120,"wires":[["p5_debug"]]},{"id":"p5_debug","type":"debug","z":"4775a535c935964a","name":"結果","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":1110,"y":1120,"wires":[]},{"id":"p5_note","type":"comment","z":"4775a535c935964a","name":"Create key/value object from array","info":"","x":330,"y":1060,"wires":[]}]

パターン1: 自動モード(Splitと連携)

用途: Splitノードで分割したメッセージを元の形式に再結合

Inject Template Split →→→ Join
(自動)
Debug

📌 動作の流れ:

  1. "Apple\nOrange\nBanana\nKiwi" をSplitで分割
  2. 4つの個別メッセージが順番に送信される
  3. JoinがSplitが付与したmsg.partsを使って自動再結合
  4. 元の形式に戻る

設定例:

モード: 自動 入力: "Apple" → "Orange" → "Banana" → "Kiwi" (4メッセージ) 出力: "Apple\nOrange\nBanana\nKiwi" (1メッセージ)

パターン2: 手動モード(メッセージ数指定)

用途: 指定した数のメッセージを結合

Inject Split →→→ Join
(count=2)
→→ Debug

📌 動作の流れ:

  1. 4つのメッセージ "Apple", "Orange", "Banana", "Kiwi" を受信
  2. 2つずつ結合して出力
  3. "Apple,Orange" → "Banana,Kiwi" の2つのメッセージが出力

設定例:

モード: 手動 結合形式: 文字列 結合文字: , メッセージ数: 2 入力: "Apple" → "Orange" → "Banana" → "Kiwi" 出力: "Apple,Orange" → "Banana,Kiwi"

パターン3: 手動モード(タイムアウト指定)

用途: 指定時間内のメッセージを結合

Inject Split →→→ Delay
(1秒/msg)
→→→ Join
(2秒)
Debug

📌 動作の流れ:

  1. メッセージを1秒間隔で受信
  2. 2秒のタイムアウトで結合
  3. 2秒以内に届いたメッセージをまとめて出力

設定例:

モード: 手動 結合形式: 文字列 結合文字: , タイムアウト: 2秒 入力: 1秒間隔で "Apple" → "Orange" → "Banana" → "Kiwi" 出力: "Apple,Orange" → "Banana,Kiwi" (約2秒ごとに出力)

パターン4: 配列形式での結合

用途: 複数のメッセージを配列にまとめる

Inject Split →→→ Join
(配列)
Debug

設定例:

モード: 手動 結合形式: 配列 メッセージ数: 2 入力: "Apple" → "Orange" → "Banana" → "Kiwi" 出力: ["Apple", "Orange"] → ["Banana", "Kiwi"]

パターン5: オブジェクト形式での結合

用途: msg.topicをキーとしたオブジェクトを作成

Inject Template
(配列)
Split →→→ Change
(topic設定)
→→→ Join
(オブジェクト)
Debug

📌 動作の流れ:

  1. Templateノードで配列データを作成
  2. Splitノードで配列を個別のメッセージに分割
  3. Changeノードでmsg.topicに商品名、msg.payloadに価格を設定
  4. Joinでtopicをキー、payloadを値としたオブジェクトに結合

設定例:

モード: 手動 結合形式: キー/値 オブジェクト メッセージ数: 2 入力: {topic: "Apple", payload: 100} → {topic: "Orange", payload: 80} 出力: {"Apple": 100, "Orange": 80}

パターン6: Reduceモード(平均値計算)

用途: メッセージシーケンスを集計して単一の値に

Inject Template
([1-10])
Split →→→ Join
(Reduce)
Debug

📌 Reduceの特殊変数:

設定例:

モード: Reduce Reduce式: $A + payload 初期値: 0 最終処理: $A / $N 入力: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 処理: (1+2+3+4+5+6+7+8+9+10) / 10 出力: 5.5 (平均値)

🏋️ 4. 実践演習

演習1: 文字列の結合初級

📝 課題:

カンマ区切りの都市名を分割し、「/」で再結合してください。

🎯 要求仕様:

📊 期待される動作:

✅ 成功の条件:

💡 ヒント

Injectノードの設定:

  • payload: 文字列型で "東京,大阪,名古屋,福岡"

Splitノードの設定:

  • 分割方法: 文字列
  • 区切り文字: ,

Joinノードの設定:

  • モード: 手動
  • 結合形式: 文字列
  • 結合文字: /
  • メッセージ数: 4(またはmsg.parts.countを使う自動モードでも可)
✅ 解答例フロー
[ { "id": "ex1_inject", "type": "inject", "name": "都市名入力", "props": [{"p": "payload"}], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "東京,大阪,名古屋,福岡", "payloadType": "str", "x": 130, "y": 100, "wires": [["ex1_split"]] }, { "id": "ex1_split", "type": "split", "name": "カンマ分割", "splt": ",", "spltType": "str", "arraySplt": 1, "arraySpltType": "len", "stream": false, "addname": "", "property": "payload", "x": 300, "y": 100, "wires": [["ex1_join"]] }, { "id": "ex1_join", "type": "join", "name": "スラッシュ結合", "mode": "custom", "build": "string", "property": "payload", "propertyType": "msg", "key": "topic", "joiner": "/", "joinerType": "str", "useparts": false, "accumulate": "false", "timeout": "", "count": "4", "reduceRight": false, "reduceExp": "", "reduceInit": "", "reduceInitType": "", "reduceFixup": "", "x": 480, "y": 100, "wires": [["ex1_debug"]] }, { "id": "ex1_debug", "type": "debug", "name": "結果", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "payload", "targetType": "msg", "x": 650, "y": 100, "wires": [] } ]

演習2: 配列への変換中級

📝 課題:

改行区切りのテキストを分割し、配列に変換してください。

🎯 要求仕様:

📊 期待される動作:

✅ 成功の条件:

💡 ヒント

Templateノードの設定:

  • テンプレート: りんご、みかん、バナナを改行で区切って入力
  • 出力: 文字列

Splitノードの設定:

  • 分割方法: 文字列
  • 区切り文字: \n(改行)

Joinノードの設定:

  • モード: 手動
  • 結合形式: 配列
  • メッセージ数: 3
✅ 解答例フロー
[ { "id": "ex2_inject", "type": "inject", "name": "実行", "props": [{"p": "payload"}], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 110, "y": 100, "wires": [["ex2_template"]] }, { "id": "ex2_template", "type": "template", "name": "フルーツリスト", "field": "payload", "fieldType": "msg", "format": "text", "syntax": "plain", "template": "りんご\nみかん\nバナナ", "output": "str", "x": 260, "y": 100, "wires": [["ex2_split"]] }, { "id": "ex2_split", "type": "split", "name": "改行分割", "splt": "\\n", "spltType": "str", "arraySplt": 1, "arraySpltType": "len", "stream": false, "addname": "", "property": "payload", "x": 420, "y": 100, "wires": [["ex2_join"]] }, { "id": "ex2_join", "type": "join", "name": "配列に結合", "mode": "custom", "build": "array", "property": "payload", "propertyType": "msg", "key": "topic", "joiner": "\\n", "joinerType": "str", "useparts": false, "accumulate": "false", "timeout": "", "count": "3", "reduceRight": false, "reduceExp": "", "reduceInit": "", "reduceInitType": "", "reduceFixup": "", "x": 580, "y": 100, "wires": [["ex2_debug"]] }, { "id": "ex2_debug", "type": "debug", "name": "結果", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "payload", "targetType": "msg", "x": 730, "y": 100, "wires": [] } ]

演習3: オブジェクトの作成中級

📝 課題:

商品と価格のペアからオブジェクトを作成してください。

🎯 要求仕様:

📊 期待される動作:

✅ 成功の条件:

💡 ヒント

Templateノードの設定:

  • 形式: JSON
  • テンプレート: [{"name": "Apple", "price": 150}, {"name": "Orange", "price": 80}]
  • 出力形式: パース済みJSONオブジェクト(配列として出力される)

Splitノードの設定:

  • 配列は自動認識されるのでデフォルト設定でOK

Changeノードの設定:

  • msg.topic を msg.payload.name に設定
  • msg.payload を msg.payload.price に設定

Joinノードの設定:

  • モード: 手動
  • 結合形式: キー/値 オブジェクト
  • メッセージ数: 2
✅ 解答例フロー
[ { "id": "ex3_inject", "type": "inject", "name": "実行", "props": [{"p": "payload"}], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 110, "y": 100, "wires": [["ex3_template"]] }, { "id": "ex3_template", "type": "template", "name": "商品データ", "field": "payload", "fieldType": "msg", "format": "json", "syntax": "plain", "template": "[{\"name\": \"Apple\", \"price\": 150}, {\"name\": \"Orange\", \"price\": 80}]", "output": "json", "x": 260, "y": 100, "wires": [["ex3_split"]] }, { "id": "ex3_split", "type": "split", "name": "分割", "splt": "\\n", "spltType": "str", "arraySplt": 1, "arraySpltType": "len", "stream": false, "addname": "", "property": "payload", "x": 390, "y": 100, "wires": [["ex3_change"]] }, { "id": "ex3_change", "type": "change", "name": "topic/payload設定", "rules": [ { "t": "set", "p": "topic", "pt": "msg", "to": "payload.name", "tot": "msg" }, { "t": "set", "p": "payload", "pt": "msg", "to": "payload.price", "tot": "msg" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 530, "y": 100, "wires": [["ex3_join"]] }, { "id": "ex3_join", "type": "join", "name": "オブジェクト結合", "mode": "custom", "build": "object", "property": "payload", "propertyType": "msg", "key": "topic", "joiner": ",", "joinerType": "str", "useparts": false, "accumulate": "false", "timeout": "", "count": "2", "reduceRight": false, "reduceExp": "", "reduceInit": "", "reduceInitType": "", "reduceFixup": "", "x": 700, "y": 100, "wires": [["ex3_debug"]] }, { "id": "ex3_debug", "type": "debug", "name": "結果", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "payload", "targetType": "msg", "x": 850, "y": 100, "wires": [] } ]

演習4: Reduceで合計値を計算上級

📝 課題:

センサーデータの合計値を計算してください。

🎯 要求仕様:

📊 期待される動作:

✅ 成功の条件:

💡 ヒント

Templateノードの設定:

  • 形式: JSON
  • テンプレート: [10, 25, 30, 15, 20]
  • 出力: JSONオブジェクト

Splitノードの設定:

  • 配列は自動認識されるのでデフォルト設定でOK

Joinノードの設定:

  • モード: Reduce
  • Reduce式: $A + payload
  • 初期値: 0(数値型)
  • 最終処理: 空(合計のみなので不要)
✅ 解答例フロー
[ { "id": "ex4_inject", "type": "inject", "name": "実行", "props": [{"p": "payload"}], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 110, "y": 100, "wires": [["ex4_template"]] }, { "id": "ex4_template", "type": "template", "name": "センサーデータ", "field": "payload", "fieldType": "msg", "format": "json", "syntax": "plain", "template": "[10, 25, 30, 15, 20]", "output": "json", "x": 270, "y": 100, "wires": [["ex4_split"]] }, { "id": "ex4_split", "type": "split", "name": "分割", "splt": "\\n", "spltType": "str", "arraySplt": 1, "arraySpltType": "len", "stream": false, "addname": "", "property": "payload", "x": 410, "y": 100, "wires": [["ex4_join"]] }, { "id": "ex4_join", "type": "join", "name": "合計計算", "mode": "reduce", "build": "string", "property": "payload", "propertyType": "msg", "key": "topic", "joiner": ",", "joinerType": "str", "useparts": false, "accumulate": "false", "timeout": "", "count": "", "reduceRight": false, "reduceExp": "$A + payload", "reduceInit": "0", "reduceInitType": "num", "reduceFixup": "", "x": 550, "y": 100, "wires": [["ex4_debug"]] }, { "id": "ex4_debug", "type": "debug", "name": "結果", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "payload", "targetType": "msg", "x": 690, "y": 100, "wires": [] } ]

🎓 5. まとめ

Joinノードの重要ポイント

⚠️ よくある間違い

📚 次のステップ

Joinノードをマスターしたら、以下のノードも学んでみましょう:

🔧 6. トラブルシューティング

よくある問題と解決方法

問題 原因 解決方法
結合されない メッセージ数が不足 count数の確認、またはタイムアウトを設定
自動モードが動作しない msg.partsが欠落 途中のノードでmsg.partsを保持する設定を確認
順序がバラバラ 非同期処理による順序変更 msg.parts.indexを使用、またはSortノードで並べ替え
Reduce結果が期待と違う JSONata式のエラー 式を単純化して動作確認、$A/$I/$Nの使い方を確認
タイムアウトで途中出力 メッセージ到着が遅い タイムアウト値を調整、またはcount指定に変更
異なるグループが混在 msg.parts.idが異なる Splitノードの設定を確認、または手動モードを使用

💡 7. 実務での活用例

ケース1: 複数センサーデータの統合

温度センサー + 湿度センサー + 気圧センサー ↓ Joinノード(オブジェクト形式) ↓ { "temperature": 25.5, "humidity": 60, "pressure": 1013 } ↓ データベース保存 or ダッシュボード表示

ケース2: API並列呼び出しの結果統合

ユーザー情報API → 注文履歴API → Join(マージ)→ 統合レスポンス 在庫情報API → 出力: { "user": {...}, "orders": [...], "inventory": {...} }

ケース3: バッチ処理の集計

1時間分のログデータ(100件) ↓ Split(行ごとに分割) ↓ フィルタリング(エラーログのみ) ↓ Join(Reduce: $A + 1, 初期値: 0) ↓ エラー件数: 15

ケース4: IoTデバイスからのデータ集約

工場の10台のデバイス ↓ 各デバイスから生産数を受信(タイムアウト: 5秒) ↓ Join(配列形式) ↓ [120, 115, 130, 125, 110, 128, 122, 118, 132, 126] ↓ 合計・平均計算 → レポート生成

📗 8. Reduceモード詳細

JSONata特殊変数

変数 説明 使用例
$A 累積値(アキュムレータ) $A + payload(合計)
$I 現在のインデックス(0から) インデックス付きオブジェクト作成
$N メッセージ総数 $A / $N(平均)
payload 現在のメッセージのpayload 各メッセージの値を参照

Reduce式の例

// 合計 $A + payload // 最大値 payload > $A ? payload : $A // 配列に追加 $append($A, [payload]) // オブジェクト配列に変換 $append($A, [{"value": payload, "index": $I}]) // 条件付き加算 payload > 10 ? $A + payload : $A

🔗 9. 追加リソース


このガイドが役に立ちましたら、実際のプロジェクトで練習してみてください!
Joinノードはデータ統合・集計の強力なツールです。

参照元:NodeREDエディター内サンプルフロー

🏠