From eab09dd61e2df72c737c69235271a7a815f0dca0 Mon Sep 17 00:00:00 2001 From: Paulo Massingue Date: Fri, 13 Nov 2026 12:07:51 +0200 Subject: [PATCH] ajuste na pesquisa de informacao, para passar a dar prioridade --- .../Controllers/GlobalSearchController.php | 3 +-- app/Services/SearchTerms.php | 10 ++++---- tests/Unit/FederatedSearchTest.php | 21 ++++++++++++++++ tests/Unit/SearchTermsTest.php | 25 +++++++++++++++++++ 4 files changed, 52 insertions(+), 7 deletions(-) diff --git a/app/Http/Controllers/GlobalSearchController.php b/app/Http/Controllers/GlobalSearchController.php index 61535617..ca7e97b5 100644 --- a/app/Http/Controllers/GlobalSearchController.php +++ b/app/Http/Controllers/GlobalSearchController.php @@ -29,8 +29,7 @@ class GlobalSearchController extends Controller if ($tokens !== []) { $factories = [ 'Páginas e serviços' => function () use ($search, $tokens, $term) { - $pages = array_values(array_filter($this->pages(), fn ($page) => $search->matches(implode(' ', $page), $tokens) - && $search->score($page['title'], $term, $tokens) > 0)); + $pages = array_values(array_filter($this->pages(), fn ($page) => $search->matches(implode(' ', $page), $tokens))); usort($pages, fn ($a, $b) => $search->score($b['title'], $term, $tokens) <=> $search->score($a['title'], $term, $tokens) ?: strcmp($a['title'], $b['title']) ?: strcmp($a['url'], $b['url'])); diff --git a/app/Services/SearchTerms.php b/app/Services/SearchTerms.php index 48c1bf0c..6258afed 100644 --- a/app/Services/SearchTerms.php +++ b/app/Services/SearchTerms.php @@ -82,10 +82,10 @@ final class SearchTerms { $title = mb_strtolower(Str::ascii($title)); $phrase = mb_strtolower(Str::ascii(trim($phrase))); - $score = $title === $phrase ? 1000 : (str_contains($title, $phrase) ? 400 : 0); + $score = $title === $phrase ? 1000 : (str_starts_with($title, $phrase) ? 600 : (str_contains($title, $phrase) ? 400 : 0)); foreach ($tokens as $token) { if ($this->matches($title, [$token])) { - $score += 20; + $score++; } } @@ -97,15 +97,15 @@ final class SearchTerms { $column = $this->normalizedColumn($titleColumn); $phrase = mb_strtolower(Str::ascii(trim($phrase))); - $sql = "CASE WHEN {$column} = ? THEN 1000 WHEN {$column} LIKE ? ESCAPE '!' THEN 400 ELSE 0 END"; - $bindings = [$phrase, $this->pattern($phrase)]; + $sql = "CASE WHEN {$column} = ? THEN 1000 WHEN {$column} LIKE ? ESCAPE '!' THEN 600 WHEN {$column} LIKE ? ESCAPE '!' THEN 400 ELSE 0 END"; + $bindings = [$phrase, ltrim($this->pattern($phrase), '%'), $this->pattern($phrase)]; foreach ($tokens as $token) { $conditions = []; foreach ($this->variants($token) as $variant) { $conditions[] = "{$column} LIKE ? ESCAPE '!'"; $bindings[] = $this->pattern($variant); } - $sql .= ' + CASE WHEN ('.implode(' OR ', $conditions).') THEN 20 ELSE 0 END'; + $sql .= ' + CASE WHEN ('.implode(' OR ', $conditions).') THEN 1 ELSE 0 END'; } $query->orderByRaw('('.$sql.') DESC', $bindings)->orderBy($titleColumn)->orderBy($tieColumn); } diff --git a/tests/Unit/FederatedSearchTest.php b/tests/Unit/FederatedSearchTest.php index 244bd13e..658c11de 100644 --- a/tests/Unit/FederatedSearchTest.php +++ b/tests/Unit/FederatedSearchTest.php @@ -3,6 +3,7 @@ namespace Tests\Unit; use App\Services\FederatedSearch; +use App\Services\SearchTerms; use PHPUnit\Framework\TestCase; final class FederatedSearchTest extends TestCase @@ -44,4 +45,24 @@ final class FederatedSearchTest extends TestCase $result = (new FederatedSearch)->page($factories, 1, 5, fn ($item) => $item['score']); $this->assertSame(['Sectores', 'Informação'], array_column($result['data'], 'title')); } + + public function test_direct_matches_precede_associations_across_sources_and_pages(): void + { + $search = new SearchTerms; + $term = 'corredor da beira'; + $tokens = $search->tokens($term); + $sources = [ + 'Oportunidades' => fn () => ['count' => 2, 'fetch' => fn ($offset, $limit) => array_slice([ + ['title' => 'Açúcar no Corredor da Beira'], ['title' => 'Produção de açúcar'], + ], $offset, $limit)], + 'Carteira' => fn () => ['count' => 1, 'fetch' => fn ($offset, $limit) => array_slice([ + ['title' => 'Corredor da Beira'], + ], $offset, $limit)], + ]; + $score = fn ($item) => $search->score($item['title'], $term, $tokens); + $service = new FederatedSearch; + + $this->assertSame(['Corredor da Beira', 'Açúcar no Corredor da Beira'], array_column($service->page($sources, 1, 2, $score)['data'], 'title')); + $this->assertSame(['Produção de açúcar'], array_column($service->page($sources, 2, 2, $score)['data'], 'title')); + } } diff --git a/tests/Unit/SearchTermsTest.php b/tests/Unit/SearchTermsTest.php index fe9a1f57..bdfbd557 100644 --- a/tests/Unit/SearchTermsTest.php +++ b/tests/Unit/SearchTermsTest.php @@ -56,4 +56,29 @@ final class SearchTermsTest extends TestCase $this->assertGreaterThan($search->score('Como investir', 'investir', ['investir']), $search->score('Investir', 'investir', ['investir'])); $this->assertSame(['de'], $search->tokens('de')); } + + public function test_direct_titles_precede_titles_and_descriptions_that_reference_the_query(): void + { + $search = new SearchTerms; + $phrase = 'corredor da beira'; + $tokens = $search->tokens($phrase); + $titles = ['Açúcar no Corredor da Beira', 'Corredor da Beira e investimentos', 'Corredor da Beira', 'Produção de açúcar']; + usort($titles, fn ($a, $b) => $search->score($b, $phrase, $tokens) <=> $search->score($a, $phrase, $tokens)); + + $this->assertSame(['Corredor da Beira', 'Corredor da Beira e investimentos', 'Açúcar no Corredor da Beira', 'Produção de açúcar'], $titles); + $this->assertSame($search->score('Corredor da Beira', $phrase, $tokens), $search->score('CORREDOR DA BEIRA', $phrase, $tokens)); + } + + public function test_database_relevance_uses_the_same_levels_and_escaped_prefix(): void + { + $connection = new PostgresConnection(fn () => throw new \LogicException('A database must not be opened.')); + $query = $connection->table('records'); + $search = new SearchTerms; + $search->orderByRelevance($query, 'title', '100% açúcar', $search->tokens('100% açúcar'), 'id'); + + $this->assertStringContainsString('THEN 1000', $query->toSql()); + $this->assertStringContainsString('THEN 600', $query->toSql()); + $this->assertStringContainsString('THEN 400', $query->toSql()); + $this->assertSame(['100% acucar', '100!% acucar%', '%100!% acucar%'], array_slice($query->getBindings(), 0, 3)); + } }