tokens(' NAMPULA agronegócio Agronegócio '); $this->assertSame(['nampula', 'agronegocio'], $tokens); $this->assertTrue($search->matches('Agronegócio em Nampula', $tokens)); $this->assertFalse($search->matches('Agronegócio em Gaza', $tokens)); $this->assertSame([], $search->tokens(' ')); } public function test_sql_matches_each_word_across_fields_with_bound_literal_patterns(): void { $connection = new PostgresConnection(fn () => throw new \LogicException('A database must not be opened.')); $query = $connection->table('records'); $search = new SearchTerms; $search->constrain($query, ['title', 'description'], $search->tokens('100% a_b')); $sql = $query->toSql(); $this->assertStringContainsString('translate(lower(coalesce(title::text', $sql); $this->assertStringContainsString(' or ', $sql); $this->assertStringContainsString(' and ', $sql); $this->assertSame(['%100!%%', '%100!%%', '%a!_b%', '%a!_b%'], $query->getBindings()); $this->assertStringNotContainsString('100%', $sql); } public function test_static_services_can_be_found_without_a_database(): void { $pages = (new \ReflectionMethod(GlobalSearchController::class, 'pages'))->invoke(new GlobalSearchController); $search = new SearchTerms; $matches = array_values(array_filter($pages, fn ($page) => $search->matches(implode(' ', $page), $search->tokens('FISCAL simulador')))); $this->assertCount(1, $matches); $this->assertSame('/simulador-fiscal', $matches[0]['url']); } public function test_spelling_variants_common_words_and_title_relevance(): void { $search = new SearchTerms; $tokens = $search->tokens('setores de investimento'); $this->assertTrue($search->matches('Sectores de investimento', $tokens)); $this->assertFalse($search->matches('Sector de saúde', $tokens)); $this->assertTrue($search->matches('Projectos âncora', $search->tokens('projeto'))); $this->assertTrue($search->matches('Oportunidades de investimento em energia', $search->tokens('Como investir em energia?'))); $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)); } }