Backup #2194

ID2194
Dateipfad/var/www/dev.campus.systemische-tools.de/src/Infrastructure/Persistence/SemanticSearchRepository.php
Version12
Typ modified
Größe8.3 KB
Hashed6915f1d27931d24bfd67dcdd918ac2f22b91a78413b018aa7ffb1acfbd3eeb
Datum2025-12-31 02:22:30
Geändert vonclaude-code-hook
GrundClaude Code Pre-Hook Backup vor Edit-Operation
Datei existiert Ja

Dateiinhalt

<?php

declare(strict_types=1);

namespace Infrastructure\Persistence;

// @responsibility: Semantik-Suche und Graph-Daten für Semantic Explorer

use Domain\Repository\SemanticSearchRepositoryInterface;
use PDO;

final class SemanticSearchRepository implements SemanticSearchRepositoryInterface
{
    private PDO $db;

    public function __construct(PDO $pdo)
    {
        $this->db = $pdo;
    }

    /**
     * {@inheritDoc}
     */
    public function getSemanticStats(): array
    {
        return $this->db->query(
            'SELECT
                (SELECT COUNT(*) FROM entities) as entities,
                (SELECT COUNT(*) FROM entity_relations) as relations,
                (SELECT COUNT(*) FROM taxonomy_terms) as taxonomy,
                (SELECT COUNT(*) FROM ontology_classes) as ontology,
                (SELECT COUNT(*) FROM chunk_semantics) as semantics,
                (SELECT COUNT(*) FROM stopwords WHERE is_active = 1) as stopwords,
                (SELECT COUNT(*) FROM entities) as total,
                (SELECT COUNT(DISTINCT type) FROM entities) as types,
                (SELECT COUNT(DISTINCT chunk_id) FROM chunk_entities) as linked_chunks,
                (SELECT COUNT(DISTINCT c.document_id) FROM chunk_entities ce JOIN chunks c ON ce.chunk_id = c.id) as linked_docs'
        )->fetch();
    }

    /**
     * {@inheritDoc}
     */
    public function findEntitySemantics(string $search = '', string $type = '', int $limit = 50, int $offset = 0): array
    {
        // Deduplicated entities with aggregated chunk/document counts
        $sql = 'SELECT e.id, e.name, e.canonical_name, e.type, e.description, e.status,
                       COUNT(DISTINCT ce.chunk_id) as chunk_count,
                       COUNT(DISTINCT c.document_id) as document_count,
                       MIN(ce.chunk_id) as first_chunk_id,
                       MIN(d.filename) as first_filename,
                       MIN(d.id) as first_document_id
                FROM entities e
                LEFT JOIN chunk_entities ce ON e.id = ce.entity_id
                LEFT JOIN chunks c ON ce.chunk_id = c.id
                LEFT JOIN documents d ON c.document_id = d.id
                WHERE 1=1';

        $params = [];

        if ($search !== '') {
            $sql .= ' AND (e.name LIKE :search OR e.description LIKE :search2 OR e.canonical_name LIKE :search3)';
            $params['search'] = '%' . $search . '%';
            $params['search2'] = '%' . $search . '%';
            $params['search3'] = '%' . $search . '%';
        }

        if ($type !== '') {
            $sql .= ' AND e.type = :type';
            $params['type'] = $type;
        }

        $sql .= ' GROUP BY e.id, e.name, e.canonical_name, e.type, e.description, e.status';
        $sql .= ' ORDER BY e.name LIMIT ' . $limit . ' OFFSET ' . $offset;

        $stmt = $this->db->prepare($sql);
        $stmt->execute($params);

        return $stmt->fetchAll();
    }

    /**
     * {@inheritDoc}
     */
    public function countEntitySemantics(string $search = '', string $type = ''): int
    {
        $sql = 'SELECT COUNT(DISTINCT e.id) FROM entities e WHERE 1=1';
        $params = [];

        if ($search !== '') {
            $sql .= ' AND (e.name LIKE :search OR e.description LIKE :search2 OR e.canonical_name LIKE :search3)';
            $params['search'] = '%' . $search . '%';
            $params['search2'] = '%' . $search . '%';
            $params['search3'] = '%' . $search . '%';
        }

        if ($type !== '') {
            $sql .= ' AND e.type = :type';
            $params['type'] = $type;
        }

        $stmt = $this->db->prepare($sql);
        $stmt->execute($params);

        return (int) $stmt->fetchColumn();
    }

