[]]); } // Create database connection $conn = new mysqli( $GLOBALS['config']['DB_HOST'], $GLOBALS['config']['DB_USER'], $GLOBALS['config']['DB_PASS'], $GLOBALS['config']['DB_NAME'] ); if ($conn->connect_error) { ResponseHelper::serverError('Database connection failed'); } // Search for similar titles // Use both LIKE for substring matching and SOUNDEX for phonetic matching $duplicates = []; // Prepare search term for LIKE $searchTerm = '%' . $conn->real_escape_string($title) . '%'; // Get SOUNDEX of title $soundexTitle = soundex($title); // First, search for exact substring matches (case-insensitive) $sql = "SELECT ticket_id, title, status, priority, created_at FROM tickets WHERE ( title LIKE ? OR SOUNDEX(title) = ? ) AND status != 'Closed' ORDER BY created_at DESC LIMIT 10"; $stmt = $conn->prepare($sql); $stmt->bind_param("ss", $searchTerm, $soundexTitle); $stmt->execute(); $result = $stmt->get_result(); while ($row = $result->fetch_assoc()) { // Calculate similarity score $similarity = 0; // Check for exact substring match if (stripos($row['title'], $title) !== false) { $similarity = 90; } // Check SOUNDEX match elseif (soundex($row['title']) === $soundexTitle) { $similarity = 70; } // Check word overlap else { $titleWords = array_map('strtolower', preg_split('/\s+/', $title)); $rowWords = array_map('strtolower', preg_split('/\s+/', $row['title'])); $matchingWords = array_intersect($titleWords, $rowWords); $similarity = (count($matchingWords) / max(count($titleWords), 1)) * 60; } if ($similarity >= 30) { $duplicates[] = [ 'ticket_id' => $row['ticket_id'], 'title' => $row['title'], 'status' => $row['status'], 'priority' => $row['priority'], 'created_at' => $row['created_at'], 'similarity' => round($similarity) ]; } } $stmt->close(); // Sort by similarity descending usort($duplicates, function($a, $b) { return $b['similarity'] - $a['similarity']; }); // Limit to top 5 $duplicates = array_slice($duplicates, 0, 5); $conn->close(); ResponseHelper::success(['duplicates' => $duplicates]);