Workflow: Postgres Code Automation

Workflow Details

Download Workflow
{
    "id": "Zrd98BnbmN1Px9an",
    "meta": {
        "instanceId": "edc0464b1050024ebda3e16fceea795e4fdf67b1f61187c4f2f3a72397278df0",
        "templateCredsSetupCompleted": true
    },
    "name": "Youtube Searcher",
    "tags": [],
    "nodes": [
        {
            "id": "5cb8757a-d8f0-49fa-803d-7f04b514f9f8",
            "name": "Loop Over Items",
            "type": "n8n-nodes-base.splitInBatches",
            "position": [
                80,
                220
            ],
            "parameters": {
                "options": []
            },
            "typeVersion": 3
        },
        {
            "id": "28964bd5-dc53-4dfa-bbb1-4eb80b952063",
            "name": "find_video_data1",
            "type": "n8n-nodes-base.httpRequest",
            "position": [
                1440,
                320
            ],
            "parameters": {
                "url": "https:\/\/www.googleapis.com\/youtube\/v3\/videos?",
                "options": [],
                "sendQuery": true,
                "queryParameters": {
                    "parameters": [
                        {
                            "name": "key",
                            "value": "={{ $env[\"GOOGLE_API_KEY\"] }}"
                        },
                        {
                            "name": "id",
                            "value": "={{ $json.id.videoId }}"
                        },
                        {
                            "name": "part",
                            "value": "contentDetails, statistics"
                        }
                    ]
                }
            },
            "typeVersion": 4.20000000000000017763568394002504646778106689453125
        },
        {
            "id": "5e8b9441-4b91-4460-a9ac-4a0a02aa57ad",
            "name": "When clicking \u2018Test workflow\u2019",
            "type": "n8n-nodes-base.manualTrigger",
            "disabled": true,
            "position": [
                -180,
                220
            ],
            "parameters": [],
            "typeVersion": 1
        },
        {
            "id": "793ef651-ea56-41bc-a0a9-feeaddf999c0",
            "name": "Execute Workflow Trigger",
            "type": "n8n-nodes-base.executeWorkflowTrigger",
            "position": [
                -160,
                -180
            ],
            "parameters": [],
            "typeVersion": 1
        },
        {
            "id": "64e331ff-2cda-4ba0-94f9-03fa6c3d6590",
            "name": "fetch_last_registered",
            "type": "n8n-nodes-base.postgres",
            "position": [
                360,
                360
            ],
            "parameters": {
                "query": "SELECT MAX(publish_time) AS latest_publish_time\nFROM video_statistics\nWHERE channel_id = '{{ $json.id }}';",
                "options": [],
                "operation": "executeQuery"
            },
            "credentials": {
                "postgres": {
                    "id": "KQiQIZTArTBSNJH7",
                    "name": "Postgres account"
                }
            },
            "typeVersion": 2.5
        },
        {
            "id": "fb0a8208-c920-4344-8816-ef6509f07abc",
            "name": "get_videos",
            "type": "n8n-nodes-base.youTube",
            "onError": "continueRegularOutput",
            "position": [
                640,
                360
            ],
            "parameters": {
                "limit": 50,
                "filters": {
                    "channelId": "={{ $('Loop Over Items').item.json.id }}",
                    "regionCode": "US",
                    "publishedAfter": "={{ $json.latest_publish_time ? new Date(new Date($json.latest_publish_time).getTime() + 60 * 60 * 1000).toISOString() : new Date(Date.now() - 3 * 30 * 24 * 60 * 60 * 1000).toISOString() }}"
                },
                "options": {
                    "order": "relevance",
                    "safeSearch": "moderate"
                },
                "resource": "video"
            },
            "credentials": {
                "youTubeOAuth2Api": {
                    "id": "o3VUdoHEk6VhB1lq",
                    "name": "YouTube account"
                }
            },
            "typeVersion": 1,
            "alwaysOutputData": true
        },
        {
            "id": "ea358d3c-9a83-49c9-a02e-745cf5b29097",
            "name": "if_is_empty",
            "type": "n8n-nodes-base.if",
            "onError": "continueRegularOutput",
            "position": [
                940,
                540
            ],
            "parameters": {
                "options": [],
                "conditions": {
                    "options": {
                        "version": 2,
                        "leftValue": "",
                        "caseSensitive": true,
                        "typeValidation": "strict"
                    },
                    "combinator": "or",
                    "conditions": [
                        {
                            "id": "7591deae-4626-4b2e-af26-d02042573a13",
                            "operator": {
                                "type": "object",
                                "operation": "notEmpty",
                                "singleValue": true
                            },
                            "leftValue": "={{ $input.item.json }}",
                            "rightValue": ""
                        }
                    ]
                }
            },
            "typeVersion": 2.20000000000000017763568394002504646778106689453125
        },
        {
            "id": "142e5c5e-f488-4667-a759-ef4494f2a194",
            "name": "Postgres",
            "type": "n8n-nodes-base.postgres",
            "position": [
                80,
                -180
            ],
            "parameters": {
                "query": "WITH RankedVideos AS (\n    SELECT \n        channel_id,\n        id,\n        view_count,\n        like_count,\n        comment_count,\n        publish_time,\n        ROW_NUMBER() OVER (PARTITION BY channel_id ORDER BY view_count DESC) AS rank_desc,\n        ROW_NUMBER() OVER (PARTITION BY channel_id ORDER BY view_count ASC) AS rank_asc\n    FROM video_statistics\n),\nFilteredVideos AS (\n    SELECT \n        channel_id,\n        id,\n        view_count,\n        like_count,\n        comment_count,\n        publish_time\n    FROM RankedVideos\n    WHERE NOT (\n        rank_desc <= 2 OR rank_asc <= 2  -- Exclude top 2 and bottom 2 videos\n    )\n    OR (\n        (SELECT COUNT(*) FROM video_statistics WHERE video_statistics.channel_id = RankedVideos.channel_id) <= 10  -- Include all videos if 10 or fewer exist\n    )\n),\nChannelStats AS (\n    SELECT \n        channel_id,\n        ROUND(AVG(view_count)::NUMERIC, 0) AS average_views -- Round to 0 decimal places\n    FROM FilteredVideos\n    GROUP BY channel_id\n)\nSELECT \n    v.channel_id,\n    c.average_views,\n    JSON_AGG(\n        JSON_BUILD_OBJECT(\n            'id', v.id,\n            'view_count', v.view_count,\n            'like_count', v.like_count,\n            'comment_count', v.comment_count,\n            'publish_time', v.publish_time\n        )\n    ) AS channel_videos\nFROM video_statistics v\nLEFT JOIN ChannelStats c\nON v.channel_id = c.channel_id\nGROUP BY v.channel_id, c.average_views;\n",
                "options": [],
                "operation": "executeQuery"
            },
            "credentials": {
                "postgres": {
                    "id": "KQiQIZTArTBSNJH7",
                    "name": "Postgres account"
                }
            },
            "typeVersion": 2.5
        },
        {
            "id": "a542b55e-bab4-476d-8333-692f5b3a5dcb",
            "name": "insert_items",
            "type": "n8n-nodes-base.postgres",
            "position": [
                2980,
                320
            ],
            "parameters": {
                "query": "{{$json.query}}",
                "options": {
                    "queryReplacement": "={{$json.parameters}}"
                },
                "operation": "executeQuery"
            },
            "credentials": {
                "postgres": {
                    "id": "KQiQIZTArTBSNJH7",
                    "name": "Postgres account"
                }
            },
            "typeVersion": 2.5
        },
        {
            "id": "6680728a-805e-4a45-8720-56726ad9e582",
            "name": "create_table",
            "type": "n8n-nodes-base.postgres",
            "position": [
                620,
                -180
            ],
            "parameters": {
                "query": "CREATE TABLE video_statistics (\n    id VARCHAR(255) PRIMARY KEY, -- Unique identifier for the video\n    view_count INT NOT NULL, -- Number of views\n    like_count INT NOT NULL, -- Number of likes\n    comment_count INT NOT NULL, -- Number of comments\n    publish_time TIMESTAMP NOT NULL, -- Timestamp of publishing\n    channel_id VARCHAR(255) NOT NULL -- Channel ID\n);\n",
                "options": [],
                "operation": "executeQuery"
            },
            "credentials": {
                "postgres": {
                    "id": "KQiQIZTArTBSNJH7",
                    "name": "Postgres account"
                }
            },
            "typeVersion": 2.5
        },
        {
            "id": "4e345df5-bdd6-4a93-9096-367bd911dbd4",
            "name": "remove_shorts",
            "type": "n8n-nodes-base.code",
            "position": [
                1720,
                320
            ],
            "parameters": {
                "jsCode": "const input = $input.all();\n\nconst iso8601ToSeconds = iso8601 => {\n  const match = iso8601 ? iso8601.match(\/PT(?:(\\d+)H)?(?:(\\d+)M)?(?:(\\d+)S)?\/) : null;\n  if (!match) {\n    console.warn(`Invalid ISO8601 duration: ${iso8601}`);\n    return 0; \n  }\n  const hours = parseInt(match[1] || 0, 10);\n  const minutes = parseInt(match[2] || 0, 10);\n  const seconds = parseInt(match[3] || 0, 10);\n  return hours * 3600 + minutes * 60 + seconds;\n};\n\nconst filteredResponses = input.filter(response => {\n  if (response.json && response.json.items) {\n    const validItems = response.json.items.filter(item => {\n      const duration = item.contentDetails?.duration;\n      if (!duration) {\n        console.warn(`Missing duration for item: ${JSON.stringify(item)}`);\n        return false; \n      }\n      const durationInSeconds = iso8601ToSeconds(duration);\n\n      return durationInSeconds > 210;\n    });\n\n    response.json.items = validItems;\n\n    return validItems.length > 0; \n  }\n\n  return false;\n});\n\nreturn filteredResponses;\n"
            },
            "typeVersion": 2,
            "alwaysOutputData": true
        },
        {
            "id": "aadac7e3-8114-4c43-b0bf-d1a7de7c3e0c",
            "name": "create_query",
            "type": "n8n-nodes-base.code",
            "position": [
                2780,
                320
            ],
            "parameters": {
                "jsCode": "const input = $input.all();\n\nlet tableName = \"video_statistics\"; \n\nconst rows = input;\n\nconst formattedRows = rows.map(elements => {\n  const row = elements.json;\n  const formattedRow = {\n    id: row.id,\n    view_count: parseInt(row.viewCount, 10) || 0, \n    like_count: parseInt(row.likeCount, 10) || 0,\n    comment_count: parseInt(row.commentCount, 10) || 0,\n    publish_time: row.publishTime ? new Date(row.publishTime).toISOString() : null,\n    channel_id: $('Loop Over Items').first().json.id || \"unknown\"\n  };\n  return formattedRow;\n});\n\nconst columns = [\"id\", \"view_count\", \"like_count\", \"comment_count\", \"publish_time\", \"channel_id\"];\n\nconst valuePlaceholders = formattedRows.map((_, rowIndex) =>\n  `(${columns.map((_, colIndex) => `$${rowIndex * columns.length + colIndex + 1}`).join(\", \")})`\n).join(\", \");\n\nconst insertQuery = `INSERT INTO ${tableName} (${columns.map(col => `\\\"${col}\\\"`).join(\", \")}) VALUES ${valuePlaceholders};`;\n\nconst parameters = formattedRows.flatMap(row => \n  columns.map(col => row[col])\n);\n\nreturn [\n  {\n    query: insertQuery,\n    parameters: parameters\n  }\n];\n"
            },
            "typeVersion": 2
        },
        {
            "id": "46376f7c-1ce1-4f8a-8392-7281aacfd1c5",
            "name": "structure_data",
            "type": "n8n-nodes-base.code",
            "position": [
                2560,
                320
            ],
            "parameters": {
                "jsCode": "const input = $input.all(); \n\nconst filteredInput = input.filter(item => item.json.viewCount !== null);\n\nconst updatedInput = filteredInput.map(item => {\n    return {\n        ...item,\n        json: {\n            ...item.json,\n            likeCount: item.json.likeCount === null ? \"0\" : item.json.likeCount,\n            commentCount: item.json.commentCount === null ? \"0\" : item.json.commentCount\n        }\n    };\n});\n\nreturn updatedInput;\n"
            },
            "typeVersion": 2
        },
        {
            "id": "f66597ef-1324-45e0-b3e8-bc8a588315e4",
            "name": "if_empty",
            "type": "n8n-nodes-base.if",
            "position": [
                2020,
                500
            ],
            "parameters": {
                "options": [],
                "conditions": {
                    "options": {
                        "version": 2,
                        "leftValue": "",
                        "caseSensitive": true,
                        "typeValidation": "strict"
                    },
                    "combinator": "and",
                    "conditions": [
                        {
                            "id": "dacc5370-f54c-4b90-a2aa-65efff196d3b",
                            "operator": {
                                "type": "object",
                                "operation": "notEmpty",
                                "singleValue": true
                            },
                            "leftValue": "={{ $json }}",
                            "rightValue": ""
                        }
                    ]
                }
            },
            "typeVersion": 2.20000000000000017763568394002504646778106689453125
        },
        {
            "id": "1176b08f-79bb-4f8f-8c83-25a7c2cee9e7",
            "name": "already_populated",
            "type": "n8n-nodes-base.set",
            "position": [
                1200,
                600
            ],
            "parameters": {
                "options": [],
                "assignments": {
                    "assignments": [
                        {
                            "id": "7579fbc3-d702-4c36-b539-11b7db6c07fa",
                            "name": "report",
                            "type": "string",
                            "value": "={{ $('Loop Over Items').item.json.url }} already populated. Latest was: {{ $('fetch_last_registered').item.json.latest_publish_time }}"
                        }
                    ]
                }
            },
            "typeVersion": 3.399999999999999911182158029987476766109466552734375
        },
        {
            "id": "265b3062-ee60-4de0-8ee0-3973e653aa7d",
            "name": "map_data",
            "type": "n8n-nodes-base.set",
            "position": [
                2340,
                320
            ],
            "parameters": {
                "options": [],
                "assignments": {
                    "assignments": [
                        {
                            "id": "1a76e4e8-cd56-4d55-bcbf-ed24708e1464",
                            "name": "id",
                            "type": "string",
                            "value": "={{ $json.items[0].id }}"
                        },
                        {
                            "id": "0b6d93ba-89fb-4781-809f-6c7bd887f9e2",
                            "name": "viewCount",
                            "type": "string",
                            "value": "={{ $json.items[0].statistics.viewCount }}"
                        },
                        {
                            "id": "9526b059-661a-49a2-81d3-3623d677ddd1",
                            "name": "likeCount",
                            "type": "string",
                            "value": "={{ $json.items[0].statistics.likeCount }}"
                        },
                        {
                            "id": "ca4adf8b-d74f-4dda-a96e-0a2ca3e864e3",
                            "name": "commentCount",
                            "type": "string",
                            "value": "={{ $json.items[0].statistics.commentCount }}"
                        },
                        {
                            "id": "8129ff1c-87c6-489b-83f8-88bdbf426b0f",
                            "name": "=publishTime",
                            "type": "string",
                            "value": "={{ $('get_videos').item.json.snippet.publishedAt }}"
                        },
                        {
                            "id": "16fc88dc-4772-4380-873d-2aa9642b31ac",
                            "name": "channelId",
                            "type": "string",
                            "value": "={{ $('if_is_empty').item.json.snippet.channelId }}"
                        }
                    ]
                }
            },
            "typeVersion": 3.399999999999999911182158029987476766109466552734375
        },
        {
            "id": "173ac548-89be-4e94-a0e3-e90c45489a0c",
            "name": "sanitize_data",
            "type": "n8n-nodes-base.code",
            "position": [
                300,
                -180
            ],
            "parameters": {
                "jsCode": "const now = new Date();\nconst twoWeeksAgo = new Date(now.getTime() - 14 * 24 * 60 * 60 * 1000);\n\nconst bestPerformingVideos = [];\n\n$input.all().forEach(channel => {\n  \n  const averageViews = parseInt(channel.json.average_views, 10);\n  \n  channel.json.channel_videos.forEach(video => {\n    const publishDate = new Date(video.publish_time);\n    const isWithinTwoWeeks = publishDate >= twoWeeksAgo && publishDate <= now;\n    const isAboveThreshold = video.view_count >= 2 * averageViews;\n\n  \n    if (isWithinTwoWeeks && isAboveThreshold) {\n      const score = (video.like_count \/ video.view_count) * 100;\n      bestPerformingVideos.push({\n        id: video.id,\n        videoUrl: `https:\/\/www.youtube.com\/watch?v=${video.id}`,\n        viewCount: video.view_count,\n        likeCount: video.like_count,\n        score: parseFloat(score.toFixed(2)),\n        commentCount: video.comment_count,\n        channelId: `https:\/\/www.youtube.com\/channel\/${channel.json.channel_id}` \n      });\n    }\n  });\n});\n\nreturn bestPerformingVideos;\n"
            },
            "typeVersion": 2,
            "alwaysOutputData": true
        },
        {
            "id": "48e729ac-985c-47f5-8895-d2e52581e849",
            "name": "Sticky Note",
            "type": "n8n-nodes-base.stickyNote",
            "position": [
                -260,
                140
            ],
            "parameters": {
                "color": 7,
                "width": 3440,
                "height": 720,
                "content": "### Save Videos To Database"
            },
            "typeVersion": 1
        },
        {
            "id": "11c51123-27f7-4de7-9215-49d89679c2f6",
            "name": "Sticky Note1",
            "type": "n8n-nodes-base.stickyNote",
            "position": [
                -260,
                -260
            ],
            "parameters": {
                "color": 6,
                "width": 780,
                "height": 280,
                "content": "### Fetch best performing videos from last 2 weeks"
            },
            "typeVersion": 1
        },
        {
            "id": "7ef37f94-9283-4b51-a127-98c94542429a",
            "name": "see table",
            "type": "n8n-nodes-base.postgres",
            "position": [
                920,
                -180
            ],
            "parameters": {
                "query": "SELECT * FROM video_statistics;",
                "options": [],
                "operation": "executeQuery"
            },
            "credentials": {
                "postgres": {
                    "id": "KQiQIZTArTBSNJH7",
                    "name": "Postgres account"
                }
            },
            "typeVersion": 2.5
        },
        {
            "id": "e66af542-ea16-4c3c-9f6e-b5401bbd41da",
            "name": "drop table",
            "type": "n8n-nodes-base.postgres",
            "position": [
                1200,
                -180
            ],
            "parameters": {
                "query": "DROP TABLE video_statistics;",
                "options": [],
                "operation": "executeQuery"
            },
            "credentials": {
                "postgres": {
                    "id": "KQiQIZTArTBSNJH7",
                    "name": "Postgres account"
                }
            },
            "typeVersion": 2.5
        }
    ],
    "active": false,
    "pinData": {
        "When clicking \u2018Test workflow\u2019": [
            {
                "json": {
                    "id": "UCMwVTLZIRRUyyVrkjDpn4pA",
                    "url": "https:\/\/www.youtube.com\/@ColeMedin"
                }
            },
            {
                "json": {
                    "id": "UC2ojq-nuP8ceeHqiroeKhBA",
                    "url": "www.youtube.com\/@nateherk"
                }
            }
        ]
    },
    "settings": {
        "executionOrder": "v1"
    },
    "versionId": "8ee4a252-a795-4931-951f-024d1f0d801a",
    "connections": {
        "Postgres": {
            "main": [
                [
                    {
                        "node": "sanitize_data",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        },
        "if_empty": {
            "main": [
                [
                    {
                        "node": "map_data",
                        "type": "main",
                        "index": 0
                    }
                ],
                [
                    {
                        "node": "Loop Over Items",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        },
        "map_data": {
            "main": [
                [
                    {
                        "node": "structure_data",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        },
        "get_videos": {
            "main": [
                [
                    {
                        "node": "if_is_empty",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        },
        "if_is_empty": {
            "main": [
                [
                    {
                        "node": "find_video_data1",
                        "type": "main",
                        "index": 0
                    }
                ],
                [
                    {
                        "node": "already_populated",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        },
        "create_query": {
            "main": [
                [
                    {
                        "node": "insert_items",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        },
        "insert_items": {
            "main": [
                [
                    {
                        "node": "Loop Over Items",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        },
        "remove_shorts": {
            "main": [
                [
                    {
                        "node": "if_empty",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        },
        "structure_data": {
            "main": [
                [
                    {
                        "node": "create_query",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        },
        "Loop Over Items": {
            "main": [
                [],
                [
                    {
                        "node": "fetch_last_registered",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        },
        "find_video_data1": {
            "main": [
                [
                    {
                        "node": "remove_shorts",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        },
        "already_populated": {
            "main": [
                [
                    {
                        "node": "Loop Over Items",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        },
        "fetch_last_registered": {
            "main": [
                [
                    {
                        "node": "get_videos",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        },
        "Execute Workflow Trigger": {
            "main": [
                [
                    {
                        "node": "Postgres",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        },
        "When clicking \u2018Test workflow\u2019": {
            "main": [
                [
                    {
                        "node": "Loop Over Items",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        }
    }
}
Back to Workflows

Related Workflows

Code Schedule Create Scheduled
View
Code Schedule Export Scheduled
View
Stickynote Send Triggered
View
Manual Mautic Automate Triggered
View
AI Logo Sheet Extractor to Airtable
View
Send an SMS using MSG91
View
Shopify Zendesk Create Triggered
View
Blockchain DEX Screener Insights Agent
View
Telegram Tron Wallet Blacklist Checker
View
Slack Comparedatasets Create Triggered
View