This workflow follows the Documentdefaultdataloader → Textsplitterrecursivecharactertextsplitter 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": "AppFlowy Content Sync to Vector Store",
"nodes": [
{
"parameters": {
"pollTimes": {
"item": [
{
"mode": "everyMinute"
}
]
},
"triggerOn": "specificDatabase",
"databaseId": "{{ $vars.appflowy_main_database_id }}",
"event": "rowCreated",
"options": {}
},
"id": "appflowy-row-created-trigger",
"name": "AppFlowy Row Created",
"type": "n8n-nodes-appflowy.trigger",
"typeVersion": 1,
"position": [
600,
200
],
"credentials": {
"appFlowyApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"pollTimes": {
"item": [
{
"mode": "everyMinute"
}
]
},
"triggerOn": "specificDatabase",
"databaseId": "{{ $vars.appflowy_main_database_id }}",
"event": "rowUpdated",
"options": {}
},
"id": "appflowy-row-updated-trigger",
"name": "AppFlowy Row Updated",
"type": "n8n-nodes-appflowy.trigger",
"typeVersion": 1,
"position": [
600,
400
],
"credentials": {
"appFlowyApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "content-extraction",
"name": "content",
"value": "={{ $json.title ? $json.title + '\\n\\n' + ($json.content || $json.description || '') : ($json.content || $json.description || 'No content available') }}",
"type": "string"
},
{
"id": "metadata-creation",
"name": "metadata",
"value": "={{ { source: 'appflowy', type: 'database_row', database_id: $json.database_id, row_id: $json.id, workspace_id: $json.workspace_id, created_at: $json.created_at, updated_at: $json.updated_at, title: $json.title } }}",
"type": "object"
},
{
"id": "document-id",
"name": "document_id",
"value": "={{ 'appflowy_' + $json.database_id + '_' + $json.id }}",
"type": "string"
}
]
},
"options": {}
},
"id": "process-appflowy-content",
"name": "Process AppFlowy Content",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
900,
300
]
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "clear-existing",
"name": "action",
"value": "clear_existing",
"type": "string"
},
{
"id": "document-id-clear",
"name": "document_id",
"value": "={{ $('Process AppFlowy Content').item.json.document_id }}",
"type": "string"
}
]
},
"options": {}
},
"id": "prepare-vector-clear",
"name": "Prepare Vector Clear",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
1150,
300
]
},
{
"parameters": {
"code": {
"execute": {
"code": "const { QdrantVectorStore } = require(\"@langchain/qdrant\");\nconst { OllamaEmbeddings } = require(\"@langchain/community/embeddings/ollama\");\n\nconst embeddings = new OllamaEmbeddings({\n model: \"nomic-embed-text\",\n baseUrl: \"http://ollama:11434\"\n});\n\nconst vectorStore = await QdrantVectorStore.fromExistingCollection(\n embeddings,\n {\n url: \"http://qdrant:6333\",\n collectionName: \"knowledge_base\",\n }\n);\n\nconst documentIdToDelete = this.getInputData()[0].json.document_id;\n\nconst filter = {\n must: [\n {\n key: \"metadata.document_id\",\n match: {\n value: documentIdToDelete,\n },\n },\n ],\n};\n\n// Delete existing vectors for this document\nvectorStore.client.delete(\"knowledge_base\", {\n filter\n});\n\nreturn [{ json: { document_id: documentIdToDelete, status: \"cleared\" } }];"
}
},
"inputs": {
"input": [
{
"type": "main",
"required": true
}
]
},
"outputs": {
"output": [
{
"type": "main"
}
]
}
},
"id": "clear-existing-vectors",
"name": "Clear Existing Vectors",
"type": "@n8n/n8n-nodes-langchain.code",
"typeVersion": 1,
"position": [
1400,
300
]
},
{
"parameters": {
"chunkSize": 500,
"chunkOverlap": 50,
"options": {}
},
"id": "text-splitter",
"name": "Recursive Character Text Splitter",
"type": "@n8n/n8n-nodes-langchain.textSplitterRecursiveCharacterTextSplitter",
"typeVersion": 1,
"position": [
1650,
500
]
},
{
"parameters": {
"options": {
"metadata": {
"metadataValues": [
{
"name": "source",
"value": "={{ $('Process AppFlowy Content').item.json.metadata.source }}"
},
{
"name": "type",
"value": "={{ $('Process AppFlowy Content').item.json.metadata.type }}"
},
{
"name": "database_id",
"value": "={{ $('Process AppFlowy Content').item.json.metadata.database_id }}"
},
{
"name": "row_id",
"value": "={{ $('Process AppFlowy Content').item.json.metadata.row_id }}"
},
{
"name": "document_id",
"value": "={{ $('Process AppFlowy Content').item.json.document_id }}"
},
{
"name": "workspace_id",
"value": "={{ $('Process AppFlowy Content').item.json.metadata.workspace_id }}"
},
{
"name": "title",
"value": "={{ $('Process AppFlowy Content').item.json.metadata.title }}"
},
{
"name": "updated_at",
"value": "={{ $('Process AppFlowy Content').item.json.metadata.updated_at }}"
}
]
}
}
},
"id": "document-loader",
"name": "Default Data Loader",
"type": "@n8n/n8n-nodes-langchain.documentDefaultDataLoader",
"typeVersion": 1,
"position": [
1650,
300
]
},
{
"parameters": {
"model": "nomic-embed-text:latest"
},
"id": "embeddings-ollama",
"name": "Embeddings Ollama",
"type": "@n8n/n8n-nodes-langchain.embeddingsOllama",
"typeVersion": 1,
"position": [
1900,
300
],
"credentials": {
"ollamaApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"mode": "insert",
"qdrantCollection": {
"__rl": true,
"value": "knowledge_base",
"mode": "list",
"cachedResultName": "knowledge_base"
},
"options": {}
},
"id": "qdrant-vector-store",
"name": "Qdrant Vector Store Insert",
"type": "@n8n/n8n-nodes-langchain.vectorStoreQdrant",
"typeVersion": 1,
"position": [
2150,
300
],
"credentials": {
"qdrantApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "upsert",
"table": {
"__rl": true,
"value": "knowledge_sync_log",
"mode": "list"
},
"data": {
"insert": [
{
"column": "document_id",
"value": "={{ $('Process AppFlowy Content').item.json.document_id }}"
},
{
"column": "source_system",
"value": "appflowy"
},
{
"column": "source_id",
"value": "={{ $('Process AppFlowy Content').item.json.metadata.row_id }}"
},
{
"column": "content_hash",
"value": "={{ $crypto.createHash('md5').update($('Process AppFlowy Content').item.json.content).digest('hex') }}"
},
{
"column": "metadata",
"value": "={{ $('Process AppFlowy Content').item.json.metadata }}"
},
{
"column": "sync_timestamp",
"value": "={{ $now }}"
},
{
"column": "status",
"value": "synced"
}
]
},
"options": {}
},
"id": "log-sync-status",
"name": "Log Sync Status",
"type": "@n8n/n8n-nodes-langchain.vectorStoreSupabase",
"typeVersion": 1,
"position": [
2400,
300
],
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"content": "## AppFlowy Content Sync to Vector Store\n\n**Purpose:** Automatically sync AppFlowy database content to the central knowledge vector store.\n\n**Triggers:**\n- New row created in AppFlowy database\n- Existing row updated in AppFlowy database\n\n**Process:**\n1. Extract content and metadata from AppFlowy\n2. Clear existing vectors for updated content\n3. Split content into chunks\n4. Generate embeddings using Ollama\n5. Store in Qdrant vector database\n6. Log sync status to Supabase",
"height": 400,
"width": 600,
"color": 4
},
"id": "workflow-documentation",
"name": "Workflow Documentation",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
200,
100
]
}
],
"connections": {
"AppFlowy Row Created": {
"main": [
[
{
"node": "Process AppFlowy Content",
"type": "main",
"index": 0
}
]
]
},
"AppFlowy Row Updated": {
"main": [
[
{
"node": "Process AppFlowy Content",
"type": "main",
"index": 0
}
]
]
},
"Process AppFlowy Content": {
"main": [
[
{
"node": "Prepare Vector Clear",
"type": "main",
"index": 0
}
]
]
},
"Prepare Vector Clear": {
"main": [
[
{
"node": "Clear Existing Vectors",
"type": "main",
"index": 0
}
]
]
},
"Clear Existing Vectors": {
"main": [
[
{
"node": "Document Loader",
"type": "main",
"index": 0
}
]
]
},
"Recursive Character Text Splitter": {
"ai_textSplitter": [
[
{
"node": "Default Data Loader",
"type": "ai_textSplitter",
"index": 0
}
]
]
},
"Default Data Loader": {
"ai_document": [
[
{
"node": "Qdrant Vector Store Insert",
"type": "ai_document",
"index": 0
}
]
]
},
"Embeddings Ollama": {
"ai_embedding": [
[
{
"node": "Qdrant Vector Store Insert",
"type": "ai_embedding",
"index": 0
}
]
]
},
"Qdrant Vector Store Insert": {
"main": [
[
{
"node": "Log Sync Status",
"type": "main",
"index": 0
}
]
]
}
},
"active": true,
"settings": {
"executionOrder": "v1"
},
"versionId": "appflowy-sync-v1",
"meta": {
"templateCredsSetupCompleted": true
},
"id": "AppFlowyContentSync",
"tags": [
"knowledge-management",
"appflowy",
"vector-store",
"sync"
]
}
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.
appFlowyApiollamaApiqdrantApisupabaseApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
AppFlowy Content Sync to Vector Store. Uses n8n-nodes-appflowy, textSplitterRecursiveCharacterTextSplitter, documentDefaultDataLoader, embeddingsOllama. Event-driven trigger; 11 nodes.
Source: https://github.com/161sam/n8n-installer/blob/main/modularium/ai-workspace/AppFlowy-Content-Sync.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.
Indexation. Uses formTrigger, embeddingsOllama, textSplitterRecursiveCharacterTextSplitter, modelSelector. Event-driven trigger; 36 nodes.
ejemplo RAG vs CRAG. Uses googleDrive, vectorStoreQdrant, embeddingsOllama, agent. Event-driven trigger; 30 nodes.
Local RAG AI Agent. Uses memoryPostgresChat, lmChatOllama, lmOllama, toolVectorStore. Event-driven trigger; 24 nodes.
V1 ocal RAG AI Agent. Uses memoryPostgresChat, lmChatOllama, lmOllama, toolVectorStore. Event-driven trigger; 24 nodes.
An on-premises, domain-specific AI assistant for Kaggle (tested on binary disaster-tweet classification), combining LLM, an n8n workflow engine, and Qdrant-backed Retrieval-Augmented Generation (RAG).