    /**
     * {@inheritDoc}
     */
    public function getGraphData(): array
    {
        // Get all entities
        $entities = $this->db->query(
            'SELECT id, name, type FROM entities ORDER BY name'
        )->fetchAll();

        // Get all relations
        $relations = $this->db->query(
            'SELECT source_entity_id, target_entity_id, relation_type, strength
             FROM entity_relations'
        )->fetchAll();

        // Build node index for link resolution
        $nodeIndex = [];
        $nodes = [];
        foreach ($entities as $i => $entity) {
            $nodeIndex[$entity['id']] = $i;
            $nodes[] = [
                'id' => 'entity_' . $entity['id'],
                'label' => $entity['name'],
                'type' => strtoupper($entity['type'] ?? 'OTHER'),
                'entityId' => (int) $entity['id'],
            ];
        }

        // Build links with index references
        $links = [];
        foreach ($relations as $relation) {
            $sourceId = $relation['source_entity_id'];
            $targetId = $relation['target_entity_id'];

            // Skip if entity not found
            if (!isset($nodeIndex[$sourceId]) || !isset($nodeIndex[$targetId])) {
                continue;
            }

            $links[] = [
                'source' => $nodeIndex[$sourceId],
                'target' => $nodeIndex[$targetId],
                'type' => $relation['relation_type'],
                'strength' => (float) ($relation['strength'] ?? 1.0),
            ];
        }

        // Get unique types for stats
        $entityTypes = array_unique(array_column($nodes, 'type'));
        $relationTypes = array_unique(array_column($links, 'type'));

        return [
            'nodes' => $nodes,
            'links' => $links,
            'stats' => [
                'nodes' => count($nodes),
                'links' => count($links),
                'entityTypes' => count($entityTypes),
                'relationTypes' => count($relationTypes),
            ],
        ];
    }

    /**
     * {@inheritDoc}
     */
    public function getTextSemanticStats(): array
    {
        // Coverage
        $coverage = $this->db->query(
            'SELECT
                (SELECT COUNT(*) FROM chunk_semantics WHERE statement_form IS NOT NULL) as analyzed,
                (SELECT COUNT(*) FROM chunks) as total'
        )->fetch();

        $analyzed = (int) ($coverage['analyzed'] ?? 0);
        $total = (int) ($coverage['total'] ?? 1);

        // By statement_form
        $byStatementForm = $this->db->query(
            'SELECT statement_form, COUNT(*) as count
             FROM chunk_semantics
             WHERE statement_form IS NOT NULL
             GROUP BY statement_form
             ORDER BY count DESC'
        )->fetchAll();

        // By intent
        $byIntent = $this->db->query(
            'SELECT intent, COUNT(*) as count
             FROM chunk_semantics
             WHERE intent IS NOT NULL
             GROUP BY intent
             ORDER BY count DESC'
        )->fetchAll();

        return [
            'analyzed' => $analyzed,
            'total' => $total,
            'coverage' => $total > 0 ? round($analyzed / $total * 100, 1) : 0.0,
            'by_statement_form' => $byStatementForm,
            'by_intent' => $byIntent,
        ];
    }

    /**
     * {@inheritDoc}
     */
    public function getKnowledgeSemanticStats(): array
    {
        // Coverage
        $coverage = $this->db->query(
            'SELECT
                (SELECT COUNT(DISTINCT entity_id) FROM entity_semantics WHERE semantic_role IS NOT NULL) as analyzed,
                (SELECT COUNT(*) FROM entities) as total'
        )->fetch();

        $analyzed = (int) ($coverage['analyzed'] ?? 0);
        $total = (int) ($coverage['total'] ?? 1);

        // By semantic_role
        $bySemanticRole = $this->db->query(
            'SELECT semantic_role, COUNT(*) as count
             FROM entity_knowledge_semantics
             WHERE semantic_role IS NOT NULL
             GROUP BY semantic_role
             ORDER BY count DESC'
        )->fetchAll();

        // By functional_category
        $byFunctionalCategory = $this->db->query(
            'SELECT functional_category, COUNT(*) as count
             FROM entity_knowledge_semantics
             WHERE functional_category IS NOT NULL
             GROUP BY functional_category
             ORDER BY count DESC'
        )->fetchAll();

        return [
            'analyzed' => $analyzed,
            'total' => $total,
            'coverage' => $total > 0 ? round($analyzed / $total * 100, 1) : 0.0,
            'by_semantic_role' => $bySemanticRole,
            'by_functional_category' => $byFunctionalCategory,
        ];
    }
}

Vollständig herunterladen

Aktionen

Herunterladen

Andere Versionen dieser Datei

ID Version Typ Größe Datum
2195 13 modified 8.3 KB 2025-12-31 02:23
2194 12 modified 8.3 KB 2025-12-31 02:22
2193 11 modified 8.3 KB 2025-12-31 02:22
2190 10 modified 8.3 KB 2025-12-31 02:21
2189 9 modified 8.3 KB 2025-12-31 02:20
2188 8 modified 8.3 KB 2025-12-31 02:19
1990 7 modified 5.7 KB 2025-12-28 03:04
1606 6 modified 5.6 KB 2025-12-27 00:27
1534 5 modified 5.3 KB 2025-12-26 14:51
1524 4 modified 5.2 KB 2025-12-26 06:52
1523 3 modified 4.7 KB 2025-12-26 06:51
1170 2 modified 4.7 KB 2025-12-25 10:28
1163 1 modified 4.8 KB 2025-12-25 10:28

← Zurück zur Übersicht