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

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
AI & RAG

For team leads, HR, and managers who want to monitor the emotional tone and morale of their teams based on message sentiment. Trigger: Runs every Monday at 9 AM. Config: Defines your Teams and Slack c

Google Gemini, Slack
AI & RAG

This workflow automatically generates a 6-slide Instagram carousel about tech topics(you can edit the prompt how you like to make it for best for your usecase) every day at noon(you can set whatever t

Google Gemini, Slack, Google Sheets
AI & RAG

AI Institutional Stock Valuation Engine with Risk Scoring & Scenario Targets

Google Sheets, XML, HTTP Request +3