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 Direct Query Tool",
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "appflowy-query-tool",
"responseMode": "responseNode",
"options": {}
},
"id": "appflowy-query-webhook",
"name": "AppFlowy Query Webhook",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [
200,
400
]
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "query-text",
"name": "queryText",
"value": "={{ $json.body.query || $json.body.searchTerm || '' }}",
"type": "string"
},
{
"id": "query-type",
"name": "queryType",
"value": "={{ $json.body.queryType || 'semantic_search' }}",
"type": "string"
},
{
"id": "database-filter",
"name": "databaseFilter",
"value": "={{ $json.body.databaseFilter || [] }}",
"type": "array"
},
{
"id": "workspace-filter",
"name": "workspaceFilter",
"value": "={{ $json.body.workspaceFilter || $vars.appflowy_default_workspace }}",
"type": "string"
},
{
"id": "result-limit",
"name": "resultLimit",
"value": "={{ $json.body.limit || 10 }}",
"type": "number"
},
{
"id": "include-metadata",
"name": "includeMetadata",
"value": "={{ $json.body.includeMetadata || true }}",
"type": "boolean"
}
]
},
"options": {}
},
"id": "process-appflowy-query",
"name": "Process AppFlowy Query",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
450,
400
]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict"
},
"conditions": [
{
"id": "semantic-search-condition",
"leftValue": "={{ $json.queryType }}",
"rightValue": "semantic_search",
"operator": {
"type": "string",
"operation": "equals"
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "check-query-type",
"name": "Check Query Type",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
700,
400
]
},
{
"parameters": {
"model": "nomic-embed-text:latest"
},
"id": "generate-query-embedding",
"name": "Generate Query Embedding",
"type": "@n8n/n8n-nodes-langchain.embeddingsOllama",
"typeVersion": 1,
"position": [
950,
300
],
"credentials": {
"ollamaApi": {
"name": "<your credential>"
}
}
},
{
"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 queryText = this.getInputData()[0].json.queryText;\nconst resultLimit = this.getInputData()[0].json.resultLimit || 10;\n\n// Create filter for AppFlowy content only\nconst filter = {\n must: [\n {\n key: \"metadata.source\",\n match: {\n value: \"appflowy\",\n },\n },\n ],\n};\n\n// Perform semantic search\nconst results = await vectorStore.similaritySearch(queryText, resultLimit, filter);\n\n// Format results\nconst formattedResults = results.map((doc, index) => ({\n id: index,\n content: doc.pageContent,\n metadata: doc.metadata,\n source: 'vector_search',\n relevanceScore: doc.score || 0\n}));\n\nreturn [{ json: { results: formattedResults, queryType: 'semantic_search', totalResults: formattedResults.length } }];"
}
},
"inputs": {
"input": [
{
"type": "main",
"required": true
}
]
},
"outputs": {
"output": [
{
"type": "main"
}
]
}
},
"id": "perform-semantic-search",
"name": "Perform Semantic Search",
"type": "@n8n/n8n-nodes-langchain.code",
"typeVersion": 1,
"position": [
1200,
300
]
},
{
"parameters": {
"operation": "listDatabases",
"workspaceId": "={{ $('Process AppFlowy Query').item.json.workspaceFilter }}",
"options": {}
},
"id": "get-appflowy-databases",
"name": "Get AppFlowy Databases",
"type": "n8n-nodes-appflowy.database",
"typeVersion": 1,
"position": [
950,
500
],
"credentials": {
"appFlowyApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"fieldsToSplitOut": "databases",
"options": {}
},
"id": "split-databases",
"name": "Split Databases",
"type": "n8n-nodes-base.splitInBatches",
"typeVersion": 3,
"position": [
1200,
500
]
},
{
"parameters": {
"operation": "listRows",
"databaseId": "={{ $json.id }}",
"options": {
"limit": "={{ $('Process AppFlowy Query').item.json.resultLimit }}"
}
},
"id": "get-database-rows",
"name": "Get Database Rows",
"type": "n8n-nodes-appflowy.database",
"typeVersion": 1,
"position": [
1450,
500
],
"credentials": {
"appFlowyApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "filter-rows",
"name": "filteredRows",
"value": "={{ $json.rows.filter(row => {\n const queryText = $('Process AppFlowy Query').item.json.queryText.toLowerCase();\n const searchableText = (row.title || '').toLowerCase() + ' ' + (row.content || '').toLowerCase() + ' ' + Object.values(row.fields || {}).join(' ').toLowerCase();\n return searchableText.includes(queryText);\n}) }}",
"type": "array"
},
{
"id": "database-context",
"name": "databaseContext",
"value": "={{ { id: $json.id, name: $json.name, type: 'appflowy_database' } }}",
"type": "object"
}
]
},
"options": {}
},
"id": "filter-and-format-rows",
"name": "Filter and Format Rows",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
1700,
500
]
},
{
"parameters": {
"fieldsToSplitOut": "filteredRows",
"options": {}
},
"id": "split-filtered-rows",
"name": "Split Filtered Rows",
"type": "n8n-nodes-base.splitInBatches",
"typeVersion": 3,
"position": [
1950,
500
]
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "format-direct-result",
"name": "formattedResult",
"value": "={{ {\n id: $json.id,\n content: ($json.title || '') + '\\n\\n' + ($json.content || '') + '\\n\\nFields: ' + JSON.stringify($json.fields || {}),\n metadata: {\n source: 'appflowy',\n type: 'database_row',\n database_id: $('Filter and Format Rows').item.json.databaseContext.id,\n database_name: $('Filter and Format Rows').item.json.databaseContext.name,\n row_id: $json.id,\n title: $json.title,\n created_at: $json.created_at,\n updated_at: $json.updated_at\n },\n source: 'direct_query',\n relevanceScore: 1.0\n} }}",
"type": "object"
}
]
},
"options": {}
},
"id": "format-direct-query-result",
"name": "Format Direct Query Result",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
2200,
500
]
},
{
"parameters": {
"fieldsToAggregate": {
"fieldToAggregate": [
{
"fieldToAggregate": "formattedResult"
}
]
},
"options": {}
},
"id": "aggregate-direct-results",
"name": "Aggregate Direct Results",
"type": "n8n-nodes-base.aggregate",
"typeVersion": 1,
"position": [
2450,
500
]
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "combine-all-results",
"name": "allResults",
"value": "={{ [...($('Perform Semantic Search').all().flatMap(item => item.results || [])), ...($('Aggregate Direct Results').all().flatMap(item => item.formattedResult || []))] }}",
"type": "array"
},
{
"id": "query-summary",
"name": "querySummary",
"value": "={{ {\n queryText: $('Process AppFlowy Query').item.json.queryText,\n queryType: $('Process AppFlowy Query').item.json.queryType,\n totalResults: $json.allResults.length,\n semanticResults: ($('Perform Semantic Search').all().flatMap(item => item.results || [])).length,\n directResults: ($('Aggregate Direct Results').all().flatMap(item => item.formattedResult || [])).length,\n timestamp: $now\n} }}",
"type": "object"
}
]
},
"options": {}
},
"id": "combine-all-results",
"name": "Combine All Results",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
2700,
400
]
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "ranked-results",
"name": "rankedResults",
"value": "={{ $json.allResults.sort((a, b) => b.relevanceScore - a.relevanceScore).slice(0, $('Process AppFlowy Query').item.json.resultLimit) }}",
"type": "array"
},
{
"id": "response-metadata",
"name": "responseMetadata",
"value": "={{ {\n queryProcessed: $json.querySummary.queryText,\n resultsFound: $json.rankedResults.length,\n queryType: $json.querySummary.queryType,\n includeMetadata: $('Process AppFlowy Query').item.json.includeMetadata,\n timestamp: $now\n} }}",
"type": "object"
}
]
},
"options": {}
},
"id": "rank-and-limit-results",
"name": "Rank and Limit Results",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
2950,
400
]
},
{
"parameters": {
"operation": "insert",
"table": {
"__rl": true,
"value": "query_analytics",
"mode": "list"
},
"data": {
"insert": [
{
"column": "query_text",
"value": "={{ $('Process AppFlowy Query').item.json.queryText }}"
},
{
"column": "query_type",
"value": "appflowy_direct"
},
{
"column": "results_count",
"value": "={{ $json.rankedResults.length }}"
},
{
"column": "response_time_ms",
"value": "={{ $now - $('Process AppFlowy Query').item.json.timestamp }}"
},
{
"column": "query_metadata",
"value": "={{ $json.responseMetadata }}"
},
{
"column": "created_at",
"value": "={{ $now }}"
}
]
},
"options": {}
},
"id": "log-query-analytics",
"name": "Log Query Analytics",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.5,
"position": [
3200,
600
],
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"options": {}
},
"id": "respond-to-appflowy-query",
"name": "Respond to AppFlowy Query",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.1,
"position": [
3200,
400
]
},
{
"parameters": {
"content": "## AppFlowy Direct Query Tool\n\n**Purpose:** Direct querying interface for AppFlowy databases and content with both semantic and direct search capabilities.\n\n**Features:**\n- **Semantic Search**: Vector-based similarity search using embeddings\n- **Direct Query**: Text-based filtering across AppFlowy databases\n- **Result Ranking**: Relevance scoring and result prioritization\n- **Analytics**: Query performance tracking and usage analytics\n- **Flexible Filtering**: Database, workspace, and content type filters\n\n**Query Types:**\n- `semantic_search`: Vector similarity search (default)\n- `direct_query`: Text-based database search\n- `hybrid`: Combination of both approaches\n\n**API Parameters:**\n- `query`: Search text/question\n- `queryType`: Type of search to perform\n- `databaseFilter`: Specific databases to search\n- `workspaceFilter`: Target workspace ID\n- `limit`: Maximum results to return\n- `includeMetadata`: Include detailed metadata\n\n**Response Format:**\n```json\n{\n \"rankedResults\": [...],\n \"responseMetadata\": {...}\n}\n```",
"height": 600,
"width": 700,
"color": 4
},
"id": "appflowy-query-documentation",
"name": "AppFlowy Query Documentation",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
100,
100
]
}
],
"connections": {
"AppFlowy Query Webhook": {
"main": [
[
{
"node": "Process AppFlowy Query",
"type": "main",
"index": 0
}
]
]
},
"Process AppFlowy Query": {
"main": [
[
{
"node": "Check Query Type",
"type": "main",
"index": 0
}
]
]
},
"Check Query Type": {
"main": [
[
{
"node": "Perform Semantic Search",
"type": "main",
"index": 0
}
],
[
{
"node": "Get AppFlowy Databases",
"type": "main",
"index": 0
}
]
]
},
"Perform Semantic Search": {
"main": [
[
{
"node": "Combine All Results",
"type": "main",
"index": 0
}
]
]
},
"Get AppFlowy Databases": {
"main": [
[
{
"node": "Split Databases",
"type": "main",
"index": 0
}
]
]
},
"Split Databases": {
"main": [
[
{
"node": "Get Database Rows",
"type": "main",
"index": 0
}
]
]
},
"Get Database Rows": {
"main": [
[
{
"node": "Filter and Format Rows",
"type": "main",
"index": 0
}
]
]
},
"Filter and Format Rows": {
"main": [
[
{
"node": "Split Filtered Rows",
"type": "main",
"index": 0
}
]
]
},
"Split Filtered Rows": {
"main": [
[
{
"node": "Format Direct Query Result",
"type": "main",
"index": 0
}
]
]
},
"Format Direct Query Result": {
"main": [
[
{
"node": "Aggregate Direct Results",
"type": "main",
"index": 0
}
]
]
},
"Aggregate Direct Results": {
"main": [
[
{
"node": "Combine All Results",
"type": "main",
"index": 0
}
]
]
},
"Combine All Results": {
"main": [
[
{
"node": "Rank and Limit Results",
"type": "main",
"index": 0
}
]
]
},
"Rank and Limit Results": {
"main": [
[
{
"node": "Respond to AppFlowy Query",
"type": "main",
"index": 0
},
{
"node": "Log Query Analytics",
"type": "main",
"index": 0
}
]
]
}
},
"active": true,
"settings": {
"executionOrder": "v1"
},
"versionId": "appflowy-query-v1",
"meta": {
"templateCredsSetupCompleted": true
},
"id": "AppFlowyQueryTool",
"tags": [
"appflowy",
"query",
"search",
"tool",
"semantic"
]
}
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.
appFlowyApiollamaApipostgres
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
AppFlowy Direct Query Tool. Uses embeddingsOllama, n8n-nodes-appflowy, postgres. Webhook trigger; 17 nodes.
Source: https://github.com/161sam/n8n-installer/blob/main/modularium/ai-workspace/AppFlowy-Query-Tool.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.
V3 Local Agentic RAG AI Agent. Uses documentDefaultDataLoader, memoryPostgresChat, chatTrigger, agent. Webhook trigger; 41 nodes.
Author: Jadai kongolo
Camila IA. Uses postgres, crypto, redis, agent. Webhook trigger; 92 nodes.
Hi! I’m Amanda, a creator of intelligent automations using n8n and Make. I’ve been building AI-powered workflows for over 2 years, always focused on usability and innovation. This one here is very spe
AI Multi-Document Analyzer with Smart Recommendations & Reporting