AutomationFlowsAI & RAG › Gmail to Vector Embeddings with Ollama & Pgvector

Gmail to Vector Embeddings with Ollama & Pgvector

Original n8n title: Gmail to Vector Embeddings with Pgvector and Ollama

Gmail to Vector Embeddings with PGVector and Ollama. Uses embeddingsOllama, documentDefaultDataLoader, textSplitterRecursiveCharacterTextSplitter, gmailTrigger. Event-driven trigger; 20 nodes.

Event trigger★★★★☆ complexityAI-powered20 nodesOllama EmbeddingsDocument Default Data LoaderText Splitter Recursive Character Text SplitterGmail TriggerPostgresVector Store PgvectorGmail
AI & RAG Trigger: Event Nodes: 20 Complexity: ★★★★☆ AI nodes: yes Added:

This workflow follows the Documentdefaultdataloader → Gmail 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 →

Download .json
{
  "id": "ZiIoKEClTk83g1Jt",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Gmail to Vector Embeddings with PGVector and Ollama",
  "tags": [],
  "nodes": [
    {
      "id": "162b1a8b-2471-4880-9fcb-7f2dcfe175a8",
      "name": "Embeddings Ollama",
      "type": "@n8n/n8n-nodes-langchain.embeddingsOllama",
      "position": [
        1920,
        -100
      ],
      "parameters": {
        "model": "nomic-embed-text:latest"
      },
      "credentials": {},
      "typeVersion": 1
    },
    {
      "id": "49eb04b0-3b54-499c-ba46-3251102a4017",
      "name": "Default Data Loader",
      "type": "@n8n/n8n-nodes-langchain.documentDefaultDataLoader",
      "position": [
        2040,
        -97.5
      ],
      "parameters": {
        "options": {
          "metadata": {
            "metadataValues": [
              {
                "name": "emails_metadata.id",
                "value": "={{ $('Extract email fields').item.json.email_id }}"
              },
              {
                "name": "emails_metadata.thread_id",
                "value": "={{ $('Extract email fields').item.json.thread_id }}"
              }
            ]
          }
        },
        "jsonData": "={{ $('Extract email fields').item.json.email_text }}",
        "jsonMode": "expressionData"
      },
      "typeVersion": 1
    },
    {
      "id": "b4853472-6ac7-4da5-97b3-b22950ddff06",
      "name": "Recursive Character Text Splitter",
      "type": "@n8n/n8n-nodes-langchain.textSplitterRecursiveCharacterTextSplitter",
      "position": [
        2128,
        100
      ],
      "parameters": {
        "options": {},
        "chunkSize": 2000,
        "chunkOverlap": 50
      },
      "typeVersion": 1
    },
    {
      "id": "b189f134-f78e-438f-9189-2f2b276b487d",
      "name": "Gmail Trigger",
      "type": "n8n-nodes-base.gmailTrigger",
      "position": [
        1260,
        280
      ],
      "parameters": {
        "simple": false,
        "filters": {
          "labelIds": [
            "INBOX"
          ]
        },
        "options": {
          "downloadAttachments": true
        },
        "pollTimes": {
          "item": [
            {
              "mode": "everyMinute"
            }
          ]
        }
      },
      "credentials": {},
      "typeVersion": 1.2
    },
    {
      "id": "81cba4c5-7762-483d-a076-3fa8799f70ce",
      "name": "Loop Over Items",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        840,
        40
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "f82243ad-6efd-4be2-bf4e-5001870ae854",
      "name": "Split Out",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        640,
        40
      ],
      "parameters": {
        "options": {
          "destinationFieldName": "after"
        },
        "fieldToSplitOut": "weeks"
      },
      "typeVersion": 1
    },
    {
      "id": "2163d5ec-416f-4299-8a9d-10c26eaef32f",
      "name": "Was manually triggered?",
      "type": "n8n-nodes-base.if",
      "position": [
        2416,
        -145
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "3cbc77e7-1796-4e1b-bbff-6391dd131336",
              "operator": {
                "type": "boolean",
                "operation": "false",
                "singleValue": true
              },
              "leftValue": "={{ $('Manual Trigger').isExecuted }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "76557325-c94e-47a9-9384-e6cbea94f67e",
      "name": "Manual Trigger",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        0,
        40
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "b9400906-a458-4305-8805-bb6bea17396b",
      "name": "No Operation, do nothing",
      "type": "n8n-nodes-base.noOp",
      "position": [
        2636,
        -145
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "5f0aa7c2-85b3-4585-8c8c-727af27de61c",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -60,
        -540
      ],
      "parameters": {
        "color": 6,
        "width": 1440,
        "height": 780,
        "content": "## Bulk e-mail import\n\nPress the `Test workflow` button to run this once, and bulk import of all your e-mail\n\n### IMPORTANT\nSpecify your Gmail account creation date by editing the code node"
      },
      "typeVersion": 1
    },
    {
      "id": "2b85d362-d40d-49c0-b3a7-27fdbee8e90b",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        360,
        -100
      ],
      "parameters": {
        "width": 220,
        "height": 300,
        "content": "## Edit this \u2b07\ufe0f\nAnd specify your Gmail account creation date"
      },
      "typeVersion": 1
    },
    {
      "id": "05a9dd25-ae36-4e3c-a249-787ee1047bff",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        740,
        260
      ],
      "parameters": {
        "color": 4,
        "width": 640,
        "height": 180,
        "content": "## Activate the workflow\nAnd this trigger will check for new mail, every minute"
      },
      "typeVersion": 1
    },
    {
      "id": "3f19abc5-a165-49e8-b97e-233c47949e68",
      "name": "Set before and after dates",
      "type": "n8n-nodes-base.set",
      "position": [
        1040,
        -320
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "48e8d703-e52a-46cc-bd72-9b0d3352091b",
              "name": "after",
              "type": "string",
              "value": "={{ $json.after }}"
            },
            {
              "id": "a515cf56-9bc6-4724-a0ef-01a6159606f7",
              "name": "before",
              "type": "string",
              "value": "={{ DateTime.fromISO($json.after).plus(1, 'week').toISODate() }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "af742b17-2086-4698-af3a-32cb7260f380",
      "name": "Extract email fields",
      "type": "n8n-nodes-base.set",
      "position": [
        1480,
        -320
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "f818bad8-b000-499c-b137-de22dff4a343",
              "name": "email_text",
              "type": "string",
              "value": "={{ $json.text }}"
            },
            {
              "id": "68c16520-4a26-4ea9-95f7-ee89b9f53c4f",
              "name": "email_from",
              "type": "string",
              "value": "={{ $json.from?.text ?? '' }}"
            },
            {
              "id": "981f1f5b-ba2f-4153-966c-45bb6b535794",
              "name": "email_to",
              "type": "string",
              "value": "={{ $json.to?.text ?? '' }}"
            },
            {
              "id": "b528dd23-a743-4a55-98df-e1ae823b29b3",
              "name": "date",
              "type": "string",
              "value": "={{ DateTime.fromISO($json.date).toISO() }}"
            },
            {
              "id": "39081032-e503-470b-8d83-b5064238d037",
              "name": "email_id",
              "type": "string",
              "value": "={{ $json.id }}"
            },
            {
              "id": "146e8e72-3c2c-4320-b93a-b109d2e46139",
              "name": "thread_id",
              "type": "string",
              "value": "={{ $json.threadId }}"
            },
            {
              "id": "a49333a5-c565-4d46-8398-d423072b1e4d",
              "name": "email_subject",
              "type": "string",
              "value": "={{ $json.subject }}"
            },
            {
              "id": "806cf930-450e-4221-8061-a71ec8bf9bbe",
              "name": "attachments",
              "type": "array",
              "value": "={{ Object.keys($binary).map(item => $binary[item].fileName).filter(item => !!item) }}"
            },
            {
              "id": "30a38aaf-04c2-4286-99c9-8bb60ae8b317",
              "name": "email_cc",
              "type": "string",
              "value": "={{ $json.cc?.text ?? ''}}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "a51f5d5f-69c7-4153-be7f-492a8694629a",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1640,
        -540
      ],
      "parameters": {
        "width": 720,
        "height": 780,
        "content": "## Magic here \ud83e\ude84\n#### (not really, just statistics)\nE-mail is stored in a `emails_metadata` structured table, and also fed to the [`nomic-embed-text`](https://ollama.com/library/nomic-embed-text) model to be stored in a `emails_embeddings` table as [vector embeddings](https://www.pinecone.io/learn/vector-embeddings/) so similarity searches are possible.\n\nThe `email_id` field can be used to make the relation between the structured records and the vector embeddings, as it's stored in their metadata as `emails_metadata.id`.\nThis is also the case for `thread_id`."
      },
      "typeVersion": 1
    },
    {
      "id": "809e9269-1275-4c87-8c7f-1840c76f5b22",
      "name": "Store structured",
      "type": "n8n-nodes-base.postgres",
      "onError": "continueErrorOutput",
      "position": [
        1700,
        -320
      ],
      "parameters": {
        "table": {
          "__rl": true,
          "mode": "name",
          "value": "emails_metadata"
        },
        "schema": {
          "__rl": true,
          "mode": "list",
          "value": "public"
        },
        "columns": {
          "value": {},
          "schema": [
            {
              "id": "email_id",
              "type": "string",
              "display": true,
              "removed": false,
              "required": true,
              "displayName": "email_id",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "thread_id",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "thread_id",
              "defaultMatch": false,
              "canBeUsedToMatch": false
            },
            {
              "id": "email_from",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "email_from",
              "defaultMatch": false,
              "canBeUsedToMatch": false
            },
            {
              "id": "email_to",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "email_to",
              "defaultMatch": false,
              "canBeUsedToMatch": false
            },
            {
              "id": "email_cc",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "email_cc",
              "defaultMatch": false,
              "canBeUsedToMatch": false
            },
            {
              "id": "date",
              "type": "dateTime",
              "display": true,
              "removed": false,
              "required": true,
              "displayName": "date",
              "defaultMatch": false,
              "canBeUsedToMatch": false
            },
            {
              "id": "email_subject",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "email_subject",
              "defaultMatch": false,
              "canBeUsedToMatch": false
            },
            {
              "id": "attachments",
              "type": "array",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "attachments",
              "defaultMatch": false,
              "canBeUsedToMatch": false
            },
            {
              "id": "email_text",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "email_text",
              "defaultMatch": false,
              "canBeUsedToMatch": false
            }
          ],
          "mappingMode": "autoMapInputData",
          "matchingColumns": [
            "email_id"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {
          "outputColumns": [
            "*"
          ]
        },
        "operation": "upsert"
      },
      "credentials": {},
      "typeVersion": 2.6
    },
    {
      "id": "1c3dca79-381c-411b-8727-baa297e1ceda",
      "name": "Store vectorized",
      "type": "@n8n/n8n-nodes-langchain.vectorStorePGVector",
      "onError": "continueRegularOutput",
      "position": [
        1936,
        -320
      ],
      "parameters": {
        "mode": "insert",
        "options": {},
        "tableName": "emails_embeddings"
      },
      "credentials": {},
      "typeVersion": 1.1
    },
    {
      "id": "3b7e13b2-73e9-42e7-900c-59611fe5af32",
      "name": "Create the table",
      "type": "n8n-nodes-base.postgres",
      "position": [
        200,
        40
      ],
      "parameters": {
        "query": "CREATE TABLE IF NOT EXISTS public.emails_metadata (\n    email_id character varying(64) NOT NULL,\n    thread_id character varying(64),\n    email_from text,\n    email_to text,\n    email_cc text,\n    date timestamp with time zone NOT NULL,\n    email_subject text,\n    email_text text,\n    attachments text[]\n);\n",
        "options": {},
        "operation": "executeQuery"
      },
      "credentials": {},
      "typeVersion": 2.6
    },
    {
      "id": "19c55312-d1da-4d1e-8637-c5b08a9c1a2d",
      "name": "Explode interval into weeks",
      "type": "n8n-nodes-base.code",
      "position": [
        420,
        40
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Edit this\nlet whenDidICreateMyGmailAccount = DateTime.fromISO('2013-11-01')\n\n// (don't edit further down)\nwhenDidICreateMyGmailAccount = whenDidICreateMyGmailAccount.set({day: 1})\nlet now = $now.set({day: 1})\nconst weeks = []\nwhile (Math.floor(Interval.fromDateTimes(whenDidICreateMyGmailAccount, now).length('weeks')) > -1) {\n  weeks.push(now.toISODate())\n  now = now.minus({weeks: 1})\n}\n\nreturn {json: { weeks }};"
      },
      "typeVersion": 2
    },
    {
      "id": "aed43a77-6d58-41ba-b0b0-fdd3e9fe777a",
      "name": "Get a batch of messages",
      "type": "n8n-nodes-base.gmail",
      "position": [
        1260,
        -320
      ],
      "parameters": {
        "simple": false,
        "filters": {
          "receivedAfter": "={{ $json.after }}",
          "receivedBefore": "={{ $json.before }}"
        },
        "options": {
          "downloadAttachments": true
        },
        "operation": "getAll",
        "returnAll": true
      },
      "credentials": {},
      "typeVersion": 2.1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "3c337d42-a3bb-4b71-ac36-deaf0cdf6019",
  "connections": {
    "Split Out": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gmail Trigger": {
      "main": [
        [
          {
            "node": "Extract email fields",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Manual Trigger": {
      "main": [
        [
          {
            "node": "Create the table",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Over Items": {
      "main": [
        [],
        [
          {
            "node": "Set before and after dates",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create the table": {
      "main": [
        [
          {
            "node": "Explode interval into weeks",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Store structured": {
      "main": [
        [
          {
            "node": "Store vectorized",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Store vectorized": {
      "main": [
        [
          {
            "node": "Was manually triggered?",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    },
    "Embeddings Ollama": {
      "ai_embedding": [
        [
          {
            "node": "Store vectorized",
            "type": "ai_embedding",
            "index": 0
          }
        ]
      ]
    },
    "Default Data Loader": {
      "ai_document": [
        [
          {
            "node": "Store vectorized",
            "type": "ai_document",
            "index": 0
          }
        ]
      ]
    },
    "Extract email fields": {
      "main": [
        [
          {
            "node": "Store structured",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get a batch of messages": {
      "main": [
        [
          {
            "node": "Extract email fields",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Was manually triggered?": {
      "main": [
        [
          {
            "node": "No Operation, do nothing",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set before and after dates": {
      "main": [
        [
          {
            "node": "Get a batch of messages",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Explode interval into weeks": {
      "main": [
        [
          {
            "node": "Split Out",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Recursive Character Text Splitter": {
      "ai_textSplitter": [
        [
          {
            "node": "Default Data Loader",
            "type": "ai_textSplitter",
            "index": 0
          }
        ]
      ]
    }
  }
}
Pro

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

How this works

This workflow transforms incoming Gmail emails into searchable vector embeddings, enabling efficient retrieval and analysis of your correspondence without manual organisation. It's ideal for knowledge workers, researchers, or teams handling high email volumes who want to leverage AI for quick insights, such as summarising discussions or finding relevant threads. The key step involves the Gmail trigger capturing new messages, followed by Ollama generating embeddings stored in a Pgvector-enabled PostgreSQL database for semantic search.

Use this when you need real-time processing of emails for AI-driven queries, like in customer support or personal archiving, but avoid it for non-text attachments or if your email volume exceeds thousands daily without scaling resources. It's not suited for one-off analyses where simpler tools like email filters suffice. Common variations include adding sentiment analysis via additional AI nodes or integrating with Slack for notifications on key email matches.

About this workflow

Gmail to Vector Embeddings with PGVector and Ollama. Uses embeddingsOllama, documentDefaultDataLoader, textSplitterRecursiveCharacterTextSplitter, gmailTrigger. Event-driven trigger; 20 nodes.

Source: https://github.com/Zie619/n8n-workflows — 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

AI Email Agent - Complete System. Uses gmailTrigger, gmail, vectorStorePGVector, embeddingsOpenAi. Event-driven trigger; 47 nodes.

Gmail Trigger, Gmail, Vector Store Pgvector +8
AI & RAG

This workflow is ideal for: Professionals Project managers Sales and support teams Anyone managing high volumes of Gmail messages

Gmail Trigger, Gmail, Text Splitter Recursive Character Text Splitter +6
AI & RAG

My Workflow. Uses outputParserStructured, httpRequest, lmChatGoogleGemini, chainLlm. Scheduled trigger; 82 nodes.

Output Parser Structured, HTTP Request, Google Gemini Chat +15
AI & RAG

Your AI workforce is ready. Are you?

Google Sheets Tool, Mcp Trigger, Google Drive +29
AI & RAG

Agent IA Projet Client. Uses executeWorkflowTrigger, lmChatOpenAi, toolWorkflow, vectorStoreQdrant. Event-driven trigger; 79 nodes.

Execute Workflow Trigger, OpenAI Chat, Tool Workflow +16