Laravel Scout

Laravel Search: Laravel Scout

Start Laravel Scout

Install Packages

composer require laravel/scout
php artisan vendor:publish --provider="Laravel\Scout\ScoutServiceProvider"

Setting environment

# .env
SCOUT_DRIVER=database
SCOUT_PREFIX=scout
SCOUT_QUEUE=false
SCOUT_QUEUE_NAME=scout

Add the Laravel Scout to the Eloquent Model

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
use Laravel\Scout\Searchable;
 
class Post extends Model
{
    use Searchable;
}

Define search columns

The toSearchableArray() function is NOT necessary, but it will search all columns on the database table by default

Defining the specific search columns is highly recommended to improve your search performance.

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
use Laravel\Scout\Searchable;
 
class Post extends Model
{
    use Searchable;
 
    /**
     * Get the indexable data array for the model.
     *
     * @return array<string, mixed>
     */
    public function toSearchableArray(): array
    {
        return [
            'title' => $this->title,
            'content' => $this->content,
            'description' => $this->description,
        ];
    }
}

Search the post via laravel scout

$search_text = 'taiwan';

$PostModelPaginate = Post::search($search_text)
    ->whereIn('status', ['published'])
    ->paginate(
        perPage: 30,
        page: 1
    );

The search sql will looks like this on the PostgreSQL

select 
  * 
from 
  "post" 
where 
  (
    "post"."title" :: text ilike '%taiwan%' 
    or "post"."content" :: text ilike '%taiwan%' 
    or "post"."description" :: text ilike '%taiwan%'
  ) 
  and "status" in ('published') 
order by 
  "id" desc 
limit 
  30 offset 0

Change laravel scout search query for columns

  • SearchUsingPrefix will use the ilike 'taiwan%' to search the database instead of the ilike '%taiwan%'

  • SearchUsingFullText will use the full text search to search the database instead of the ilike '%taiwan%'

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
use Laravel\Scout\Searchable;
use Laravel\Scout\Attributes\SearchUsingFullText;
use Laravel\Scout\Attributes\SearchUsingPrefix;
 
class Post extends Model
{
    use Searchable;
 
    /**
     * Get the indexable data array for the model.
     *
     * @return array<string, mixed>
     */
    #[SearchUsingPrefix(['title', 'content'])]
    #[SearchUsingFullText(['description'])]
    public function toSearchableArray(): array
    {
        return [
            'title' => $this->title,
            'content' => $this->content,
            'description' => $this->description,
        ];
    }
}

We still use the same query to search the database

$search_text = 'taiwan';

$PostModelPaginate = Post::search($search_text)
    ->whereIn('status', ['published'])
    ->paginate(
        perPage: 30,
        page: 1
    );

The search sql will looks like this on the PostgreSQL

select 
  * 
from 
  "post" 
where 
  (
    "post"."title" :: text ilike 'taiwan%' 
    or "post"."content" :: text ilike 'taiwan%' 
    or (
      to_tsvector(
        'english', "post"."description"
      )
    ) @@ plainto_tsquery('english', ?)
  ) 
  and "status" in ('published') 
limit 
  30 offset 0

Update search database

Adding Records

use App\Models\Order;
 
$order = new Order;
 
// ...
 
$order->save();

Adding Records Via Query

use App\Models\Order;
 
Order::where('price', '>', 100)->searchable();

Updating Records

use App\Models\Order;
 
$order = Order::find(1);
 
// Update the order...
 
$order->save();

Removing Records

use App\Models\Order;
 
$order = Order::find(1);
 
$order->delete();
Order::where('price', '>', 100)->unsearchable();

Update search database with queue

You have to update the search database when you update your data.

But you can use queue to update search database behind the scenes.

// config/scout.php
return [
  'queue' => env('SCOUT_QUEUE', false),
]
// config/scout.php
return [
    'queue' => [
        'connection' => 'redis',
        'queue' => 'scout'
    ],
]

Add the queue job to your artisan queue command

php artisan queue:work --queue=high,low,scout

Add macro count method to Laravel Scout

// app/Providers/AppServiceProvider.php
use Illuminate\Support\Facades\Response;
use Illuminate\Support\ServiceProvider;
use Laravel\Scout\Builder;
 
/**
 * Bootstrap any application services.
 */
public function boot(): void
{
    Builder::macro('count', function () {
        return $this->engine()->getTotalCount(
            $this->engine()->search($this)
        );
    });
}
use App\Models\Order;
 
Post::search('Taiwan')->count();

Reference