This workflow follows the Execute Workflow Trigger → Form Trigger recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"name": "major",
"nodes": [
{
"parameters": {
"operation": "executeQuery",
"query": "Select cdm_json\nFROM cdm_messages\nwhere\n least(upper(trim(pair_a)), upper(trim(pair_b))) =\n least(upper(trim($1)), upper(trim($2)))\nand\n greatest(upper(trim(pair_a)), upper(trim(pair_b))) =\n greatest(upper(trim($1)), upper(trim($2)))\norder by coalesce(nullif(creation_date,'')::timestamptz, created_at) desc",
"options": {
"queryReplacement": "=$1 = {{ $json.pair_a }} \n$2 = {{ $json.pair_b }}"
}
},
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.6,
"position": [
0,
0
],
"id": "59c34bc8-b13a-498e-a6d2-f53260d4e157",
"name": "Execute a SQL query2",
"executeOnce": true,
"alwaysOutputData": true,
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "// n8n Code node (JavaScript)\n// Input: item.json = CDM OR item.json.cdm_json = CDM (object or JSON string)\n// Output: json.source_family + json.maneuverable + json.object_type_pair + json.dominant_miss_axis_ric\n\nfunction asObject(maybeJson) {\n if (maybeJson == null) return null;\n if (typeof maybeJson === \"object\") return maybeJson;\n if (typeof maybeJson === \"string\") {\n try { return JSON.parse(maybeJson); } catch { return null; }\n }\n return null;\n}\n\nfunction pickFirst(obj, keys) {\n for (const k of keys) {\n if (obj && obj[k] != null && obj[k] !== \"\") return obj[k];\n }\n return null;\n}\n\nfunction uniqNonEmpty(arr) {\n return [...new Set(arr.filter(v => v != null && v !== \"\"))];\n}\n\nfunction normalizeBool(val) {\n if (val === true) return true;\n if (val === false) return false;\n return null; // unknown\n}\n\nfunction boolToTFU(val) {\n if (val === true) return \"true\";\n if (val === false) return \"false\";\n return \"unknown\";\n}\n\nfunction normalizeObjectType(raw) {\n if (!raw) return \"unknown\";\n const s = String(raw).trim().toUpperCase();\n\n // CCSDS-ish / SATCAT-ish variants\n if (s.includes(\"PAYLOAD\")) return \"payload\";\n if (s.includes(\"DEBRIS\")) return \"debris\";\n if (s.includes(\"ROCKET\") || s.includes(\"R/B\") || s.includes(\"RB\") || s.includes(\"BODY\")) return \"rocket body\";\n if (s.includes(\"UNKNOWN\")) return \"unknown\";\n\n // fallback\n return String(raw).trim().toLowerCase();\n}\n\nfunction canonicalPairLabel(a, b, sep = \"\u2013\") {\n const A = a ?? \"unknown\";\n const B = b ?? \"unknown\";\n return [A, B].sort((x, y) => x.localeCompare(y)).join(sep);\n}\n\nfunction dominantAxisRICFromRTN(relPosRTN) {\n // relPosRTN expected keys: r, t, n (meters)\n if (!relPosRTN || typeof relPosRTN !== \"object\") return \"unknown\";\n\n const r = Number(relPosRTN.r);\n const t = Number(relPosRTN.t);\n const n = Number(relPosRTN.n);\n\n if (![r, t, n].every(v => Number.isFinite(v))) return \"unknown\";\n\n const ar = Math.abs(r), at = Math.abs(t), an = Math.abs(n);\n const max = Math.max(ar, at, an);\n\n if (max === 0) return \"unknown\";\n\n // tie protection: \u0435\u0441\u043b\u0438 \u0440\u0430\u0437\u043d\u0438\u0446\u0430 \u043c\u0435\u0436\u0434\u0443 top-2 < 1% \u2014 \u0441\u0447\u0438\u0442\u0430\u0435\u043c \u043d\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0451\u043d\u043d\u044b\u043c\n const sorted = [ar, at, an].sort((a, b) => b - a);\n if (sorted[1] / sorted[0] > 0.99) return \"unknown\";\n\n // RTN -> RIC mapping:\n // r (radial) -> R\n // t (along-track) -> I\n // n (cross-track) -> C\n if (max === ar) return \"R\";\n if (max === at) return \"I\";\n if (max === an) return \"C\";\n return \"unknown\";\n}\n\nconst items = $input.all();\n\nreturn items.map((item) => {\n const cdm = asObject(item.json?.cdm_json) ?? asObject(item.json) ?? {};\n\n // provider: \u043e\u0431\u044b\u0447\u043d\u043e originator (\u0443 \u0442\u0435\u0431\u044f \"SKIBIDI\")\n const provider = pickFirst(cdm, [\"provider\", \"originator\", \"source\", \"message_originator\"]);\n\n const objects = (cdm && typeof cdm === \"object\" && cdm.objects && typeof cdm.objects === \"object\")\n ? cdm.objects\n : {};\n\n // \u043e\u0436\u0438\u0434\u0430\u0435\u043c object1/object2, \u043d\u043e \u043a\u043e\u0434 \u043d\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442 \u0438\u043c\u0451\u043d \u043a\u043b\u044e\u0447\u0435\u0439\n const objEntries = Object.entries(objects);\n\n const byObject = {};\n const allCatalogs = [];\n const allEphemeris = [];\n const allCovMethods = [];\n const maneuverables = [];\n const objectTypes = [];\n\n for (const [objKey, objVal] of objEntries) {\n const md = objVal?.metadata ?? {};\n\n const catalog_name = md?.catalog_name ?? null;\n const ephemeris_name = md?.ephemeris_name ?? null;\n const covariance_method = md?.covariance_method ?? null;\n\n const maneuverableRaw = normalizeBool(md?.maneuverable);\n const maneuverableTFU = boolToTFU(maneuverableRaw);\n\n const object_type_norm = normalizeObjectType(md?.object_type);\n\n byObject[objKey] = {\n object: md?.object ?? objKey,\n object_name: md?.object_name ?? null,\n object_type_raw: md?.object_type ?? null,\n object_type: object_type_norm,\n maneuverable: maneuverableTFU,\n catalog_name,\n ephemeris_name,\n covariance_method,\n };\n\n allCatalogs.push(catalog_name);\n allEphemeris.push(ephemeris_name);\n allCovMethods.push(covariance_method);\n maneuverables.push(maneuverableRaw);\n objectTypes.push(object_type_norm);\n }\n\n // maneuverable \u043f\u043e \u043f\u0430\u0440\u0435: true/false/unknown (\u0435\u0441\u043b\u0438 \u0441\u043c\u0435\u0448\u0430\u043d\u043e \u0438\u043b\u0438 \u043d\u0435 \u0445\u0432\u0430\u0442\u0430\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0445 \u2014 unknown)\n let maneuverable_pair = \"unknown\";\n if (maneuverables.length >= 2) {\n const a = maneuverables[0], b = maneuverables[1];\n if (a === true && b === true) maneuverable_pair = \"true\";\n else if (a === false && b === false) maneuverable_pair = \"false\";\n else maneuverable_pair = \"unknown\"; // mixed/unknown\n } else if (maneuverables.length === 1) {\n maneuverable_pair = boolToTFU(maneuverables[0]);\n }\n\n // object type pair (\u043a\u0430\u043d\u043e\u043d\u0438\u0447\u0435\u0441\u043a\u0438\u0439, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u0440\u044f\u0434\u043e\u043a \u043d\u0435 \u0448\u0443\u043c\u0435\u043b)\n const object_type_pair = canonicalPairLabel(objectTypes[0] ?? \"unknown\", objectTypes[1] ?? \"unknown\");\n\n // dominant miss-vector axis (RIC) from relative_position_rtn_m\n const relPosRTN = cdm?.relative_metadata_data?.relative_position_rtn_m ?? null;\n const dominant_miss_axis_ric = dominantAxisRICFromRTN(relPosRTN);\n\n const source_family = {\n provider,\n catalogs: uniqNonEmpty(allCatalogs),\n ephemeris: uniqNonEmpty(allEphemeris),\n covariance_methods: uniqNonEmpty(allCovMethods),\n\n signature: [\n provider ?? \"NA\",\n ...uniqNonEmpty(allCatalogs),\n ...uniqNonEmpty(allEphemeris),\n ...uniqNonEmpty(allCovMethods),\n ].join(\"|\"),\n };\n\n return {\n json: {\n message_id: cdm?.message_id ?? null,\n creation_date: cdm?.creation_date ?? null,\n ccsds_cdm_vers: cdm?.ccsds_cdm_vers ?? null,\n\n source_family,\n source_family_by_object: byObject,\n\n maneuverable: maneuverable_pair,\n maneuverable_by_object: Object.fromEntries(\n Object.entries(byObject).map(([k, v]) => [k, v.maneuverable])\n ),\n\n object_type_pair,\n dominant_miss_axis_ric,\n },\n };\n});\n\n"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
208,
0
],
"id": "f46a1b18-af17-4245-86be-f6c7594f6604",
"name": "Code in JavaScript"
},
{
"parameters": {
"operation": "executeQuery",
"query": "with rows as (\n select value as j\n from jsonb_array_elements(\n case\n when jsonb_typeof($1::jsonb) = 'array' then $1::jsonb\n else jsonb_build_array($1::jsonb)\n end\n ) as value\n),\nx as (\n select\n nullif(j->>'message_id','') as message_id,\n\n j->'source_family'->>'provider' as provider,\n j->'source_family'->>'signature' as signature,\n\n case\n when jsonb_typeof(j->'source_family'->'catalogs') = 'array'\n then coalesce(\n (select array_agg(v) from jsonb_array_elements_text(j->'source_family'->'catalogs') as v),\n '{}'::text[]\n )\n else '{}'::text[]\n end as catalogs,\n\n case\n when jsonb_typeof(j->'source_family'->'ephemeris') = 'array'\n then coalesce(\n (select array_agg(v) from jsonb_array_elements_text(j->'source_family'->'ephemeris') as v),\n '{}'::text[]\n )\n else '{}'::text[]\n end as ephemeris,\n\n case\n when jsonb_typeof(j->'source_family'->'covariance_methods') = 'array'\n then coalesce(\n (select array_agg(v) from jsonb_array_elements_text(j->'source_family'->'covariance_methods') as v),\n '{}'::text[]\n )\n else '{}'::text[]\n end as covariance_methods,\n\n coalesce(j->>'maneuverable','unknown') as maneuverable,\n j->>'object_type_pair' as object_type_pair,\n coalesce(j->>'dominant_miss_axis_ric','unknown') as dominant_miss_axis_ric,\n\n -- object1 (\u0431\u0435\u0437 obj1_object)\n j->'source_family_by_object'->'object1'->>'object_name' as obj1_object_name,\n j->'source_family_by_object'->'object1'->>'object_type_raw' as obj1_object_type_raw,\n j->'source_family_by_object'->'object1'->>'object_type' as obj1_object_type,\n coalesce(\n j->'maneuverable_by_object'->>'object1',\n j->'source_family_by_object'->'object1'->>'maneuverable',\n 'unknown'\n ) as obj1_maneuverable,\n j->'source_family_by_object'->'object1'->>'catalog_name' as obj1_catalog_name,\n j->'source_family_by_object'->'object1'->>'ephemeris_name' as obj1_ephemeris_name,\n j->'source_family_by_object'->'object1'->>'covariance_method' as obj1_covariance_method,\n\n -- object2 (\u0431\u0435\u0437 obj2_object \u0438 \u0431\u0435\u0437 obj2_ephemeris_name)\n j->'source_family_by_object'->'object2'->>'object_name' as obj2_object_name,\n j->'source_family_by_object'->'object2'->>'object_type_raw' as obj2_object_type_raw,\n j->'source_family_by_object'->'object2'->>'object_type' as obj2_object_type,\n coalesce(\n j->'maneuverable_by_object'->>'object2',\n j->'source_family_by_object'->'object2'->>'maneuverable',\n 'unknown'\n ) as obj2_maneuverable,\n j->'source_family_by_object'->'object2'->>'catalog_name' as obj2_catalog_name,\n j->'source_family_by_object'->'object2'->>'covariance_method' as obj2_covariance_method,\n\n j->'source_family_by_object' as source_family_by_object,\n j->'maneuverable_by_object' as maneuverable_by_object\n\n from rows\n where nullif(j->>'message_id','') is not null\n)\ninsert into cdm_source_features (\n message_id,\n provider, signature,\n catalogs, ephemeris, covariance_methods,\n maneuverable, object_type_pair, dominant_miss_axis_ric,\n\n obj1_object_name, obj1_object_type_raw, obj1_object_type, obj1_maneuverable, obj1_catalog_name, obj1_ephemeris_name, obj1_covariance_method,\n obj2_object_name, obj2_object_type_raw, obj2_object_type, obj2_maneuverable, obj2_catalog_name, obj2_covariance_method,\n\n source_family_by_object, maneuverable_by_object,\n updated_at\n)\nselect\n message_id,\n provider, signature,\n catalogs, ephemeris, covariance_methods,\n maneuverable, object_type_pair, dominant_miss_axis_ric,\n\n obj1_object_name, obj1_object_type_raw, obj1_object_type, obj1_maneuverable, obj1_catalog_name, obj1_ephemeris_name, obj1_covariance_method,\n obj2_object_name, obj2_object_type_raw, obj2_object_type, obj2_maneuverable, obj2_catalog_name, obj2_covariance_method,\n\n source_family_by_object, maneuverable_by_object,\n now()\nfrom x\non conflict (message_id) do update set\n provider = coalesce(excluded.provider, cdm_source_features.provider),\n signature = coalesce(excluded.signature, cdm_source_features.signature),\n catalogs = coalesce(excluded.catalogs, cdm_source_features.catalogs),\n ephemeris = coalesce(excluded.ephemeris, cdm_source_features.ephemeris),\n covariance_methods = coalesce(excluded.covariance_methods, cdm_source_features.covariance_methods),\n maneuverable = coalesce(excluded.maneuverable, cdm_source_features.maneuverable),\n object_type_pair = coalesce(excluded.object_type_pair, cdm_source_features.object_type_pair),\n dominant_miss_axis_ric = coalesce(excluded.dominant_miss_axis_ric, cdm_source_features.dominant_miss_axis_ric),\n\n obj1_object_name = coalesce(excluded.obj1_object_name, cdm_source_features.obj1_object_name),\n obj1_object_type_raw = coalesce(excluded.obj1_object_type_raw, cdm_source_features.obj1_object_type_raw),\n obj1_object_type = coalesce(excluded.obj1_object_type, cdm_source_features.obj1_object_type),\n obj1_maneuverable = coalesce(excluded.obj1_maneuverable, cdm_source_features.obj1_maneuverable),\n obj1_catalog_name = coalesce(excluded.obj1_catalog_name, cdm_source_features.obj1_catalog_name),\n obj1_ephemeris_name = coalesce(excluded.obj1_ephemeris_name, cdm_source_features.obj1_ephemeris_name),\n obj1_covariance_method = coalesce(excluded.obj1_covariance_method, cdm_source_features.obj1_covariance_method),\n\n obj2_object_name = coalesce(excluded.obj2_object_name, cdm_source_features.obj2_object_name),\n obj2_object_type_raw = coalesce(excluded.obj2_object_type_raw, cdm_source_features.obj2_object_type_raw),\n obj2_object_type = coalesce(excluded.obj2_object_type, cdm_source_features.obj2_object_type),\n obj2_maneuverable = coalesce(excluded.obj2_maneuverable, cdm_source_features.obj2_maneuverable),\n obj2_catalog_name = coalesce(excluded.obj2_catalog_name, cdm_source_features.obj2_catalog_name),\n obj2_covariance_method = coalesce(excluded.obj2_covariance_method, cdm_source_features.obj2_covariance_method),\n\n source_family_by_object = coalesce(excluded.source_family_by_object, cdm_source_features.source_family_by_object),\n maneuverable_by_object = coalesce(excluded.maneuverable_by_object, cdm_source_features.maneuverable_by_object),\n updated_at = now();",
"options": {
"queryReplacement": "=$1 = {{$input.all().map(i => i.json)}}\n$2 = {{ $json.pair_b }}"
}
},
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.6,
"position": [
448,
0
],
"id": "243de97c-640d-4612-81ff-badacfb8c5b9",
"name": "Execute a SQL query4",
"executeOnce": true,
"alwaysOutputData": true,
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"workflowInputs": {
"values": [
{
"name": "main_pair_key"
}
]
}
},
"type": "n8n-nodes-base.executeWorkflowTrigger",
"typeVersion": 1.1,
"position": [
-224,
176
],
"id": "78bf9c68-d491-459c-bf72-ec65a0ba9339",
"name": "When Executed by Another Workflow"
},
{
"parameters": {
"operation": "executeQuery",
"query": "select message_id, provider, catalogs, object_type_pair, dominant_miss_axis_ric, obj1_object_name, obj2_object_name, maneuverable_by_object, covariance_methods, pair_key, creation_date\nfrom cdm_source_features\n",
"options": {
"queryReplacement": "=$1 = {{ $json.pair_a }} \n$2 = {{ $json.pair_b }}"
}
},
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.6,
"position": [
304,
160
],
"id": "2b1e5817-28b5-42bf-b1d6-1c7e7307f28c",
"name": "Execute a SQL query",
"executeOnce": true,
"alwaysOutputData": true,
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "// n8n Code node (JavaScript)\n\nconst rows = $input.all().map(i => i.json);\n\n// \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438\nconst FAIL_ON_MISMATCH = false; // true = \u0431\u0440\u043e\u0441\u0430\u0442\u044c \u043e\u0448\u0438\u0431\u043a\u0443, \u0435\u0441\u043b\u0438 \u0435\u0441\u0442\u044c \u043d\u0435\u0441\u043e\u0432\u043f\u0430\u0434\u0435\u043d\u0438\u044f \u0432\u043d\u0443\u0442\u0440\u0438 group (stable)\nconst OUTPUT_ONLY_BAD = false; // true = \u0432\u044b\u0432\u043e\u0434\u0438\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e pair_key \u0433\u0434\u0435 ok=false\n\n// helpers\nconst dashNormalize = (s) => String(s ?? '')\n .replace(/[\\u2010\\u2011\\u2012\\u2013\\u2014\\u2212]/g, '-') // \u0440\u0430\u0437\u043d\u044b\u0435 \u0442\u0438\u0440\u0435 -> '-'\n .trim();\n\nconst normStr = (s) => dashNormalize(s).toUpperCase();\nconst normProvider = (p) => {\n const x = normStr(p);\n return x ? x : 'UNKNOWN';\n};\n\nconst normArrSet = (a) => {\n if (!Array.isArray(a)) return [];\n const set = new Set(a.map(x => normStr(x)).filter(Boolean));\n return Array.from(set).sort(); // \u043f\u043e\u0440\u044f\u0434\u043e\u043a \u043d\u0435 \u0432\u0430\u0436\u0435\u043d\n};\n\nconst normBool = (v) => {\n if (typeof v === 'boolean') return v;\n const s = String(v ?? '').trim().toLowerCase();\n if (s === 'true' || s === '1' || s === 'yes') return true;\n if (s === 'false' || s === '0' || s === 'no') return false;\n return null;\n};\n\nconst normManeuverable = (m) => ({\n object1: normBool(m?.object1),\n object2: normBool(m?.object2),\n});\n\nconst normObjTypePair = (s) => dashNormalize(s).toLowerCase().replace(/\\s+/g, ' ').trim();\nconst normAxis = (a) => {\n const s = normStr(a);\n return s || null;\n};\n\nconst stableComparable = (r) => ({\n provider: normProvider(r.provider),\n catalogs: normArrSet(r.catalogs),\n object_type_pair: normObjTypePair(r.object_type_pair),\n dominant_miss_axis_ric: normAxis(r.dominant_miss_axis_ric),\n maneuverable_by_object: normManeuverable(r.maneuverable_by_object),\n covariance_methods: normArrSet(r.covariance_methods),\n});\n\nconst stableSig = (x) => JSON.stringify(x);\nconst deepEq = (a, b) => JSON.stringify(a) === JSON.stringify(b);\n\n// === main_pair_key \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442 \u0438\u0437 \"When Executed by Another Workflow\" ===\nconst mainPairKey = String(\n $('When Executed by Another Workflow').first().json.main_pair_key ?? ''\n).trim();\n\nif (!mainPairKey) {\n return [{\n json: { ok: false, reason: 'missing_main_pair_key', hint: 'main_pair_key \u043f\u0443\u0441\u0442\u043e\u0439/\u043d\u0435 \u043f\u0440\u0438\u0448\u0451\u043b \u0438\u0437 \u0442\u0440\u0438\u0433\u0433\u0435\u0440-\u043d\u043e\u0434\u044b' }\n }];\n}\n\n// provider \u0438 stable-\u044d\u0442\u0430\u043b\u043e\u043d \u0431\u0435\u0440\u0451\u043c \u0422\u041e\u041b\u042c\u041a\u041e \u0443 \u0441\u0442\u0440\u043e\u043a main_pair_key\nconst mainRows = rows.filter(r => String(r.pair_key ?? '').trim() === mainPairKey);\n\nif (mainRows.length === 0) {\n const available = Array.from(new Set(rows.map(r => String(r.pair_key ?? '').trim()))).filter(Boolean);\n return [{\n json: {\n ok: false,\n reason: 'main_pair_key_not_present_in_rows',\n main_pair_key: mainPairKey,\n available_pair_keys: available,\n }\n }];\n}\n\n// \u0441\u0442\u0440\u043e\u0438\u043c \u043d\u043e\u0440\u043c-\u0433\u0440\u0443\u043f\u043f\u0443 main\nconst mainNorm = mainRows.map(r => ({\n pair_key: mainPairKey,\n message_id: String(r.message_id ?? ''),\n creation_date: String(r.creation_date ?? ''),\n stable: stableComparable(r),\n}));\n\nconst mainRef = mainNorm[0];\nconst mainRefSig = stableSig(mainRef.stable);\n\n// \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u043a\u043e\u043d\u0441\u0438\u0441\u0442\u0435\u043d\u0442\u043d\u043e\u0441\u0442\u044c stable \u0432\u043d\u0443\u0442\u0440\u0438 main_pair_key\nconst mainInternalMismatches = [];\nfor (let i = 0; i < mainNorm.length; i++) {\n const cur = mainNorm[i];\n if (stableSig(cur.stable) !== mainRefSig) {\n mainInternalMismatches.push({\n at_index: i,\n message_id: cur.message_id,\n creation_date: cur.creation_date,\n ref: mainRef.stable,\n got: cur.stable,\n });\n }\n}\nif (mainInternalMismatches.length > 0) {\n return [{\n json: {\n ok: false,\n reason: 'inconsistent_stable_within_main_pair_key',\n main_pair_key: mainPairKey,\n main_provider: mainRef.stable.provider,\n mismatches: mainInternalMismatches,\n }\n }];\n}\n\nconst mainStable = mainRef.stable;\nconst mainProvider = mainStable.provider;\n\n// group by pair_key\nconst byPair = new Map();\nfor (const r of rows) {\n const k = String(r.pair_key ?? '').trim();\n if (!byPair.has(k)) byPair.set(k, []);\n byPair.get(k).push(r);\n}\n\nlet mismatchCount = 0;\nconst out = [];\n\nfor (const [pair_key, group] of byPair.entries()) {\n const normGroup = group.map(r => ({\n pair_key,\n message_id: String(r.message_id ?? ''),\n creation_date: String(r.creation_date ?? ''),\n stable: stableComparable(r),\n }));\n\n const ref = normGroup[0];\n const groupProvider = ref.stable.provider;\n\n // 1) provider \u0434\u043e\u043b\u0436\u0435\u043d \u0441\u043e\u0432\u043f\u0430\u0441\u0442\u044c \u0441 main_provider\n if (groupProvider !== mainProvider) {\n const record = {\n pair_key,\n count: group.length,\n ok: false,\n reason: 'provider_mismatch_to_main',\n main_pair_key: mainPairKey,\n main_provider: mainProvider,\n group_provider: groupProvider,\n main_reference_stable_fields: mainStable,\n group_reference_stable_fields: ref.stable,\n mismatches_within_group: [],\n mismatches_to_main: [{\n field: 'provider',\n ref: mainProvider,\n got: groupProvider,\n }],\n };\n if (!OUTPUT_ONLY_BAD || !record.ok) out.push(record);\n continue;\n }\n\n // 2) \u043a\u043e\u043d\u0441\u0438\u0441\u0442\u0435\u043d\u0442\u043d\u043e\u0441\u0442\u044c stable \u0432\u043d\u0443\u0442\u0440\u0438 group (\u043a\u0430\u043a \u0443 \u0442\u0435\u0431\u044f \u0431\u044b\u043b\u043e)\n const refSig = stableSig(ref.stable);\n const mismatchesWithin = [];\n for (let i = 0; i < normGroup.length; i++) {\n const cur = normGroup[i];\n if (stableSig(cur.stable) !== refSig) {\n mismatchesWithin.push({\n at_index: i,\n message_id: cur.message_id,\n creation_date: cur.creation_date,\n ref: ref.stable,\n got: cur.stable,\n });\n }\n }\n\n // 3) \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u0435 stable group vs stable main (MAJOR match)\n const mismatchesToMain = [];\n\n if (!deepEq(ref.stable.catalogs, mainStable.catalogs)) {\n mismatchesToMain.push({ field: 'catalogs', ref: mainStable.catalogs, got: ref.stable.catalogs });\n }\n if (!deepEq(ref.stable.covariance_methods, mainStable.covariance_methods)) {\n mismatchesToMain.push({ field: 'covariance_methods', ref: mainStable.covariance_methods, got: ref.stable.covariance_methods });\n }\n if (ref.stable.object_type_pair !== mainStable.object_type_pair) {\n mismatchesToMain.push({ field: 'object_type_pair', ref: mainStable.object_type_pair, got: ref.stable.object_type_pair });\n }\n if (ref.stable.dominant_miss_axis_ric !== mainStable.dominant_miss_axis_ric) {\n mismatchesToMain.push({ field: 'dominant_miss_axis_ric', ref: mainStable.dominant_miss_axis_ric, got: ref.stable.dominant_miss_axis_ric });\n }\n if (!deepEq(ref.stable.maneuverable_by_object, mainStable.maneuverable_by_object)) {\n mismatchesToMain.push({ field: 'maneuverable_by_object', ref: mainStable.maneuverable_by_object, got: ref.stable.maneuverable_by_object });\n }\n\n const ok = (mismatchesWithin.length === 0) && (mismatchesToMain.length === 0);\n if (!ok) mismatchCount += (mismatchesWithin.length + mismatchesToMain.length);\n\n const record = {\n pair_key,\n count: group.length,\n ok,\n main_pair_key: mainPairKey,\n main_provider: mainProvider,\n group_provider: groupProvider,\n main_reference_stable_fields: mainStable,\n group_reference_stable_fields: ref.stable,\n mismatches_within_group: mismatchesWithin,\n mismatches_to_main: mismatchesToMain,\n };\n\n if (!OUTPUT_ONLY_BAD || !ok) out.push(record);\n}\n\nif (FAIL_ON_MISMATCH && mismatchCount > 0) {\n throw new Error(`Stable-field mismatches found: ${mismatchCount}`);\n}\n\nreturn out.map(x => ({ json: x }));"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
544,
144
],
"id": "19c2dfb2-01dd-49cd-86eb-e9f5ef12d1eb",
"name": "Code in JavaScript1"
},
{
"parameters": {
"formTitle": "\u0444\u044b\u0432\u0444\u044b",
"formDescription": "\u0444\u0432\u044b\u0444\u0432\u044b\u0444",
"formFields": {
"values": [
{
"fieldLabel": "pair_a",
"fieldType": "textarea"
},
{
"fieldLabel": "pair_b",
"fieldType": "textarea"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.formTrigger",
"typeVersion": 2.5,
"position": [
-208,
16
],
"id": "0a525924-aa4d-4001-8248-b7d4c71bea32",
"name": "On form submission"
}
],
"connections": {
"Execute a SQL query2": {
"main": [
[
{
"node": "Code in JavaScript",
"type": "main",
"index": 0
}
]
]
},
"Code in JavaScript": {
"main": [
[
{
"node": "Execute a SQL query4",
"type": "main",
"index": 0
}
]
]
},
"When Executed by Another Workflow": {
"main": [
[
{
"node": "Execute a SQL query",
"type": "main",
"index": 0
}
]
]
},
"Execute a SQL query": {
"main": [
[
{
"node": "Code in JavaScript1",
"type": "main",
"index": 0
}
]
]
},
"On form submission": {
"main": [
[
{
"node": "Execute a SQL query2",
"type": "main",
"index": 0
}
]
]
}
},
"active": true,
"settings": {
"executionOrder": "v1",
"binaryMode": "separate",
"availableInMCP": false
},
"versionId": "9859f31d-160f-4798-9f36-97102d473e8c",
"meta": {
"templateCredsSetupCompleted": true
},
"id": "4rwNOFZXD0xQnTNN",
"tags": []
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
postgres
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
major. Uses postgres, executeWorkflowTrigger, formTrigger. Event-driven trigger; 7 nodes.
Source: https://github.com/garfieldgg228-g423/Aktau-esa5-DEMO/blob/f235e9619920970a642f882710ed20992602ea80/workflows/major.json — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Agendamiento_v2. Uses n8n-nodes-evolution-api, redis, httpRequest, executeWorkflowTrigger. Event-driven trigger; 59 nodes.
Cancelacion_v2. Uses executeWorkflowTrigger, redis, httpRequest, n8n-nodes-evolution-api. Event-driven trigger; 46 nodes.
Youtube Searcher. Uses splitInBatches, httpRequest, manualTrigger, executeWorkflowTrigger. Event-driven trigger; 21 nodes.
This workflow provides a reliable and secure system for uploading and managing documents.
QuepasaAutomatic. Uses postgres, executeWorkflowTrigger. Event-driven trigger; 20 nodes.