Skip to main content
Glama
SearchIndex.php5.9 kB
<?php declare(strict_types=1); namespace GoldenPathDigital\LaravelAscend\Documentation; final class SearchIndex { /** @var DocumentationLoader */ private $loader; /** * @var array<int, array<string, mixed>> */ private array $entries; public function __construct(DocumentationLoader $loader) { $this->loader = $loader; $this->entries = $this->buildEntries(); } /** * @return array<int, array<string, mixed>> */ public function search(string $query, int $limit = 10): array { $query = trim($query); if ($query === '' || $limit <= 0) { return []; } $terms = array_values(array_filter(array_map( static fn (string $term): string => strtolower($term), preg_split('/\s+/', $query) ?: [], ))); if ($terms === []) { return []; } $results = []; foreach ($this->entries as $entry) { $score = 0; foreach ($terms as $term) { if (str_contains($entry['search_tokens'], $term)) { $score++; } } if ($score === 0) { continue; } $results[] = [ 'type' => $entry['type'], 'id' => $entry['id'], 'title' => $entry['title'], 'summary' => $entry['summary'], 'metadata' => $entry['metadata'], 'score' => $score, ]; } usort($results, static function (array $left, array $right): int { $scoreComparison = $right['score'] <=> $left['score']; if ($scoreComparison !== 0) { return $scoreComparison; } $typeComparison = strcmp($left['type'], $right['type']); if ($typeComparison !== 0) { return $typeComparison; } return strcmp($left['title'], $right['title']); }); return array_slice($results, 0, $limit); } public function getEntryCount(): int { return count($this->entries); } /** * @return array<int, array<string, mixed>> */ private function buildEntries(): array { $entries = []; foreach ($this->loader->loadBreakingChangeEntries() as $identifier => $change) { $summary = $this->createSummary($change['description'] ?? ''); $metadata = [ 'slug' => $change['slug'] ?? null, 'version' => $change['version'] ?? null, 'severity' => $change['severity'] ?? null, 'category' => $change['category'] ?? null, ]; $entries[] = $this->createEntry( 'breaking_change', $identifier, (string) ($change['title'] ?? $change['id']), $summary, $metadata, [ (string) ($change['id'] ?? ''), (string) ($change['severity'] ?? ''), (string) ($change['category'] ?? ''), (string) ($change['version'] ?? ''), ] ); } foreach ($this->loader->loadPatternDocuments() as $patternId => $pattern) { $summary = $this->createSummary($pattern['description'] ?? ''); $metadata = [ 'category' => $pattern['category'] ?? null, 'complexity' => $pattern['complexity'] ?? null, 'applies_to_versions' => $pattern['applies_to_versions'] ?? [], ]; $entries[] = $this->createEntry( 'pattern', (string) $patternId, (string) ($pattern['name'] ?? $patternId), $summary, $metadata, array_merge( (array) ($pattern['applies_to_versions'] ?? []), [ (string) ($pattern['category'] ?? ''), (string) ($pattern['complexity'] ?? ''), ] ) ); } return $entries; } /** * @param array<string, mixed> $metadata * @param array<int, string> $additionalTokens * * @return array<string, mixed> */ private function createEntry( string $type, string $id, string $title, string $summary, array $metadata, array $additionalTokens = [] ): array { $tokenSource = strtolower( trim( implode( ' ', array_filter([ $title, $summary, implode(' ', array_filter($additionalTokens)), ]), ), ), ); return [ 'type' => $type, 'id' => $id, 'title' => $title, 'summary' => $summary, 'metadata' => $metadata, 'search_tokens' => $tokenSource, ]; } private function createSummary(string $text): string { $trimmed = trim($text); if ($trimmed === '') { return ''; } if ($this->stringLength($trimmed) <= 200) { return $trimmed; } return rtrim($this->stringSubstring($trimmed, 0, 197)) . '...'; } private function stringLength(string $value): int { return function_exists('mb_strlen') ? mb_strlen($value) : strlen($value); } private function stringSubstring(string $value, int $start, int $length): string { return function_exists('mb_substr') ? mb_substr($value, $start, $length) : substr($value, $start, $length); } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/aarongrtech/laravel-ascend'

If you have feedback or need assistance with the MCP directory API, please join our Discord server