AutomationFlowsAI & RAG › AppFlowy Semantic Query Tool with Ollama

AppFlowy Semantic Query Tool with Ollama

Original n8n title: Appflowy Direct Query Tool

AppFlowy Direct Query Tool. Uses embeddingsOllama, n8n-nodes-appflowy, postgres. Webhook trigger; 17 nodes.

Webhook trigger★★★★☆ complexityAI-powered17 nodesOllama EmbeddingsN8N Nodes AppflowyPostgres
AI & RAG Trigger: Webhook Nodes: 17 Complexity: ★★★★☆ AI nodes: yes Added:
AppFlowy Semantic Query Tool with Ollama — n8n workflow card showing Ollama Embeddings, N8N Nodes Appflowy, Postgres integration

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": "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.

Pro

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 →

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

V3 Local Agentic RAG AI Agent. Uses documentDefaultDataLoader, memoryPostgresChat, chatTrigger, agent. Webhook trigger; 41 nodes.

Document Default Data Loader, Memory Postgres Chat, Chat Trigger +9
AI & RAG

Author: Jadai kongolo

Document Default Data Loader, Memory Postgres Chat, Chat Trigger +9
AI & RAG

Camila IA. Uses postgres, crypto, redis, agent. Webhook trigger; 92 nodes.

Postgres, Crypto, Redis +13
AI & RAG

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

OpenAI Chat, Redis, OpenAI +11
AI & RAG

AI Multi-Document Analyzer with Smart Recommendations & Reporting

Crypto, Agent, OpenAI Chat +8