AutomationFlowsAI & RAG › Firestore 정오 요약 리포트 (gemini)

Firestore 정오 요약 리포트 (gemini)

Firestore 정오 요약 리포트 (Gemini). Uses googleFirebaseCloudFirestore, googleGemini, slack. Scheduled trigger; 9 nodes.

Cron / scheduled trigger★★★★☆ complexityAI-powered9 nodesGoogle Firebase Cloud FirestoreGoogle GeminiSlack
AI & RAG Trigger: Cron / scheduled Nodes: 9 Complexity: ★★★★☆ AI nodes: yes Added:

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 →

Download .json
{
  "name": "Firestore \uc815\uc624 \uc694\uc57d \ub9ac\ud3ec\ud2b8 (Gemini)",
  "nodes": [
    {
      "parameters": {
        "triggerTimes": {
          "item": [
            {
              "mode": "everyDay",
              "hour": 12,
              "minute": 0
            }
          ]
        },
        "timezone": "Asia/Seoul"
      },
      "id": "cron-trigger",
      "name": "Cron (\ub9e4\uc77c 12:00 KST)",
      "type": "n8n-nodes-base.cron",
      "typeVersion": 1,
      "position": [
        260,
        260
      ]
    },
    {
      "parameters": {
        "functionCode": "// \uc624\ub298 00:00 KST ~ \ud604\uc7ac(\uc815\uc624) \uc2dc\uac04 \ubc94\uc704 \uacc4\uc0b0\nconst now = new Date();\nconst y = now.getFullYear();\nconst m = now.getMonth();\nconst d = now.getDate();\nconst start = new Date(y, m, d, 0, 0, 0);\nconst end = now;\n\n// \uc5b4\uc81c \uac19\uc740 \uc2dc\uac04\ub300\ub3c4 \uacc4\uc0b0 (\ube44\uad50\uc6a9)\nconst yesterday = new Date(start);\nyesterday.setDate(yesterday.getDate() - 1);\nconst yesterdayEnd = new Date(end);\nyesterdayEnd.setDate(yesterdayEnd.getDate() - 1);\n\nreturn [{\n  json: {\n    startISO: start.toISOString(),\n    endISO: end.toISOString(),\n    yesterdayStartISO: yesterday.toISOString(),\n    yesterdayEndISO: yesterdayEnd.toISOString(),\n    dateRange: `${start.toISOString().split('T')[0]} 00:00 ~ ${end.toISOString().split('T')[0]} 12:00`\n  }\n}];"
      },
      "id": "time-calculator",
      "name": "\uc2dc\uac04 \ubc94\uc704 \uacc4\uc0b0",
      "type": "n8n-nodes-base.function",
      "typeVersion": 2,
      "position": [
        520,
        260
      ]
    },
    {
      "parameters": {
        "operation": "getAll",
        "projectId": "what2eat-e21e3",
        "collection": "reports",
        "query": {
          "queryFieldsUi": {
            "queryFieldsValues": [
              {
                "field": "created_at",
                "operator": ">=",
                "value": "={{$json.startISO}}"
              },
              {
                "field": "created_at",
                "operator": "<=",
                "value": "={{$json.endISO}}"
              }
            ]
          }
        },
        "limit": 1000
      },
      "id": "firestore-today",
      "name": "Firestore: \uc624\ub298 \ub370\uc774\ud130 \uc870\ud68c",
      "type": "n8n-nodes-base.googleFirebaseCloudFirestore",
      "typeVersion": 1,
      "credentials": {
        "googleFirebaseCloudFirestoreOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "position": [
        780,
        200
      ]
    },
    {
      "parameters": {
        "operation": "getAll",
        "projectId": "what2eat-e21e3",
        "collection": "reports",
        "query": {
          "queryFieldsUi": {
            "queryFieldsValues": [
              {
                "field": "created_at",
                "operator": ">=",
                "value": "={{$json.yesterdayStartISO}}"
              },
              {
                "field": "created_at",
                "operator": "<=",
                "value": "={{$json.yesterdayEndISO}}"
              }
            ]
          }
        },
        "limit": 1000
      },
      "id": "firestore-yesterday",
      "name": "Firestore: \uc5b4\uc81c \ub370\uc774\ud130 \uc870\ud68c",
      "type": "n8n-nodes-base.googleFirebaseCloudFirestore",
      "typeVersion": 1,
      "credentials": {
        "googleFirebaseCloudFirestoreOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "position": [
        780,
        320
      ]
    },
    {
      "parameters": {
        "functionCode": "// \uc624\ub298\uacfc \uc5b4\uc81c \ub370\uc774\ud130\ub97c \uc9d1\uacc4\ud558\uace0 \ube44\uad50 \ubd84\uc11d\uc6a9 \ud14d\uc2a4\ud2b8 \uc0dd\uc131\nconst todayData = $input.first().json;\nconst yesterdayData = $input.last().json;\n\n// \uc624\ub298 \ub370\uc774\ud130 \uc9d1\uacc4\nconst todayItems = todayData || [];\nconst todayByCategory = {};\nlet todayTotalValue = 0;\n\nfor (const item of todayItems) {\n  const category = item.category || '\uae30\ud0c0';\n  const value = Number(item.value || 0);\n  \n  if (!todayByCategory[category]) {\n    todayByCategory[category] = { count: 0, sum: 0, items: [] };\n  }\n  \n  todayByCategory[category].count += 1;\n  todayByCategory[category].sum += value;\n  todayByCategory[category].items.push({\n    title: item.title || '\uc81c\ubaa9 \uc5c6\uc74c',\n    value: value,\n    created_at: item.created_at\n  });\n  todayTotalValue += value;\n}\n\n// \uc5b4\uc81c \ub370\uc774\ud130 \uc9d1\uacc4\nconst yesterdayItems = yesterdayData || [];\nconst yesterdayByCategory = {};\nlet yesterdayTotalValue = 0;\n\nfor (const item of yesterdayItems) {\n  const category = item.category || '\uae30\ud0c0';\n  const value = Number(item.value || 0);\n  \n  if (!yesterdayByCategory[category]) {\n    yesterdayByCategory[category] = { count: 0, sum: 0 };\n  }\n  \n  yesterdayByCategory[category].count += 1;\n  yesterdayByCategory[category].sum += value;\n  yesterdayTotalValue += value;\n}\n\n// \uac01 \uce74\ud14c\uace0\ub9ac\ubcc4 \uc0c1\uc704 \ud56d\ubaa9 \uc815\ub82c\nfor (const category of Object.keys(todayByCategory)) {\n  todayByCategory[category].items.sort((a, b) => b.value - a.value);\n  todayByCategory[category].top5 = todayByCategory[category].items.slice(0, 5);\n}\n\n// \uc804\uccb4 \uc0c1\uc704 \ud56d\ubaa9\nconst allTodayItems = [];\nfor (const categoryInfo of Object.values(todayByCategory)) {\n  allTodayItems.push(...categoryInfo.items);\n}\nallTodayItems.sort((a, b) => b.value - a.value);\n\n// \ubcc0\ud654\uc728 \uacc4\uc0b0\nconst valueChangePercent = yesterdayTotalValue > 0 ? \n  ((todayTotalValue - yesterdayTotalValue) / yesterdayTotalValue * 100).toFixed(1) : 0;\n\n// Gemini\uc5d0 \uc804\ub2ec\ud560 \ubd84\uc11d \ud14d\uc2a4\ud2b8 \uc0dd\uc131\nconst analysisText = `\ud83d\udcca **Firestore \ub370\uc774\ud130 \ubd84\uc11d \uc694\uccad**\n\n**\uae30\uac04**: ${$json.dateRange}\n**\ucd1d \ubb38\uc11c \uc218**: ${todayItems.length}\uac1c (\uc5b4\uc81c: ${yesterdayItems.length}\uac1c)\n**\ucd1d \uac12**: ${todayTotalValue.toFixed(2)} (\uc5b4\uc81c: ${yesterdayTotalValue.toFixed(2)}, \ubcc0\ud654: ${valueChangePercent}%)\n\n**\uce74\ud14c\uace0\ub9ac\ubcc4 \uc9d1\uacc4 (\uc624\ub298)**:\n` +\nObject.entries(todayByCategory).map(([category, info]) => {\n  const yesterdayInfo = yesterdayByCategory[category] || { count: 0, sum: 0 };\n  const countChange = yesterdayInfo.count > 0 ? \n    ((info.count - yesterdayInfo.count) / yesterdayInfo.count * 100).toFixed(1) : 'N/A';\n  const valueChange = yesterdayInfo.sum > 0 ? \n    ((info.sum - yesterdayInfo.sum) / yesterdayInfo.sum * 100).toFixed(1) : 'N/A';\n  \n  return `- **${category}**: \uac1c\uc218=${info.count} (${countChange}%), \ud569\uacc4=${info.sum.toFixed(2)} (${valueChange}%)\\n  \uc0c1\uc7045: ${info.top5.map(item => `${item.title}(${item.value})`).join(', ')}`;\n}).join('\\n') +\n`\\n\\n**\uc804\uccb4 \uc0c1\uc704 10\uac1c \ud56d\ubaa9**:\\n` +\nallTodayItems.slice(0, 10).map((item, i) => `${i+1}. ${item.title}: ${item.value}`).join('\\n') +\n`\\n\\n**\ud2b9\uc774\uc0ac\ud56d**:\\n- \ucd1d \uac12 \ubcc0\ud654: ${valueChangePercent}%\\n- \uac00\uc7a5 \ud65c\ubc1c\ud55c \uce74\ud14c\uace0\ub9ac: ${Object.entries(todayByCategory).sort((a,b) => b[1].count - a[1].count)[0]?.[0] || 'N/A'}\\n- \ucd5c\uace0\uac12 \ud56d\ubaa9: ${allTodayItems[0]?.title || 'N/A'} (${allTodayItems[0]?.value || 0})`;\n\nreturn [{\n  json: {\n    analysisText,\n    todayData: {\n      totalCount: todayItems.length,\n      totalValue: todayTotalValue,\n      categories: todayByCategory,\n      topItems: allTodayItems.slice(0, 10)\n    },\n    yesterdayData: {\n      totalCount: yesterdayItems.length,\n      totalValue: yesterdayTotalValue\n    },\n    comparison: {\n      valueChangePercent: parseFloat(valueChangePercent),\n      countChangePercent: yesterdayItems.length > 0 ? \n        ((todayItems.length - yesterdayItems.length) / yesterdayItems.length * 100).toFixed(1) : 'N/A'\n    }\n  }\n}];"
      },
      "id": "data-aggregator",
      "name": "\ub370\uc774\ud130 \uc9d1\uacc4 & \ubd84\uc11d \ud14d\uc2a4\ud2b8 \uc0dd\uc131",
      "type": "n8n-nodes-base.function",
      "typeVersion": 2,
      "position": [
        1040,
        260
      ]
    },
    {
      "parameters": {
        "model": "gemini-pro",
        "options": {
          "temperature": 0.2,
          "maxTokens": 2000
        },
        "systemMessage": "\ub2f9\uc2e0\uc740 \ub370\uc774\ud130 \ubd84\uc11d \uc804\ubb38\uac00\uc785\ub2c8\ub2e4. Firestore \ub370\uc774\ud130\ub97c \ubd84\uc11d\ud558\uc5ec \uba85\ud655\ud558\uace0 \uc2e4\ud589 \uac00\ub2a5\ud55c \uc778\uc0ac\uc774\ud2b8\uac00 \ud3ec\ud568\ub41c \ud55c\uad6d\uc5b4 \ub9ac\ud3ec\ud2b8\ub97c \uc791\uc131\ud574\uc8fc\uc138\uc694. \ub9c8\ud06c\ub2e4\uc6b4 \ud615\uc2dd\uc744 \uc0ac\uc6a9\ud558\uace0, \uad6c\uccb4\uc801\uc778 \uc218\uce58\uc640 \ubcc0\ud654\uc728\uc744 \ud3ec\ud568\ud558\uc5ec \uc804\ubb38\uc801\uc778 \ubd84\uc11d\uc744 \uc81c\uacf5\ud574\uc8fc\uc138\uc694.",
        "text": "={{$json.analysisText}}\n\n\uc704 \ub370\uc774\ud130\ub97c \ubc14\ud0d5\uc73c\ub85c \ub2e4\uc74c\uc744 \ud3ec\ud568\ud55c \uc815\uc624 \uc694\uc57d \ub9ac\ud3ec\ud2b8\ub97c \uc791\uc131\ud574\uc8fc\uc138\uc694:\n1. \uc8fc\uc694 \ud2b8\ub80c\ub4dc \ubc0f \ubcc0\ud654\n2. \uce74\ud14c\uace0\ub9ac\ubcc4 \uc0c1\uc138 \ubd84\uc11d\n3. \ud2b9\uc774\uc0ac\ud56d \ubc0f \uc774\uc0c1 \uc9d5\ud6c4\n4. \uc2e4\ud589 \uac00\ub2a5\ud55c \uc778\uc0ac\uc774\ud2b8 3\uac00\uc9c0\n5. \ub0b4\uc77c\uc744 \uc704\ud55c \uad8c\uc7a5\uc0ac\ud56d\n\n\ub9c8\ud06c\ub2e4\uc6b4 \ud615\uc2dd\uc73c\ub85c \uc791\uc131\ud574\uc8fc\uc138\uc694."
      },
      "id": "gemini-analyzer",
      "name": "Gemini AI \ubd84\uc11d",
      "type": "n8n-nodes-base.googleGemini",
      "typeVersion": 1,
      "credentials": {
        "googleGeminiApi": {
          "name": "<your credential>"
        }
      },
      "position": [
        1300,
        260
      ]
    },
    {
      "parameters": {
        "functionCode": "// \ucd5c\uc885 \ub9ac\ud3ec\ud2b8 \ud3ec\ub9f7\ud305\nconst geminiAnalysis = $json.text || $json.data || '\ubd84\uc11d \uacb0\uacfc\ub97c \uac00\uc838\uc62c \uc218 \uc5c6\uc2b5\ub2c8\ub2e4.';\nconst aggregatedData = $('\ub370\uc774\ud130 \uc9d1\uacc4 & \ubd84\uc11d \ud14d\uc2a4\ud2b8 \uc0dd\uc131').first().json;\nconst timeInfo = $('\uc2dc\uac04 \ubc94\uc704 \uacc4\uc0b0').first().json;\n\nconst finalReport = `\ud83d\udcca **Firestore \uc815\uc624 \uc694\uc57d \ub9ac\ud3ec\ud2b8**\n\ud83d\udcc5 **\ubd84\uc11d \uae30\uac04**: ${timeInfo.dateRange}\n\ud83d\udcc8 **\ub370\uc774\ud130 \ud604\ud669**: \ucd1d ${aggregatedData.todayData.totalCount}\uac1c \ubb38\uc11c, \ucd1d \uac12 ${aggregatedData.todayData.totalValue.toFixed(2)}\n\ud83d\udcca **\uc804\uc77c \ub300\ube44**: \uac12 ${aggregatedData.comparison.valueChangePercent}% \ubcc0\ud654, \ubb38\uc11c \uc218 ${aggregatedData.comparison.countChangePercent}% \ubcc0\ud654\n\n---\n\n${geminiAnalysis}\n\n---\n\n\ud83d\udccb **\uc694\uc57d \ud1b5\uacc4**:\n- \ubd84\uc11d \uc2dc\uac04: ${new Date().toLocaleString('ko-KR', { timeZone: 'Asia/Seoul' })}\n- \ub370\uc774\ud130 \uc18c\uc2a4: Firestore \uceec\ub809\uc158 'reports'\n- \ubd84\uc11d \ub3c4\uad6c: Google Gemini AI`;\n\nreturn [{\n  json: {\n    finalReport,\n    summary: {\n      timestamp: new Date().toISOString(),\n      dataCount: aggregatedData.todayData.totalCount,\n      totalValue: aggregatedData.todayData.totalValue,\n      changePercent: aggregatedData.comparison.valueChangePercent\n    }\n  }\n}];"
      },
      "id": "report-formatter",
      "name": "\ucd5c\uc885 \ub9ac\ud3ec\ud2b8 \ud3ec\ub9f7\ud305",
      "type": "n8n-nodes-base.function",
      "typeVersion": 2,
      "position": [
        1560,
        260
      ]
    },
    {
      "parameters": {
        "authentication": "accessToken",
        "channel": "#daily-reports",
        "text": "={{$json.finalReport}}",
        "otherOptions": {
          "thread_ts": "",
          "reply_broadcast": false
        }
      },
      "id": "slack-notification",
      "name": "Slack \uc54c\ub9bc \uc804\uc1a1",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2,
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "position": [
        1820,
        260
      ]
    },
    {
      "parameters": {
        "authentication": "accessToken",
        "channel": "#daily-reports",
        "text": "\u274c **Firestore \uc815\uc624 \uc694\uc57d \uc2e4\ud328**\n\n\uc6cc\ud06c\ud50c\ub85c\uc6b0 \uc2e4\ud589 \uc911 \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4.\n\n**\uc624\ub958 \uc815\ubcf4**:\n- \uc2dc\uac04: {{new Date().toLocaleString('ko-KR', { timeZone: 'Asia/Seoul' })}}\n- \ub178\ub4dc: {{$node.name}}\n- \uc624\ub958: {{$json.error.message || '\uc54c \uc218 \uc5c6\ub294 \uc624\ub958'}}\n\n\uad00\ub9ac\uc790\uc5d0\uac8c \ubb38\uc758\ud574\uc8fc\uc138\uc694.",
        "otherOptions": {}
      },
      "id": "slack-error",
      "name": "Slack \uc624\ub958 \uc54c\ub9bc",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2,
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "position": [
        1300,
        400
      ]
    }
  ],
  "connections": {
    "Cron (\ub9e4\uc77c 12:00 KST)": {
      "main": [
        [
          {
            "node": "\uc2dc\uac04 \ubc94\uc704 \uacc4\uc0b0",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\uc2dc\uac04 \ubc94\uc704 \uacc4\uc0b0": {
      "main": [
        [
          {
            "node": "Firestore: \uc624\ub298 \ub370\uc774\ud130 \uc870\ud68c",
            "type": "main",
            "index": 0
          },
          {
            "node": "Firestore: \uc5b4\uc81c \ub370\uc774\ud130 \uc870\ud68c",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Firestore: \uc624\ub298 \ub370\uc774\ud130 \uc870\ud68c": {
      "main": [
        [
          {
            "node": "\ub370\uc774\ud130 \uc9d1\uacc4 & \ubd84\uc11d \ud14d\uc2a4\ud2b8 \uc0dd\uc131",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Firestore: \uc5b4\uc81c \ub370\uc774\ud130 \uc870\ud68c": {
      "main": [
        [
          {
            "node": "\ub370\uc774\ud130 \uc9d1\uacc4 & \ubd84\uc11d \ud14d\uc2a4\ud2b8 \uc0dd\uc131",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ub370\uc774\ud130 \uc9d1\uacc4 & \ubd84\uc11d \ud14d\uc2a4\ud2b8 \uc0dd\uc131": {
      "main": [
        [
          {
            "node": "Gemini AI \ubd84\uc11d",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gemini AI \ubd84\uc11d": {
      "main": [
        [
          {
            "node": "\ucd5c\uc885 \ub9ac\ud3ec\ud2b8 \ud3ec\ub9f7\ud305",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ucd5c\uc885 \ub9ac\ud3ec\ud2b8 \ud3ec\ub9f7\ud305": {
      "main": [
        [
          {
            "node": "Slack \uc54c\ub9bc \uc804\uc1a1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "staticData": null,
  "tags": [
    {
      "createdAt": "2024-01-01T00:00:00.000Z",
      "updatedAt": "2024-01-01T00:00:00.000Z",
      "id": "firestore-summary",
      "name": "Firestore Summary"
    }
  ],
  "triggerCount": 1,
  "updatedAt": "2024-01-01T00:00:00.000Z",
  "versionId": "1"
}

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.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

Firestore 정오 요약 리포트 (Gemini). Uses googleFirebaseCloudFirestore, googleGemini, slack. Scheduled trigger; 9 nodes.

Source: https://github.com/LearningnRunning/TIL/blob/69d748ad3e15ae05c1da0e9fe0d92334067e4bb2/workflow/firestore-summary/n8n_workflow.json — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

The AI Blog Creator with Gemini, Replicate Image, Supabase Publishing & Slack is a fully automated content generation and publishing workflow designed for modern marketing and SaaS teams. It automatic

HTTP Request, Slack, Google Gemini
AI & RAG

This scheduled workflow pulls the latest AI-related articles from NewsAPI, uses Google Gemini to filter and draft a LinkedIn post plus image prompt, generates an image with OpenAI, uploads the image a

HTTP Request, Google Gemini, Gmail +2
AI & RAG

This workflow runs daily to compare procurement transactions in Google Sheets against an approved supplier list, flags unapproved (maverick) spend, uses Google Gemini to generate compliant alternative

Google Sheets, Google Gemini, Slack +1
AI & RAG

This workflow runs daily to compare department budgets and actual spend from Google Sheets, calculates variance, uses Google Gemini to generate root-cause insights, sends Slack alerts when variance br

Gmail, Google Sheets, Google Gemini +1
AI & RAG

Automatically monitors restaurant ratings on Google Places daily, detects meaningful changes, uses Google Gemini AI to diagnose the root cause from real customer reviews, and delivers smart alerts to

Google Sheets, HTTP Request, Google Gemini +1