Use ILIKE for case-insensitive search on PostgreSQL

pull/7143/head
Your Name 2 weeks ago
parent 0fc121b685
commit 613cf413de

@ -31,6 +31,15 @@ class SearchController extends Controller
$this->middleware('auth');
}
/**
* Case-insensitive LIKE operator. PostgreSQL's LIKE is case-sensitive, so
* use ILIKE there to match MySQL's default case-insensitive behaviour.
*/
protected function likeOperator(): string
{
return config('database.default') === 'pgsql' ? 'ilike' : 'like';
}
public function searchAPI(Request $request): JsonResponse
{
$this->validate($request, [
@ -109,7 +118,7 @@ class SearchController extends Controller
->whereNull('in_reply_to_id')
->whereNull('reblog_of_id')
->whereProfileId(Auth::user()->profile_id)
->where('caption', 'like', '%'.$tag.'%')
->where('caption', $this->likeOperator(), '%'.$tag.'%')
->latest()
->limit(10)
->get();
@ -140,7 +149,7 @@ class SearchController extends Controller
$tokens = Cache::remember($key, $ttl, function () use ($tag) {
$htag = Str::startsWith($tag, '#') == true ? mb_substr($tag, 1) : $tag;
$hashtags = Hashtag::select('id', 'name', 'slug')
->where('slug', 'like', '%'.$htag.'%')
->where('slug', $this->likeOperator(), '%'.$htag.'%')
->whereHas('posts')
->limit(20)
->get();
@ -170,7 +179,7 @@ class SearchController extends Controller
// $tokens = Cache::remember($key, $ttl, function() use($tag) {
$htag = Str::contains($tag, ',') == true ? explode(',', $tag) : [$tag];
$hashtags = Place::select('id', 'name', 'slug', 'country')
->where('name', 'like', '%'.$htag[0].'%')
->where('name', $this->likeOperator(), '%'.$htag[0].'%')
->paginate(20);
$tags = [];
if ($hashtags->count() > 0) {
@ -242,7 +251,7 @@ class SearchController extends Controller
}
$users = Profile::select('status', 'domain', 'username', 'name', 'id')
->whereNull('status')
->where('username', 'like', '%'.$tag.'%')
->where('username', $this->likeOperator(), '%'.$tag.'%')
->limit(20)
->orderBy('domain')
->get();

@ -0,0 +1,53 @@
<?php
use App\Http\Controllers\SearchController;
use App\Models\User;
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
uses(LazilyRefreshDatabase::class);
/*
|--------------------------------------------------------------------------
| SearchController case-insensitive operator
|--------------------------------------------------------------------------
|
| Search must use ILIKE on PostgreSQL (whose LIKE is case-sensitive) so
| case-mismatched queries still match, mirroring MySQL/SQLite behaviour.
|
*/
function likeOperatorFor(string $driver): string
{
config(['database.default' => $driver]);
$controller = app(SearchController::class);
$m = new ReflectionMethod($controller, 'likeOperator');
$m->setAccessible(true);
return $m->invoke($controller);
}
it('uses ILIKE on postgres', function () {
expect(likeOperatorFor('pgsql'))->toBe('ilike');
});
it('uses LIKE on mysql', function () {
expect(likeOperatorFor('mysql'))->toBe('like');
});
it('uses LIKE on sqlite', function () {
expect(likeOperatorFor('sqlite'))->toBe('like');
});
it('search endpoint still responds on the default driver', function () {
$user = User::factory()->create();
$user->refresh();
$this->actingAs($user)
->getJson('/api/search?'.http_build_query([
'q' => 'photography',
'src' => 'metro',
'v' => 2,
'scope' => 'hashtag',
]))
->assertOk();
});
Loading…
Cancel
Save