documentRepository = $documentRepository; $this->chunkRepository = $chunkRepository; $this->entityRepository = $entityRepository; $this->relationRepository = $relationRepository; $this->semanticSearchRepository = $semanticSearchRepository; $this->vectorSearchService = $vectorSearchService; } /** * GET /semantic-explorer * Dashboard mit Statistiken */ public function index(): void { $docStats = $this->documentRepository->getStats(); $chunkStats = $this->chunkRepository->getStats(); $semanticStats = $this->semanticSearchRepository->getSemanticStats(); $documents = $this->documentRepository->findAll(); $recentChunks = $this->chunkRepository->findRecent(5); $this->view('semantic-explorer.index', [ 'title' => 'Semantic Explorer', 'docStats' => $docStats, 'chunkStats' => $chunkStats, 'semanticStats' => $semanticStats, 'documents' => $documents, 'recentChunks' => $recentChunks, ]); } /** * GET /semantic-explorer/dokumente * Liste aller Dokumente */ public function dokumente(): void { $status = $this->getString('status'); $search = $this->getString('search'); $documents = $this->documentRepository->findFiltered($status, $search); $this->view('semantic-explorer.dokumente.index', [ 'title' => 'Dokumente', 'documents' => $documents, 'currentStatus' => $status, 'currentSearch' => $search, ]); } /** * GET /semantic-explorer/dokumente/{id} * Dokument-Details mit Chunks */ public function dokumentShow(int $id): void { $document = $this->documentRepository->find($id); if ($document === null) { $this->notFound('Dokument nicht gefunden'); } $chunks = $this->chunkRepository->findByDocument($id); // Heading-Paths dekodieren foreach ($chunks as &$chunk) { $chunk['heading_path_decoded'] = $this->decodeJson($chunk['heading_path'] ?? null); $chunk['metadata_decoded'] = $this->decodeJson($chunk['metadata'] ?? null); } $this->view('semantic-explorer.dokumente.show', [ 'title' => $document['filename'], 'document' => $document, 'chunks' => $chunks, ]); } /** * GET /semantic-explorer/chunks * Liste aller Chunks */ public function chunks(): void { $search = $this->getString('search'); $embedded = $this->getString('embedded'); $pagination = $this->getPagination(50); $totalCount = $this->chunkRepository->count($search, $embedded); $chunks = $this->chunkRepository->findFiltered($search, $embedded, $pagination->limit, $pagination->offset); $pagination = $pagination->withTotal($totalCount); $this->view('semantic-explorer.chunks.index', [ 'title' => 'Chunks', 'chunks' => $chunks, 'currentSearch' => $search, 'currentEmbedded' => $embedded, 'currentPage' => $pagination->page, 'totalCount' => $pagination->totalCount, 'totalPages' => $pagination->totalPages(), ]); } /** * GET /semantic-explorer/chunks/{id} * Chunk-Details */ public function chunkShow(int $id): void { $chunk = $this->chunkRepository->find($id); if ($chunk === null) { $this->notFound('Chunk nicht gefunden'); } // JSON-Felder dekodieren $chunk['heading_path_decoded'] = $this->decodeJson($chunk['heading_path'] ?? null); $chunk['metadata_decoded'] = $this->decodeJson($chunk['metadata'] ?? null); // Nachbar-Chunks $prevChunk = $this->chunkRepository->findByDocumentAndIndex( $chunk['document_id'], $chunk['chunk_index'] - 1 ); $nextChunk = $this->chunkRepository->findByDocumentAndIndex( $chunk['document_id'], $chunk['chunk_index'] + 1 ); $this->view('semantic-explorer.chunks.show', [ 'title' => 'Chunk #' . $chunk['id'], 'chunk' => $chunk, 'prevChunk' => $prevChunk, 'nextChunk' => $nextChunk, ]); } /** * GET /semantic-explorer/suche * Semantische Suche in Nutzdaten */ public function suche(): void { $query = $this->getString('q'); $limit = $this->getLimit(20, 10); $results = []; if ($query !== '') { // Vektor-Suche via Qdrant $results = $this->vectorSearch($query, $limit); } $this->view('semantic-explorer.suche', [ 'title' => 'Semantische Suche', 'query' => $query, 'results' => $results, 'limit' => $limit, ]); } /** * Vektor-Suche in documents Collection using VectorSearchService */ private function vectorSearch(string $query, int $limit): array { // Search via service $response = $this->vectorSearchService->search($query, 'documents', $limit); if (empty($response)) { return []; } // Chunk-Details aus DB laden $results = []; foreach ($response as $point) { $chunkId = $point['payload']['chunk_id'] ?? null; if ($chunkId === null) { continue; } $chunk = $this->chunkRepository->find($chunkId); if ($chunk !== null) { $chunk['score'] = $point['score']; $chunk['heading_path_decoded'] = $this->decodeJson($chunk['heading_path'] ?? null); $results[] = $chunk; } } return $results; } /** * GET /semantic-explorer/semantik * Begriffe mit Bedeutungen */ public function semantik(): void { $search = $this->getString('search'); $type = $this->getString('type'); $pagination = $this->getPagination(50); $totalCount = $this->semanticSearchRepository->countEntitySemantics($search, $type); $semantics = $this->semanticSearchRepository->findEntitySemantics($search, $type, $pagination->limit, $pagination->offset); $pagination = $pagination->withTotal($totalCount); $stats = $this->semanticSearchRepository->getSemanticStats(); $entityTypes = $this->entityRepository->getStats(); $this->view('semantic-explorer.semantik', [ 'title' => 'Semantik', 'semantics' => $semantics, 'stats' => $stats, 'entityTypes' => $entityTypes, 'currentSearch' => $search, 'currentType' => $type, 'currentPage' => $pagination->page, 'totalCount' => $pagination->totalCount, 'totalPages' => $pagination->totalPages(), ]); } /** * GET /semantic-explorer/graph * Entity-Relations Graph Visualisierung */ public function graph(): void { $entityTypes = $this->entityRepository->getStats(); $relationTypes = $this->relationRepository->getTypes(); $this->view('semantic-explorer.graph', [ 'title' => 'Entity Graph', 'entityTypes' => $entityTypes, 'relationTypes' => $relationTypes, ]); } /** * GET /semantic-explorer/graph-data * JSON-Daten für Graph-Visualisierung */ public function graphData(): void { $data = $this->semanticSearchRepository->getGraphData(); $this->json($data); } }