pdo = $pdo; } /** * Find a collection by its Qdrant collection_id. * * @return array|null */ public function find(string $collectionId): ?array { $stmt = $this->pdo->prepare(' SELECT * FROM rag_collections WHERE collection_id = ? '); $stmt->execute([$collectionId]); $result = $stmt->fetch(PDO::FETCH_ASSOC); return $result ?: null; } /** * Find multiple collections by their IDs. * * @param array $collectionIds * @return array> */ public function findByIds(array $collectionIds): array { if (empty($collectionIds)) { return []; } $placeholders = implode(',', array_fill(0, count($collectionIds), '?')); $stmt = $this->pdo->prepare(" SELECT * FROM rag_collections WHERE collection_id IN ($placeholders) "); $stmt->execute($collectionIds); return $stmt->fetchAll(PDO::FETCH_ASSOC); } /** * Get all active and searchable collections for dropdowns. * * @return array> */ public function getSearchable(): array { $stmt = $this->pdo->query(' SELECT collection_id, display_name, points_count, vector_size FROM rag_collections WHERE is_active = 1 AND is_searchable = 1 ORDER BY sort_order ASC, display_name ASC '); return $stmt->fetchAll(PDO::FETCH_ASSOC); } /** * Get all active collections (including non-searchable). * * @return array> */ public function getActive(): array { $stmt = $this->pdo->query(' SELECT collection_id, display_name, points_count, vector_size, is_searchable FROM rag_collections WHERE is_active = 1 ORDER BY sort_order ASC, display_name ASC '); return $stmt->fetchAll(PDO::FETCH_ASSOC); } /** * Get all collections (for admin). * * @return array> */ public function getAll(): array { $stmt = $this->pdo->query(' SELECT * FROM rag_collections ORDER BY sort_order ASC, display_name ASC '); return $stmt->fetchAll(PDO::FETCH_ASSOC); } /** * Check if a collection exists. */ public function exists(string $collectionId): bool { $stmt = $this->pdo->prepare(' SELECT 1 FROM rag_collections WHERE collection_id = ? '); $stmt->execute([$collectionId]); return $stmt->fetchColumn() !== false; } /** * Update collection metadata (from Qdrant sync). * * @param array $data */ public function updateMetadata(string $collectionId, array $data): void { $allowed = ['vector_size', 'distance_metric', 'points_count', 'last_synced_at']; $updates = []; $params = []; foreach ($allowed as $field) { if (array_key_exists($field, $data)) { $updates[] = "$field = ?"; $params[] = $data[$field]; } } if (empty($updates)) { return; } $params[] = $collectionId; $sql = 'UPDATE rag_collections SET ' . implode(', ', $updates) . ' WHERE collection_id = ?'; $stmt = $this->pdo->prepare($sql); $stmt->execute($params); } /** * Create a new collection entry. * * @param array $data */ public function create(array $data): int { $stmt = $this->pdo->prepare(' INSERT INTO rag_collections (collection_id, display_name, description, vector_size, distance_metric, points_count, embedding_model, source_type, is_active, is_searchable, sort_order) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) '); $stmt->execute([ $data['collection_id'], $data['display_name'] ?? $data['collection_id'], $data['description'] ?? null, $data['vector_size'] ?? null, $data['distance_metric'] ?? 'Cosine', $data['points_count'] ?? 0, $data['embedding_model'] ?? null, $data['source_type'] ?? 'manual', $data['is_active'] ?? true, $data['is_searchable'] ?? true, $data['sort_order'] ?? 0, ]); return (int) $this->pdo->lastInsertId(); } /** * Deactivate a collection (soft delete). */ public function deactivate(string $collectionId): void { $stmt = $this->pdo->prepare(' UPDATE rag_collections SET is_active = 0, is_searchable = 0 WHERE collection_id = ? '); $stmt->execute([$collectionId]); } /** * Check if collection is referenced by sessions, orders, or preferences. * * @return array{has_references: bool, references: array} */ public function checkReferences(string $collectionId): array { $references = []; $jsonValue = json_encode($collectionId); // Check chat_sessions (ki_content) try { $pdo = DatabaseFactory::content(); $stmt = $pdo->prepare(" SELECT COUNT(*) FROM chat_sessions WHERE JSON_CONTAINS(collections, ?, '$') "); $stmt->execute([$jsonValue]); $count = (int) $stmt->fetchColumn(); if ($count > 0) { $references[] = ['type' => 'chat_sessions', 'count' => $count]; } // Check content_orders (ki_content) $stmt = $pdo->prepare(" SELECT COUNT(*) FROM content_orders WHERE JSON_CONTAINS(collections, ?, '$') "); $stmt->execute([$jsonValue]); $count = (int) $stmt->fetchColumn(); if ($count > 0) { $references[] = ['type' => 'content_orders', 'count' => $count]; } } catch (\PDOException $e) { // Tables might not exist yet } return [ 'has_references' => count($references) > 0, 'references' => $references, ]; } }