{
"event": "PreToolUse",
"tool_name": "Read",
"tool_input": {
"file_path": "\/var\/www\/dev.campus.systemische-tools.de\/src\/Infrastructure\/Docs\/HybridSearchService.php",
"offset": 80,
"limit": 150
}
}
{
"tool_response": {
"type": "text",
"file": {
"filePath": "\/var\/www\/dev.campus.systemische-tools.de\/src\/Infrastructure\/Docs\/HybridSearchService.php",
"content": " $stmt = $this->pdo->query(\"\n SELECT entities FROM dokumentation_chunks\n WHERE entities IS NOT NULL AND entities != '[]'\n \");\n\n $byType = [];\n\n foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {\n $entities = $this->decodeJsonArray($row['entities'] ?? null);\n foreach ($entities as $entity) {\n if (isset($entity['name'], $entity['type'])) {\n $type = $entity['type'];\n if (!isset($byType[$type])) {\n $byType[$type] = [];\n }\n if (!in_array($entity['name'], $byType[$type], true)) {\n $byType[$type][] = $entity['name'];\n }\n }\n }\n }\n\n return $byType;\n }\n\n \/** Suggests related searches based on current results. *\/\n public function suggestRelatedSearches(array $results): array\n {\n $suggestions = [];\n\n foreach ($results as $result) {\n \/\/ Add keywords from results\n foreach ($result['keywords'] ?? [] as $keyword) {\n if (!in_array($keyword, $suggestions, true)) {\n $suggestions[] = $keyword;\n }\n }\n\n \/\/ Add entity names\n foreach ($result['entities'] ?? [] as $entity) {\n if (isset($entity['name']) && !in_array($entity['name'], $suggestions, true)) {\n $suggestions[] = $entity['name'];\n }\n }\n }\n\n return array_slice($suggestions, 0, 5);\n }\n\n \/** Performs semantic search in Qdrant. *\/\n private function semanticSearch(string $query, array $filters, int $limit): array\n {\n $embedding = $this->ollama->getEmbedding($query);\n\n $url = sprintf('%s\/collections\/%s\/points\/search', $this->qdrantHost, self::COLLECTION);\n\n $payload = [\n 'vector' => array_values($embedding),\n 'limit' => $limit,\n 'with_payload' => true,\n ];\n\n \/\/ Add Qdrant filter if taxonomy category specified\n if (isset($filters['taxonomy_category'])) {\n $payload['filter'] = [\n 'must' => [\n [\n 'key' => 'taxonomy_category',\n 'match' => ['value' => $filters['taxonomy_category']],\n ],\n ],\n ];\n }\n\n try {\n $response = $this->makeRequest($url, $payload, 'POST');\n\n if (!isset($response['result']) || !is_array($response['result'])) {\n return [];\n }\n\n return array_map(static function (array $item): array {\n return [\n 'id' => (string) $item['id'],\n 'score' => (float) ($item['score'] ?? 0),\n 'payload' => is_array($item['payload'] ?? null) ? $item['payload'] : [],\n ];\n }, $response['result']);\n } catch (RuntimeException) {\n return [];\n }\n }\n\n \/** Enriches vector results with SQL data and applies filters. *\/\n private function enrichAndFilter(array $vectorResults, array $filters): array\n {\n $results = [];\n $minScore = $filters['min_score'] ?? 0.3;\n foreach ($vectorResults as $vr) {\n if ($vr['score'] < $minScore) {\n continue;\n }\n $chunkId = (int) ($vr['payload']['chunk_id'] ?? 0);\n if ($chunkId === 0) {\n continue;\n }\n $chunk = $this->getChunkWithDocument($chunkId);\n if ($chunk === null) {\n continue;\n }\n \/\/ Apply entity\/keyword filters\n if (!$this->matchesFilters($chunk, $filters)) {\n continue;\n }\n $results[] = [\n 'chunk_id' => $chunkId, 'doc_id' => (int) $chunk['dokumentation_id'],\n 'path' => $chunk['doc_path'] ?? '', 'title' => $chunk['doc_title'] ?? '',\n 'content' => $chunk['content_clean'] ?? $chunk['content'] ?? '',\n 'heading_path' => $this->decodeJsonArray($chunk['heading_path'] ?? null),\n 'taxonomy' => $this->decodeJsonArray($chunk['taxonomy_path'] ?? null),\n 'entities' => $this->decodeJsonArray($chunk['entities'] ?? null),\n 'keywords' => $this->decodeJsonArray($chunk['keywords'] ?? null),\n 'score' => $vr['score'], 'relevance_score' => $vr['score'],\n ];\n }\n return $results;\n }\n\n \/** Checks if chunk matches entity\/keyword filters. *\/\n private function matchesFilters(array $chunk, array $filters): bool\n {\n if (isset($filters['entity_name'])) {\n $entities = $this->decodeJsonArray($chunk['entities'] ?? null);\n $found = false;\n foreach ($entities as $e) {\n if (isset($e['name']) && stripos($e['name'], $filters['entity_name']) !== false) {\n $found = true;\n break;\n }\n }\n if (!$found) {\n return false;\n }\n }\n if (isset($filters['entity_type'])) {\n $entities = $this->decodeJsonArray($chunk['entities'] ?? null);\n $found = false;\n foreach ($entities as $e) {\n if (isset($e['type']) && strtoupper($e['type']) === strtoupper($filters['entity_type'])) {\n $found = true;",
"numLines": 150,
"startLine": 80,
"totalLines": 343
}
}
}