2001 lines
50 KiB
Markdown
2001 lines
50 KiB
Markdown
# Laravel Scout
|
||
|
||
* Introduction
|
||
* Installation
|
||
* Queueing
|
||
* Driver Prerequisites
|
||
* Algolia
|
||
* Meilisearch
|
||
* Typesense
|
||
* Configuration
|
||
* Configuring Model Indexes
|
||
* Configuring Searchable Data
|
||
* Configuring the Model ID
|
||
* Configuring Search Engines per Model
|
||
* Identifying Users
|
||
* Database / Collection Engines
|
||
* Database Engine
|
||
* Collection Engine
|
||
* Indexing
|
||
* Batch Import
|
||
* Adding Records
|
||
* Updating Records
|
||
* Removing Records
|
||
* Pausing Indexing
|
||
* Conditionally Searchable Model Instances
|
||
* Searching
|
||
* Where Clauses
|
||
* Pagination
|
||
* Soft Deleting
|
||
* Customizing Engine Searches
|
||
* Custom Engines
|
||
|
||
## Introduction
|
||
|
||
[Laravel Scout](https://github.com/laravel/scout) provides a simple, driver-
|
||
based solution for adding full-text search to your [Eloquent
|
||
models](/docs/12.x/eloquent). Using model observers, Scout will automatically
|
||
keep your search indexes in sync with your Eloquent records.
|
||
|
||
Currently, Scout ships with [Algolia](https://www.algolia.com/),
|
||
[Meilisearch](https://www.meilisearch.com),
|
||
[Typesense](https://typesense.org), and MySQL / PostgreSQL (`database`)
|
||
drivers. In addition, Scout includes a "collection" driver that is designed
|
||
for local development usage and does not require any external dependencies or
|
||
third-party services. Furthermore, writing custom drivers is simple and you
|
||
are free to extend Scout with your own search implementations.
|
||
|
||
## Installation
|
||
|
||
First, install Scout via the Composer package manager:
|
||
|
||
|
||
|
||
1composer require laravel/scout
|
||
|
||
|
||
composer require laravel/scout
|
||
|
||
After installing Scout, you should publish the Scout configuration file using
|
||
the `vendor:publish` Artisan command. This command will publish the
|
||
`scout.php` configuration file to your application's `config` directory:
|
||
|
||
|
||
|
||
1php artisan vendor:publish --provider="Laravel\Scout\ScoutServiceProvider"
|
||
|
||
|
||
php artisan vendor:publish --provider="Laravel\Scout\ScoutServiceProvider"
|
||
|
||
Finally, add the `Laravel\Scout\Searchable` trait to the model you would like
|
||
to make searchable. This trait will register a model observer that will
|
||
automatically keep the model in sync with your search driver:
|
||
|
||
|
||
|
||
1<?php
|
||
|
||
2
|
||
|
||
3namespace App\Models;
|
||
|
||
4
|
||
|
||
5use Illuminate\Database\Eloquent\Model;
|
||
|
||
6use Laravel\Scout\Searchable;
|
||
|
||
7
|
||
|
||
8class Post extends Model
|
||
|
||
9{
|
||
|
||
10 use Searchable;
|
||
|
||
11}
|
||
|
||
|
||
<?php
|
||
|
||
namespace App\Models;
|
||
|
||
use Illuminate\Database\Eloquent\Model;
|
||
use Laravel\Scout\Searchable;
|
||
|
||
class Post extends Model
|
||
{
|
||
use Searchable;
|
||
}
|
||
|
||
### Queueing
|
||
|
||
While not strictly required to use Scout, you should strongly consider
|
||
configuring a [queue driver](/docs/12.x/queues) before using the library.
|
||
Running a queue worker will allow Scout to queue all operations that sync your
|
||
model information to your search indexes, providing much better response times
|
||
for your application's web interface.
|
||
|
||
Once you have configured a queue driver, set the value of the `queue` option
|
||
in your `config/scout.php` configuration file to `true`:
|
||
|
||
|
||
|
||
1'queue' => true,
|
||
|
||
|
||
'queue' => true,
|
||
|
||
Even when the `queue` option is set to `false`, it's important to remember
|
||
that some Scout drivers like Algolia and Meilisearch always index records
|
||
asynchronously. Meaning, even though the index operation has completed within
|
||
your Laravel application, the search engine itself may not reflect the new and
|
||
updated records immediately.
|
||
|
||
To specify the connection and queue that your Scout jobs utilize, you may
|
||
define the `queue` configuration option as an array:
|
||
|
||
|
||
|
||
1'queue' => [
|
||
|
||
2 'connection' => 'redis',
|
||
|
||
3 'queue' => 'scout'
|
||
|
||
4],
|
||
|
||
|
||
'queue' => [
|
||
'connection' => 'redis',
|
||
'queue' => 'scout'
|
||
],
|
||
|
||
Of course, if you customize the connection and queue that Scout jobs utilize,
|
||
you should run a queue worker to process jobs on that connection and queue:
|
||
|
||
|
||
|
||
1php artisan queue:work redis --queue=scout
|
||
|
||
|
||
php artisan queue:work redis --queue=scout
|
||
|
||
## Driver Prerequisites
|
||
|
||
### Algolia
|
||
|
||
When using the Algolia driver, you should configure your Algolia `id` and
|
||
`secret` credentials in your `config/scout.php` configuration file. Once your
|
||
credentials have been configured, you will also need to install the Algolia
|
||
PHP SDK via the Composer package manager:
|
||
|
||
|
||
|
||
1composer require algolia/algoliasearch-client-php
|
||
|
||
|
||
composer require algolia/algoliasearch-client-php
|
||
|
||
### Meilisearch
|
||
|
||
[Meilisearch](https://www.meilisearch.com) is a blazingly fast and open source
|
||
search engine. If you aren't sure how to install Meilisearch on your local
|
||
machine, you may use [Laravel Sail](/docs/12.x/sail#meilisearch), Laravel's
|
||
officially supported Docker development environment.
|
||
|
||
When using the Meilisearch driver you will need to install the Meilisearch PHP
|
||
SDK via the Composer package manager:
|
||
|
||
|
||
|
||
1composer require meilisearch/meilisearch-php http-interop/http-factory-guzzle
|
||
|
||
|
||
composer require meilisearch/meilisearch-php http-interop/http-factory-guzzle
|
||
|
||
Then, set the `SCOUT_DRIVER` environment variable as well as your Meilisearch
|
||
`host` and `key` credentials within your application's `.env` file:
|
||
|
||
|
||
|
||
1SCOUT_DRIVER=meilisearch
|
||
|
||
2MEILISEARCH_HOST=http://127.0.0.1:7700
|
||
|
||
3MEILISEARCH_KEY=masterKey
|
||
|
||
|
||
SCOUT_DRIVER=meilisearch
|
||
MEILISEARCH_HOST=http://127.0.0.1:7700
|
||
MEILISEARCH_KEY=masterKey
|
||
|
||
For more information regarding Meilisearch, please consult the [Meilisearch
|
||
documentation](https://docs.meilisearch.com/learn/getting_started/quick_start.html).
|
||
|
||
In addition, you should ensure that you install a version of
|
||
`meilisearch/meilisearch-php` that is compatible with your Meilisearch binary
|
||
version by reviewing [Meilisearch's documentation regarding binary
|
||
compatibility](https://github.com/meilisearch/meilisearch-php#-compatibility-
|
||
with-meilisearch).
|
||
|
||
When upgrading Scout on an application that utilizes Meilisearch, you should
|
||
always [review any additional breaking
|
||
changes](https://github.com/meilisearch/Meilisearch/releases) to the
|
||
Meilisearch service itself.
|
||
|
||
### Typesense
|
||
|
||
[Typesense](https://typesense.org) is a lightning-fast, open source search
|
||
engine and supports keyword search, semantic search, geo search, and vector
|
||
search.
|
||
|
||
You can [self-host](https://typesense.org/docs/guide/install-
|
||
typesense.html#option-2-local-machine-self-hosting) Typesense or use
|
||
[Typesense Cloud](https://cloud.typesense.org).
|
||
|
||
To get started using Typesense with Scout, install the Typesense PHP SDK via
|
||
the Composer package manager:
|
||
|
||
|
||
|
||
1composer require typesense/typesense-php
|
||
|
||
|
||
composer require typesense/typesense-php
|
||
|
||
Then, set the `SCOUT_DRIVER` environment variable as well as your Typesense
|
||
host and API key credentials within your application's .env file:
|
||
|
||
|
||
|
||
1SCOUT_DRIVER=typesense
|
||
|
||
2TYPESENSE_API_KEY=masterKey
|
||
|
||
3TYPESENSE_HOST=localhost
|
||
|
||
|
||
SCOUT_DRIVER=typesense
|
||
TYPESENSE_API_KEY=masterKey
|
||
TYPESENSE_HOST=localhost
|
||
|
||
If you are using [Laravel Sail](/docs/12.x/sail), you may need to adjust the
|
||
`TYPESENSE_HOST` environment variable to match the Docker container name. You
|
||
may also optionally specify your installation's port, path, and protocol:
|
||
|
||
|
||
|
||
1TYPESENSE_PORT=8108
|
||
|
||
2TYPESENSE_PATH=
|
||
|
||
3TYPESENSE_PROTOCOL=http
|
||
|
||
|
||
TYPESENSE_PORT=8108
|
||
TYPESENSE_PATH=
|
||
TYPESENSE_PROTOCOL=http
|
||
|
||
Additional settings and schema definitions for your Typesense collections can
|
||
be found within your application's `config/scout.php` configuration file. For
|
||
more information regarding Typesense, please consult the [Typesense
|
||
documentation](https://typesense.org/docs/guide/#quick-start).
|
||
|
||
#### Preparing Data for Storage in Typesense
|
||
|
||
When utilizing Typesense, your searchable models must define a
|
||
`toSearchableArray` method that casts your model's primary key to a string and
|
||
creation date to a UNIX timestamp:
|
||
|
||
|
||
|
||
1/**
|
||
|
||
2 * Get the indexable data array for the model.
|
||
|
||
3 *
|
||
|
||
4 * @return array<string, mixed>
|
||
|
||
5 */
|
||
|
||
6public function toSearchableArray(): array
|
||
|
||
7{
|
||
|
||
8 return array_merge($this->toArray(),[
|
||
|
||
9 'id' => (string) $this->id,
|
||
|
||
10 'created_at' => $this->created_at->timestamp,
|
||
|
||
11 ]);
|
||
|
||
12}
|
||
|
||
|
||
/**
|
||
* Get the indexable data array for the model.
|
||
*
|
||
* @return array<string, mixed>
|
||
*/
|
||
public function toSearchableArray(): array
|
||
{
|
||
return array_merge($this->toArray(),[
|
||
'id' => (string) $this->id,
|
||
'created_at' => $this->created_at->timestamp,
|
||
]);
|
||
}
|
||
|
||
You should also define your Typesense collection schemas in your application's
|
||
`config/scout.php` file. A collection schema describes the data types of each
|
||
field that is searchable via Typesense. For more information on all available
|
||
schema options, please consult the [Typesense
|
||
documentation](https://typesense.org/docs/latest/api/collections.html#schema-
|
||
parameters).
|
||
|
||
If you need to change your Typesense collection's schema after it has been
|
||
defined, you may either run `scout:flush` and `scout:import`, which will
|
||
delete all existing indexed data and recreate the schema. Or, you may use
|
||
Typesense's API to modify the collection's schema without removing any indexed
|
||
data.
|
||
|
||
If your searchable model is soft deletable, you should define a
|
||
`__soft_deleted` field in the model's corresponding Typesense schema within
|
||
your application's `config/scout.php` configuration file:
|
||
|
||
|
||
|
||
1User::class => [
|
||
|
||
2 'collection-schema' => [
|
||
|
||
3 'fields' => [
|
||
|
||
4 // ...
|
||
|
||
5 [
|
||
|
||
6 'name' => '__soft_deleted',
|
||
|
||
7 'type' => 'int32',
|
||
|
||
8 'optional' => true,
|
||
|
||
9 ],
|
||
|
||
10 ],
|
||
|
||
11 ],
|
||
|
||
12],
|
||
|
||
|
||
User::class => [
|
||
'collection-schema' => [
|
||
'fields' => [
|
||
// ...
|
||
[
|
||
'name' => '__soft_deleted',
|
||
'type' => 'int32',
|
||
'optional' => true,
|
||
],
|
||
],
|
||
],
|
||
],
|
||
|
||
#### Dynamic Search Parameters
|
||
|
||
Typesense allows you to modify your [search
|
||
parameters](https://typesense.org/docs/latest/api/search.html#search-
|
||
parameters) dynamically when performing a search operation via the `options`
|
||
method:
|
||
|
||
|
||
|
||
1use App\Models\Todo;
|
||
|
||
2
|
||
|
||
3Todo::search('Groceries')->options([
|
||
|
||
4 'query_by' => 'title, description'
|
||
|
||
5])->get();
|
||
|
||
|
||
use App\Models\Todo;
|
||
|
||
Todo::search('Groceries')->options([
|
||
'query_by' => 'title, description'
|
||
])->get();
|
||
|
||
## Configuration
|
||
|
||
### Configuring Model Indexes
|
||
|
||
Each Eloquent model is synced with a given search "index", which contains all
|
||
of the searchable records for that model. In other words, you can think of
|
||
each index like a MySQL table. By default, each model will be persisted to an
|
||
index matching the model's typical "table" name. Typically, this is the plural
|
||
form of the model name; however, you are free to customize the model's index
|
||
by overriding the `searchableAs` method on the model:
|
||
|
||
|
||
|
||
1<?php
|
||
|
||
2
|
||
|
||
3namespace App\Models;
|
||
|
||
4
|
||
|
||
5use Illuminate\Database\Eloquent\Model;
|
||
|
||
6use Laravel\Scout\Searchable;
|
||
|
||
7
|
||
|
||
8class Post extends Model
|
||
|
||
9{
|
||
|
||
10 use Searchable;
|
||
|
||
11
|
||
|
||
12 /**
|
||
|
||
13 * Get the name of the index associated with the model.
|
||
|
||
14 */
|
||
|
||
15 public function searchableAs(): string
|
||
|
||
16 {
|
||
|
||
17 return 'posts_index';
|
||
|
||
18 }
|
||
|
||
19}
|
||
|
||
|
||
<?php
|
||
|
||
namespace App\Models;
|
||
|
||
use Illuminate\Database\Eloquent\Model;
|
||
use Laravel\Scout\Searchable;
|
||
|
||
class Post extends Model
|
||
{
|
||
use Searchable;
|
||
|
||
/**
|
||
* Get the name of the index associated with the model.
|
||
*/
|
||
public function searchableAs(): string
|
||
{
|
||
return 'posts_index';
|
||
}
|
||
}
|
||
|
||
### Configuring Searchable Data
|
||
|
||
By default, the entire `toArray` form of a given model will be persisted to
|
||
its search index. If you would like to customize the data that is synchronized
|
||
to the search index, you may override the `toSearchableArray` method on the
|
||
model:
|
||
|
||
|
||
|
||
1<?php
|
||
|
||
2
|
||
|
||
3namespace App\Models;
|
||
|
||
4
|
||
|
||
5use Illuminate\Database\Eloquent\Model;
|
||
|
||
6use Laravel\Scout\Searchable;
|
||
|
||
7
|
||
|
||
8class Post extends Model
|
||
|
||
9{
|
||
|
||
10 use Searchable;
|
||
|
||
11
|
||
|
||
12 /**
|
||
|
||
13 * Get the indexable data array for the model.
|
||
|
||
14 *
|
||
|
||
15 * @return array<string, mixed>
|
||
|
||
16 */
|
||
|
||
17 public function toSearchableArray(): array
|
||
|
||
18 {
|
||
|
||
19 $array = $this->toArray();
|
||
|
||
20
|
||
|
||
21 // Customize the data array...
|
||
|
||
22
|
||
|
||
23 return $array;
|
||
|
||
24 }
|
||
|
||
25}
|
||
|
||
|
||
<?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
|
||
{
|
||
$array = $this->toArray();
|
||
|
||
// Customize the data array...
|
||
|
||
return $array;
|
||
}
|
||
}
|
||
|
||
Some search engines such as Meilisearch will only perform filter operations
|
||
(`>`, `<`, etc.) on data of the correct type. So, when using these search
|
||
engines and customizing your searchable data, you should ensure that numeric
|
||
values are cast to their correct type:
|
||
|
||
|
||
|
||
1public function toSearchableArray()
|
||
|
||
2{
|
||
|
||
3 return [
|
||
|
||
4 'id' => (int) $this->id,
|
||
|
||
5 'name' => $this->name,
|
||
|
||
6 'price' => (float) $this->price,
|
||
|
||
7 ];
|
||
|
||
8}
|
||
|
||
|
||
public function toSearchableArray()
|
||
{
|
||
return [
|
||
'id' => (int) $this->id,
|
||
'name' => $this->name,
|
||
'price' => (float) $this->price,
|
||
];
|
||
}
|
||
|
||
#### Configuring Index Settings (Algolia)
|
||
|
||
Sometimes you may want to configure additional settings on your Algolia
|
||
indexes. While you can manage these settings via the Algolia UI, it is
|
||
sometimes more efficient to manage the desired state of your index
|
||
configuration directly from your application's `config/scout.php`
|
||
configuration file.
|
||
|
||
This approach allows you to deploy these settings through your application's
|
||
automated deployment pipeline, avoiding manual configuration and ensuring
|
||
consistency across multiple environments. You may configure filterable
|
||
attributes, ranking, faceting, or [any other supported
|
||
settings](https://www.algolia.com/doc/rest-
|
||
api/search/#tag/Indices/operation/setSettings).
|
||
|
||
To get started, add settings for each index in your application's
|
||
`config/scout.php` configuration file:
|
||
|
||
|
||
|
||
1use App\Models\User;
|
||
|
||
2use App\Models\Flight;
|
||
|
||
3
|
||
|
||
4'algolia' => [
|
||
|
||
5 'id' => env('ALGOLIA_APP_ID', ''),
|
||
|
||
6 'secret' => env('ALGOLIA_SECRET', ''),
|
||
|
||
7 'index-settings' => [
|
||
|
||
8 User::class => [
|
||
|
||
9 'searchableAttributes' => ['id', 'name', 'email'],
|
||
|
||
10 'attributesForFaceting'=> ['filterOnly(email)'],
|
||
|
||
11 // Other settings fields...
|
||
|
||
12 ],
|
||
|
||
13 Flight::class => [
|
||
|
||
14 'searchableAttributes'=> ['id', 'destination'],
|
||
|
||
15 ],
|
||
|
||
16 ],
|
||
|
||
17],
|
||
|
||
|
||
use App\Models\User;
|
||
use App\Models\Flight;
|
||
|
||
'algolia' => [
|
||
'id' => env('ALGOLIA_APP_ID', ''),
|
||
'secret' => env('ALGOLIA_SECRET', ''),
|
||
'index-settings' => [
|
||
User::class => [
|
||
'searchableAttributes' => ['id', 'name', 'email'],
|
||
'attributesForFaceting'=> ['filterOnly(email)'],
|
||
// Other settings fields...
|
||
],
|
||
Flight::class => [
|
||
'searchableAttributes'=> ['id', 'destination'],
|
||
],
|
||
],
|
||
],
|
||
|
||
If the model underlying a given index is soft deletable and is included in the
|
||
`index-settings` array, Scout will automatically include support for faceting
|
||
on soft deleted models on that index. If you have no other faceting attributes
|
||
to define for a soft deletable model index, you may simply add an empty entry
|
||
to the `index-settings` array for that model:
|
||
|
||
|
||
|
||
1'index-settings' => [
|
||
|
||
2 Flight::class => []
|
||
|
||
3],
|
||
|
||
|
||
'index-settings' => [
|
||
Flight::class => []
|
||
],
|
||
|
||
After configuring your application's index settings, you must invoke the
|
||
`scout:sync-index-settings` Artisan command. This command will inform Algolia
|
||
of your currently configured index settings. For convenience, you may wish to
|
||
make this command part of your deployment process:
|
||
|
||
|
||
|
||
1php artisan scout:sync-index-settings
|
||
|
||
|
||
php artisan scout:sync-index-settings
|
||
|
||
#### Configuring Filterable Data and Index Settings (Meilisearch)
|
||
|
||
Unlike Scout's other drivers, Meilisearch requires you to pre-define index
|
||
search settings such as filterable attributes, sortable attributes, and [other
|
||
supported settings
|
||
fields](https://docs.meilisearch.com/reference/api/settings.html).
|
||
|
||
Filterable attributes are any attributes you plan to filter on when invoking
|
||
Scout's `where` method, while sortable attributes are any attributes you plan
|
||
to sort by when invoking Scout's `orderBy` method. To define your index
|
||
settings, adjust the `index-settings` portion of your `meilisearch`
|
||
configuration entry in your application's `scout` configuration file:
|
||
|
||
|
||
|
||
1use App\Models\User;
|
||
|
||
2use App\Models\Flight;
|
||
|
||
3
|
||
|
||
4'meilisearch' => [
|
||
|
||
5 'host' => env('MEILISEARCH_HOST', 'http://localhost:7700'),
|
||
|
||
6 'key' => env('MEILISEARCH_KEY', null),
|
||
|
||
7 'index-settings' => [
|
||
|
||
8 User::class => [
|
||
|
||
9 'filterableAttributes'=> ['id', 'name', 'email'],
|
||
|
||
10 'sortableAttributes' => ['created_at'],
|
||
|
||
11 // Other settings fields...
|
||
|
||
12 ],
|
||
|
||
13 Flight::class => [
|
||
|
||
14 'filterableAttributes'=> ['id', 'destination'],
|
||
|
||
15 'sortableAttributes' => ['updated_at'],
|
||
|
||
16 ],
|
||
|
||
17 ],
|
||
|
||
18],
|
||
|
||
|
||
use App\Models\User;
|
||
use App\Models\Flight;
|
||
|
||
'meilisearch' => [
|
||
'host' => env('MEILISEARCH_HOST', 'http://localhost:7700'),
|
||
'key' => env('MEILISEARCH_KEY', null),
|
||
'index-settings' => [
|
||
User::class => [
|
||
'filterableAttributes'=> ['id', 'name', 'email'],
|
||
'sortableAttributes' => ['created_at'],
|
||
// Other settings fields...
|
||
],
|
||
Flight::class => [
|
||
'filterableAttributes'=> ['id', 'destination'],
|
||
'sortableAttributes' => ['updated_at'],
|
||
],
|
||
],
|
||
],
|
||
|
||
If the model underlying a given index is soft deletable and is included in the
|
||
`index-settings` array, Scout will automatically include support for filtering
|
||
on soft deleted models on that index. If you have no other filterable or
|
||
sortable attributes to define for a soft deletable model index, you may simply
|
||
add an empty entry to the `index-settings` array for that model:
|
||
|
||
|
||
|
||
1'index-settings' => [
|
||
|
||
2 Flight::class => []
|
||
|
||
3],
|
||
|
||
|
||
'index-settings' => [
|
||
Flight::class => []
|
||
],
|
||
|
||
After configuring your application's index settings, you must invoke the
|
||
`scout:sync-index-settings` Artisan command. This command will inform
|
||
Meilisearch of your currently configured index settings. For convenience, you
|
||
may wish to make this command part of your deployment process:
|
||
|
||
|
||
|
||
1php artisan scout:sync-index-settings
|
||
|
||
|
||
php artisan scout:sync-index-settings
|
||
|
||
### Configuring the Model ID
|
||
|
||
By default, Scout will use the primary key of the model as the model's unique
|
||
ID / key that is stored in the search index. If you need to customize this
|
||
behavior, you may override the `getScoutKey` and the `getScoutKeyName` methods
|
||
on the model:
|
||
|
||
|
||
|
||
1<?php
|
||
|
||
2
|
||
|
||
3namespace App\Models;
|
||
|
||
4
|
||
|
||
5use Illuminate\Database\Eloquent\Model;
|
||
|
||
6use Laravel\Scout\Searchable;
|
||
|
||
7
|
||
|
||
8class User extends Model
|
||
|
||
9{
|
||
|
||
10 use Searchable;
|
||
|
||
11
|
||
|
||
12 /**
|
||
|
||
13 * Get the value used to index the model.
|
||
|
||
14 */
|
||
|
||
15 public function getScoutKey(): mixed
|
||
|
||
16 {
|
||
|
||
17 return $this->email;
|
||
|
||
18 }
|
||
|
||
19
|
||
|
||
20 /**
|
||
|
||
21 * Get the key name used to index the model.
|
||
|
||
22 */
|
||
|
||
23 public function getScoutKeyName(): mixed
|
||
|
||
24 {
|
||
|
||
25 return 'email';
|
||
|
||
26 }
|
||
|
||
27}
|
||
|
||
|
||
<?php
|
||
|
||
namespace App\Models;
|
||
|
||
use Illuminate\Database\Eloquent\Model;
|
||
use Laravel\Scout\Searchable;
|
||
|
||
class User extends Model
|
||
{
|
||
use Searchable;
|
||
|
||
/**
|
||
* Get the value used to index the model.
|
||
*/
|
||
public function getScoutKey(): mixed
|
||
{
|
||
return $this->email;
|
||
}
|
||
|
||
/**
|
||
* Get the key name used to index the model.
|
||
*/
|
||
public function getScoutKeyName(): mixed
|
||
{
|
||
return 'email';
|
||
}
|
||
}
|
||
|
||
### Configuring Search Engines per Model
|
||
|
||
When searching, Scout will typically use the default search engine specified
|
||
in your application's `scout` configuration file. However, the search engine
|
||
for a particular model can be changed by overriding the `searchableUsing`
|
||
method on the model:
|
||
|
||
|
||
|
||
1<?php
|
||
|
||
2
|
||
|
||
3namespace App\Models;
|
||
|
||
4
|
||
|
||
5use Illuminate\Database\Eloquent\Model;
|
||
|
||
6use Laravel\Scout\Engines\Engine;
|
||
|
||
7use Laravel\Scout\EngineManager;
|
||
|
||
8use Laravel\Scout\Searchable;
|
||
|
||
9
|
||
|
||
10class User extends Model
|
||
|
||
11{
|
||
|
||
12 use Searchable;
|
||
|
||
13
|
||
|
||
14 /**
|
||
|
||
15 * Get the engine used to index the model.
|
||
|
||
16 */
|
||
|
||
17 public function searchableUsing(): Engine
|
||
|
||
18 {
|
||
|
||
19 return app(EngineManager::class)->engine('meilisearch');
|
||
|
||
20 }
|
||
|
||
21}
|
||
|
||
|
||
<?php
|
||
|
||
namespace App\Models;
|
||
|
||
use Illuminate\Database\Eloquent\Model;
|
||
use Laravel\Scout\Engines\Engine;
|
||
use Laravel\Scout\EngineManager;
|
||
use Laravel\Scout\Searchable;
|
||
|
||
class User extends Model
|
||
{
|
||
use Searchable;
|
||
|
||
/**
|
||
* Get the engine used to index the model.
|
||
*/
|
||
public function searchableUsing(): Engine
|
||
{
|
||
return app(EngineManager::class)->engine('meilisearch');
|
||
}
|
||
}
|
||
|
||
### Identifying Users
|
||
|
||
Scout also allows you to auto identify users when using
|
||
[Algolia](https://algolia.com). Associating the authenticated user with search
|
||
operations may be helpful when viewing your search analytics within Algolia's
|
||
dashboard. You can enable user identification by defining a `SCOUT_IDENTIFY`
|
||
environment variable as `true` in your application's `.env` file:
|
||
|
||
|
||
|
||
1SCOUT_IDENTIFY=true
|
||
|
||
|
||
SCOUT_IDENTIFY=true
|
||
|
||
Enabling this feature will also pass the request's IP address and your
|
||
authenticated user's primary identifier to Algolia so this data is associated
|
||
with any search request that is made by the user.
|
||
|
||
## Database / Collection Engines
|
||
|
||
### Database Engine
|
||
|
||
The database engine currently supports MySQL and PostgreSQL.
|
||
|
||
If your application interacts with small to medium sized databases or has a
|
||
light workload, you may find it more convenient to get started with Scout's
|
||
"database" engine. The database engine will use "where like" clauses and full
|
||
text indexes when filtering results from your existing database to determine
|
||
the applicable search results for your query.
|
||
|
||
To use the database engine, you may simply set the value of the `SCOUT_DRIVER`
|
||
environment variable to `database`, or specify the `database` driver directly
|
||
in your application's `scout` configuration file:
|
||
|
||
|
||
|
||
1SCOUT_DRIVER=database
|
||
|
||
|
||
SCOUT_DRIVER=database
|
||
|
||
Once you have specified the database engine as your preferred driver, you must
|
||
configure your searchable data. Then, you may start executing search queries
|
||
against your models. Search engine indexing, such as the indexing needed to
|
||
seed Algolia, Meilisearch or Typesense indexes, is unnecessary when using the
|
||
database engine.
|
||
|
||
#### Customizing Database Searching Strategies
|
||
|
||
By default, the database engine will execute a "where like" query against
|
||
every model attribute that you have configured as searchable. However, in some
|
||
situations, this may result in poor performance. Therefore, the database
|
||
engine's search strategy can be configured so that some specified columns
|
||
utilize full text search queries or only use "where like" constraints to
|
||
search the prefixes of strings (`example%`) instead of searching within the
|
||
entire string (`%example%`).
|
||
|
||
To define this behavior, you may assign PHP attributes to your model's
|
||
`toSearchableArray` method. Any columns that are not assigned additional
|
||
search strategy behavior will continue to use the default "where like"
|
||
strategy:
|
||
|
||
|
||
|
||
1use Laravel\Scout\Attributes\SearchUsingFullText;
|
||
|
||
2use Laravel\Scout\Attributes\SearchUsingPrefix;
|
||
|
||
3
|
||
|
||
4/**
|
||
|
||
5 * Get the indexable data array for the model.
|
||
|
||
6 *
|
||
|
||
7 * @return array<string, mixed>
|
||
|
||
8 */
|
||
|
||
9#[SearchUsingPrefix(['id', 'email'])]
|
||
|
||
10#[SearchUsingFullText(['bio'])]
|
||
|
||
11public function toSearchableArray(): array
|
||
|
||
12{
|
||
|
||
13 return [
|
||
|
||
14 'id' => $this->id,
|
||
|
||
15 'name' => $this->name,
|
||
|
||
16 'email' => $this->email,
|
||
|
||
17 'bio' => $this->bio,
|
||
|
||
18 ];
|
||
|
||
19}
|
||
|
||
|
||
use Laravel\Scout\Attributes\SearchUsingFullText;
|
||
use Laravel\Scout\Attributes\SearchUsingPrefix;
|
||
|
||
/**
|
||
* Get the indexable data array for the model.
|
||
*
|
||
* @return array<string, mixed>
|
||
*/
|
||
#[SearchUsingPrefix(['id', 'email'])]
|
||
#[SearchUsingFullText(['bio'])]
|
||
public function toSearchableArray(): array
|
||
{
|
||
return [
|
||
'id' => $this->id,
|
||
'name' => $this->name,
|
||
'email' => $this->email,
|
||
'bio' => $this->bio,
|
||
];
|
||
}
|
||
|
||
Before specifying that a column should use full text query constraints, ensure
|
||
that the column has been assigned a [full text
|
||
index](/docs/12.x/migrations#available-index-types).
|
||
|
||
### Collection Engine
|
||
|
||
While you are free to use the Algolia, Meilisearch, or Typesense search
|
||
engines during local development, you may find it more convenient to get
|
||
started with the "collection" engine. The collection engine will use "where"
|
||
clauses and collection filtering on results from your existing database to
|
||
determine the applicable search results for your query. When using this
|
||
engine, it is not necessary to "index" your searchable models, as they will
|
||
simply be retrieved from your local database.
|
||
|
||
To use the collection engine, you may simply set the value of the
|
||
`SCOUT_DRIVER` environment variable to `collection`, or specify the
|
||
`collection` driver directly in your application's `scout` configuration file:
|
||
|
||
|
||
|
||
1SCOUT_DRIVER=collection
|
||
|
||
|
||
SCOUT_DRIVER=collection
|
||
|
||
Once you have specified the collection driver as your preferred driver, you
|
||
may start executing search queries against your models. Search engine
|
||
indexing, such as the indexing needed to seed Algolia, Meilisearch, or
|
||
Typesense indexes, is unnecessary when using the collection engine.
|
||
|
||
#### Differences From Database Engine
|
||
|
||
On first glance, the "database" and "collections" engines are fairly similar.
|
||
They both interact directly with your database to retrieve search results.
|
||
However, the collection engine does not utilize full text indexes or `LIKE`
|
||
clauses to find matching records. Instead, it pulls all possible records and
|
||
uses Laravel's `Str::is` helper to determine if the search string exists
|
||
within the model attribute values.
|
||
|
||
The collection engine is the most portable search engine as it works across
|
||
all relational databases supported by Laravel (including SQLite and SQL
|
||
Server); however, it is less efficient than Scout's database engine.
|
||
|
||
## Indexing
|
||
|
||
### Batch Import
|
||
|
||
If you are installing Scout into an existing project, you may already have
|
||
database records you need to import into your indexes. Scout provides a
|
||
`scout:import` Artisan command that you may use to import all of your existing
|
||
records into your search indexes:
|
||
|
||
|
||
|
||
1php artisan scout:import "App\Models\Post"
|
||
|
||
|
||
php artisan scout:import "App\Models\Post"
|
||
|
||
The `scout:queue-import` command may be used to import all of your existing
|
||
records using [queued jobs](/docs/12.x/queues):
|
||
|
||
|
||
|
||
1php artisan scout:queue-import "App\Models\Post" --chunk=500
|
||
|
||
|
||
php artisan scout:queue-import "App\Models\Post" --chunk=500
|
||
|
||
The `flush` command may be used to remove all of a model's records from your
|
||
search indexes:
|
||
|
||
|
||
|
||
1php artisan scout:flush "App\Models\Post"
|
||
|
||
|
||
php artisan scout:flush "App\Models\Post"
|
||
|
||
#### Modifying the Import Query
|
||
|
||
If you would like to modify the query that is used to retrieve all of your
|
||
models for batch importing, you may define a `makeAllSearchableUsing` method
|
||
on your model. This is a great place to add any eager relationship loading
|
||
that may be necessary before importing your models:
|
||
|
||
|
||
|
||
1use Illuminate\Database\Eloquent\Builder;
|
||
|
||
2
|
||
|
||
3/**
|
||
|
||
4 * Modify the query used to retrieve models when making all of the models searchable.
|
||
|
||
5 */
|
||
|
||
6protected function makeAllSearchableUsing(Builder $query): Builder
|
||
|
||
7{
|
||
|
||
8 return $query->with('author');
|
||
|
||
9}
|
||
|
||
|
||
use Illuminate\Database\Eloquent\Builder;
|
||
|
||
/**
|
||
* Modify the query used to retrieve models when making all of the models searchable.
|
||
*/
|
||
protected function makeAllSearchableUsing(Builder $query): Builder
|
||
{
|
||
return $query->with('author');
|
||
}
|
||
|
||
The `makeAllSearchableUsing` method may not be applicable when using a queue
|
||
to batch import models. Relationships are [not
|
||
restored](/docs/12.x/queues#handling-relationships) when model collections are
|
||
processed by jobs.
|
||
|
||
### Adding Records
|
||
|
||
Once you have added the `Laravel\Scout\Searchable` trait to a model, all you
|
||
need to do is `save` or `create` a model instance and it will automatically be
|
||
added to your search index. If you have configured Scout to use queues this
|
||
operation will be performed in the background by your queue worker:
|
||
|
||
|
||
|
||
1use App\Models\Order;
|
||
|
||
2
|
||
|
||
3$order = new Order;
|
||
|
||
4
|
||
|
||
5// ...
|
||
|
||
6
|
||
|
||
7$order->save();
|
||
|
||
|
||
use App\Models\Order;
|
||
|
||
$order = new Order;
|
||
|
||
// ...
|
||
|
||
$order->save();
|
||
|
||
#### Adding Records via Query
|
||
|
||
If you would like to add a collection of models to your search index via an
|
||
Eloquent query, you may chain the `searchable` method onto the Eloquent query.
|
||
The `searchable` method will [chunk the results](/docs/12.x/eloquent#chunking-
|
||
results) of the query and add the records to your search index. Again, if you
|
||
have configured Scout to use queues, all of the chunks will be imported in the
|
||
background by your queue workers:
|
||
|
||
|
||
|
||
1use App\Models\Order;
|
||
|
||
2
|
||
|
||
3Order::where('price', '>', 100)->searchable();
|
||
|
||
|
||
use App\Models\Order;
|
||
|
||
Order::where('price', '>', 100)->searchable();
|
||
|
||
You may also call the `searchable` method on an Eloquent relationship
|
||
instance:
|
||
|
||
|
||
|
||
1$user->orders()->searchable();
|
||
|
||
|
||
$user->orders()->searchable();
|
||
|
||
Or, if you already have a collection of Eloquent models in memory, you may
|
||
call the `searchable` method on the collection instance to add the model
|
||
instances to their corresponding index:
|
||
|
||
|
||
|
||
1$orders->searchable();
|
||
|
||
|
||
$orders->searchable();
|
||
|
||
The `searchable` method can be considered an "upsert" operation. In other
|
||
words, if the model record is already in your index, it will be updated. If it
|
||
does not exist in the search index, it will be added to the index.
|
||
|
||
### Updating Records
|
||
|
||
To update a searchable model, you only need to update the model instance's
|
||
properties and `save` the model to your database. Scout will automatically
|
||
persist the changes to your search index:
|
||
|
||
|
||
|
||
1use App\Models\Order;
|
||
|
||
2
|
||
|
||
3$order = Order::find(1);
|
||
|
||
4
|
||
|
||
5// Update the order...
|
||
|
||
6
|
||
|
||
7$order->save();
|
||
|
||
|
||
use App\Models\Order;
|
||
|
||
$order = Order::find(1);
|
||
|
||
// Update the order...
|
||
|
||
$order->save();
|
||
|
||
You may also invoke the `searchable` method on an Eloquent query instance to
|
||
update a collection of models. If the models do not exist in your search
|
||
index, they will be created:
|
||
|
||
|
||
|
||
1Order::where('price', '>', 100)->searchable();
|
||
|
||
|
||
Order::where('price', '>', 100)->searchable();
|
||
|
||
If you would like to update the search index records for all of the models in
|
||
a relationship, you may invoke the `searchable` on the relationship instance:
|
||
|
||
|
||
|
||
1$user->orders()->searchable();
|
||
|
||
|
||
$user->orders()->searchable();
|
||
|
||
Or, if you already have a collection of Eloquent models in memory, you may
|
||
call the `searchable` method on the collection instance to update the model
|
||
instances in their corresponding index:
|
||
|
||
|
||
|
||
1$orders->searchable();
|
||
|
||
|
||
$orders->searchable();
|
||
|
||
#### Modifying Records Before Importing
|
||
|
||
Sometimes you may need to prepare the collection of models before they are
|
||
made searchable. For instance, you may want to eager load a relationship so
|
||
that the relationship data can be efficiently added to your search index. To
|
||
accomplish this, define a `makeSearchableUsing` method on the corresponding
|
||
model:
|
||
|
||
|
||
|
||
1use Illuminate\Database\Eloquent\Collection;
|
||
|
||
2
|
||
|
||
3/**
|
||
|
||
4 * Modify the collection of models being made searchable.
|
||
|
||
5 */
|
||
|
||
6public function makeSearchableUsing(Collection $models): Collection
|
||
|
||
7{
|
||
|
||
8 return $models->load('author');
|
||
|
||
9}
|
||
|
||
|
||
use Illuminate\Database\Eloquent\Collection;
|
||
|
||
/**
|
||
* Modify the collection of models being made searchable.
|
||
*/
|
||
public function makeSearchableUsing(Collection $models): Collection
|
||
{
|
||
return $models->load('author');
|
||
}
|
||
|
||
### Removing Records
|
||
|
||
To remove a record from your index you may simply `delete` the model from the
|
||
database. This may be done even if you are using [soft
|
||
deleted](/docs/12.x/eloquent#soft-deleting) models:
|
||
|
||
|
||
|
||
1use App\Models\Order;
|
||
|
||
2
|
||
|
||
3$order = Order::find(1);
|
||
|
||
4
|
||
|
||
5$order->delete();
|
||
|
||
|
||
use App\Models\Order;
|
||
|
||
$order = Order::find(1);
|
||
|
||
$order->delete();
|
||
|
||
If you do not want to retrieve the model before deleting the record, you may
|
||
use the `unsearchable` method on an Eloquent query instance:
|
||
|
||
|
||
|
||
1Order::where('price', '>', 100)->unsearchable();
|
||
|
||
|
||
Order::where('price', '>', 100)->unsearchable();
|
||
|
||
If you would like to remove the search index records for all of the models in
|
||
a relationship, you may invoke the `unsearchable` on the relationship
|
||
instance:
|
||
|
||
|
||
|
||
1$user->orders()->unsearchable();
|
||
|
||
|
||
$user->orders()->unsearchable();
|
||
|
||
Or, if you already have a collection of Eloquent models in memory, you may
|
||
call the `unsearchable` method on the collection instance to remove the model
|
||
instances from their corresponding index:
|
||
|
||
|
||
|
||
1$orders->unsearchable();
|
||
|
||
|
||
$orders->unsearchable();
|
||
|
||
To remove all of the model records from their corresponding index, you may
|
||
invoke the `removeAllFromSearch` method:
|
||
|
||
|
||
|
||
1Order::removeAllFromSearch();
|
||
|
||
|
||
Order::removeAllFromSearch();
|
||
|
||
### Pausing Indexing
|
||
|
||
Sometimes you may need to perform a batch of Eloquent operations on a model
|
||
without syncing the model data to your search index. You may do this using the
|
||
`withoutSyncingToSearch` method. This method accepts a single closure which
|
||
will be immediately executed. Any model operations that occur within the
|
||
closure will not be synced to the model's index:
|
||
|
||
|
||
|
||
1use App\Models\Order;
|
||
|
||
2
|
||
|
||
3Order::withoutSyncingToSearch(function () {
|
||
|
||
4 // Perform model actions...
|
||
|
||
5});
|
||
|
||
|
||
use App\Models\Order;
|
||
|
||
Order::withoutSyncingToSearch(function () {
|
||
// Perform model actions...
|
||
});
|
||
|
||
### Conditionally Searchable Model Instances
|
||
|
||
Sometimes you may need to only make a model searchable under certain
|
||
conditions. For example, imagine you have `App\Models\Post` model that may be
|
||
in one of two states: "draft" and "published". You may only want to allow
|
||
"published" posts to be searchable. To accomplish this, you may define a
|
||
`shouldBeSearchable` method on your model:
|
||
|
||
|
||
|
||
1/**
|
||
|
||
2 * Determine if the model should be searchable.
|
||
|
||
3 */
|
||
|
||
4public function shouldBeSearchable(): bool
|
||
|
||
5{
|
||
|
||
6 return $this->isPublished();
|
||
|
||
7}
|
||
|
||
|
||
/**
|
||
* Determine if the model should be searchable.
|
||
*/
|
||
public function shouldBeSearchable(): bool
|
||
{
|
||
return $this->isPublished();
|
||
}
|
||
|
||
The `shouldBeSearchable` method is only applied when manipulating models
|
||
through the `save` and `create` methods, queries, or relationships. Directly
|
||
making models or collections searchable using the `searchable` method will
|
||
override the result of the `shouldBeSearchable` method.
|
||
|
||
The `shouldBeSearchable` method is not applicable when using Scout's
|
||
"database" engine, as all searchable data is always stored in the database. To
|
||
achieve similar behavior when using the database engine, you should use where
|
||
clauses instead.
|
||
|
||
## Searching
|
||
|
||
You may begin searching a model using the `search` method. The search method
|
||
accepts a single string that will be used to search your models. You should
|
||
then chain the `get` method onto the search query to retrieve the Eloquent
|
||
models that match the given search query:
|
||
|
||
|
||
|
||
1use App\Models\Order;
|
||
|
||
2
|
||
|
||
3$orders = Order::search('Star Trek')->get();
|
||
|
||
|
||
use App\Models\Order;
|
||
|
||
$orders = Order::search('Star Trek')->get();
|
||
|
||
Since Scout searches return a collection of Eloquent models, you may even
|
||
return the results directly from a route or controller and they will
|
||
automatically be converted to JSON:
|
||
|
||
|
||
|
||
1use App\Models\Order;
|
||
|
||
2use Illuminate\Http\Request;
|
||
|
||
3
|
||
|
||
4Route::get('/search', function (Request $request) {
|
||
|
||
5 return Order::search($request->search)->get();
|
||
|
||
6});
|
||
|
||
|
||
use App\Models\Order;
|
||
use Illuminate\Http\Request;
|
||
|
||
Route::get('/search', function (Request $request) {
|
||
return Order::search($request->search)->get();
|
||
});
|
||
|
||
If you would like to get the raw search results before they are converted to
|
||
Eloquent models, you may use the `raw` method:
|
||
|
||
|
||
|
||
1$orders = Order::search('Star Trek')->raw();
|
||
|
||
|
||
$orders = Order::search('Star Trek')->raw();
|
||
|
||
#### Custom Indexes
|
||
|
||
Search queries will typically be performed on the index specified by the
|
||
model's searchableAs method. However, you may use the `within` method to
|
||
specify a custom index that should be searched instead:
|
||
|
||
|
||
|
||
1$orders = Order::search('Star Trek')
|
||
|
||
2 ->within('tv_shows_popularity_desc')
|
||
|
||
3 ->get();
|
||
|
||
|
||
$orders = Order::search('Star Trek')
|
||
->within('tv_shows_popularity_desc')
|
||
->get();
|
||
|
||
### Where Clauses
|
||
|
||
Scout allows you to add simple "where" clauses to your search queries.
|
||
Currently, these clauses only support basic numeric equality checks and are
|
||
primarily useful for scoping search queries by an owner ID:
|
||
|
||
|
||
|
||
1use App\Models\Order;
|
||
|
||
2
|
||
|
||
3$orders = Order::search('Star Trek')->where('user_id', 1)->get();
|
||
|
||
|
||
use App\Models\Order;
|
||
|
||
$orders = Order::search('Star Trek')->where('user_id', 1)->get();
|
||
|
||
In addition, the `whereIn` method may be used to verify that a given column's
|
||
value is contained within the given array:
|
||
|
||
|
||
|
||
1$orders = Order::search('Star Trek')->whereIn(
|
||
|
||
2 'status', ['open', 'paid']
|
||
|
||
3)->get();
|
||
|
||
|
||
$orders = Order::search('Star Trek')->whereIn(
|
||
'status', ['open', 'paid']
|
||
)->get();
|
||
|
||
The `whereNotIn` method verifies that the given column's value is not
|
||
contained in the given array:
|
||
|
||
|
||
|
||
1$orders = Order::search('Star Trek')->whereNotIn(
|
||
|
||
2 'status', ['closed']
|
||
|
||
3)->get();
|
||
|
||
|
||
$orders = Order::search('Star Trek')->whereNotIn(
|
||
'status', ['closed']
|
||
)->get();
|
||
|
||
Since a search index is not a relational database, more advanced "where"
|
||
clauses are not currently supported.
|
||
|
||
If your application is using Meilisearch, you must configure your
|
||
application's filterable attributes before utilizing Scout's "where" clauses.
|
||
|
||
### Pagination
|
||
|
||
In addition to retrieving a collection of models, you may paginate your search
|
||
results using the `paginate` method. This method will return an
|
||
`Illuminate\Pagination\LengthAwarePaginator` instance just as if you had
|
||
[paginated a traditional Eloquent query](/docs/12.x/pagination):
|
||
|
||
|
||
|
||
1use App\Models\Order;
|
||
|
||
2
|
||
|
||
3$orders = Order::search('Star Trek')->paginate();
|
||
|
||
|
||
use App\Models\Order;
|
||
|
||
$orders = Order::search('Star Trek')->paginate();
|
||
|
||
You may specify how many models to retrieve per page by passing the amount as
|
||
the first argument to the `paginate` method:
|
||
|
||
|
||
|
||
1$orders = Order::search('Star Trek')->paginate(15);
|
||
|
||
|
||
$orders = Order::search('Star Trek')->paginate(15);
|
||
|
||
Once you have retrieved the results, you may display the results and render
|
||
the page links using [Blade](/docs/12.x/blade) just as if you had paginated a
|
||
traditional Eloquent query:
|
||
|
||
|
||
|
||
1<div class="container">
|
||
|
||
2 @foreach ($orders as $order)
|
||
|
||
3 {{ $order->price }}
|
||
|
||
4 @endforeach
|
||
|
||
5</div>
|
||
|
||
6
|
||
|
||
7{{ $orders->links() }}
|
||
|
||
|
||
<div class="container">
|
||
@foreach ($orders as $order)
|
||
{{ $order->price }}
|
||
@endforeach
|
||
</div>
|
||
|
||
{{ $orders->links() }}
|
||
|
||
Of course, if you would like to retrieve the pagination results as JSON, you
|
||
may return the paginator instance directly from a route or controller:
|
||
|
||
|
||
|
||
1use App\Models\Order;
|
||
|
||
2use Illuminate\Http\Request;
|
||
|
||
3
|
||
|
||
4Route::get('/orders', function (Request $request) {
|
||
|
||
5 return Order::search($request->input('query'))->paginate(15);
|
||
|
||
6});
|
||
|
||
|
||
use App\Models\Order;
|
||
use Illuminate\Http\Request;
|
||
|
||
Route::get('/orders', function (Request $request) {
|
||
return Order::search($request->input('query'))->paginate(15);
|
||
});
|
||
|
||
Since search engines are not aware of your Eloquent model's global scope
|
||
definitions, you should not utilize global scopes in applications that utilize
|
||
Scout pagination. Or, you should recreate the global scope's constraints when
|
||
searching via Scout.
|
||
|
||
### Soft Deleting
|
||
|
||
If your indexed models are [soft deleting](/docs/12.x/eloquent#soft-deleting)
|
||
and you need to search your soft deleted models, set the `soft_delete` option
|
||
of the `config/scout.php` configuration file to `true`:
|
||
|
||
|
||
|
||
1'soft_delete' => true,
|
||
|
||
|
||
'soft_delete' => true,
|
||
|
||
When this configuration option is `true`, Scout will not remove soft deleted
|
||
models from the search index. Instead, it will set a hidden `__soft_deleted`
|
||
attribute on the indexed record. Then, you may use the `withTrashed` or
|
||
`onlyTrashed` methods to retrieve the soft deleted records when searching:
|
||
|
||
|
||
|
||
1use App\Models\Order;
|
||
|
||
2
|
||
|
||
3// Include trashed records when retrieving results...
|
||
|
||
4$orders = Order::search('Star Trek')->withTrashed()->get();
|
||
|
||
5
|
||
|
||
6// Only include trashed records when retrieving results...
|
||
|
||
7$orders = Order::search('Star Trek')->onlyTrashed()->get();
|
||
|
||
|
||
use App\Models\Order;
|
||
|
||
// Include trashed records when retrieving results...
|
||
$orders = Order::search('Star Trek')->withTrashed()->get();
|
||
|
||
// Only include trashed records when retrieving results...
|
||
$orders = Order::search('Star Trek')->onlyTrashed()->get();
|
||
|
||
When a soft deleted model is permanently deleted using `forceDelete`, Scout
|
||
will remove it from the search index automatically.
|
||
|
||
### Customizing Engine Searches
|
||
|
||
If you need to perform advanced customization of the search behavior of an
|
||
engine you may pass a closure as the second argument to the `search` method.
|
||
For example, you could use this callback to add geo-location data to your
|
||
search options before the search query is passed to Algolia:
|
||
|
||
|
||
|
||
1use Algolia\AlgoliaSearch\SearchIndex;
|
||
|
||
2use App\Models\Order;
|
||
|
||
3
|
||
|
||
4Order::search(
|
||
|
||
5 'Star Trek',
|
||
|
||
6 function (SearchIndex $algolia, string $query, array $options) {
|
||
|
||
7 $options['body']['query']['bool']['filter']['geo_distance'] = [
|
||
|
||
8 'distance' => '1000km',
|
||
|
||
9 'location' => ['lat' => 36, 'lon' => 111],
|
||
|
||
10 ];
|
||
|
||
11
|
||
|
||
12 return $algolia->search($query, $options);
|
||
|
||
13 }
|
||
|
||
14)->get();
|
||
|
||
|
||
use Algolia\AlgoliaSearch\SearchIndex;
|
||
use App\Models\Order;
|
||
|
||
Order::search(
|
||
'Star Trek',
|
||
function (SearchIndex $algolia, string $query, array $options) {
|
||
$options['body']['query']['bool']['filter']['geo_distance'] = [
|
||
'distance' => '1000km',
|
||
'location' => ['lat' => 36, 'lon' => 111],
|
||
];
|
||
|
||
return $algolia->search($query, $options);
|
||
}
|
||
)->get();
|
||
|
||
#### Customizing the Eloquent Results Query
|
||
|
||
After Scout retrieves a list of matching Eloquent models from your
|
||
application's search engine, Eloquent is used to retrieve all of the matching
|
||
models by their primary keys. You may customize this query by invoking the
|
||
`query` method. The `query` method accepts a closure that will receive the
|
||
Eloquent query builder instance as an argument:
|
||
|
||
|
||
|
||
1use App\Models\Order;
|
||
|
||
2use Illuminate\Database\Eloquent\Builder;
|
||
|
||
3
|
||
|
||
4$orders = Order::search('Star Trek')
|
||
|
||
5 ->query(fn (Builder $query) => $query->with('invoices'))
|
||
|
||
6 ->get();
|
||
|
||
|
||
use App\Models\Order;
|
||
use Illuminate\Database\Eloquent\Builder;
|
||
|
||
$orders = Order::search('Star Trek')
|
||
->query(fn (Builder $query) => $query->with('invoices'))
|
||
->get();
|
||
|
||
Since this callback is invoked after the relevant models have already been
|
||
retrieved from your application's search engine, the `query` method should not
|
||
be used for "filtering" results. Instead, you should use Scout where clauses.
|
||
|
||
## Custom Engines
|
||
|
||
#### Writing the Engine
|
||
|
||
If one of the built-in Scout search engines doesn't fit your needs, you may
|
||
write your own custom engine and register it with Scout. Your engine should
|
||
extend the `Laravel\Scout\Engines\Engine` abstract class. This abstract class
|
||
contains eight methods your custom engine must implement:
|
||
|
||
|
||
|
||
1use Laravel\Scout\Builder;
|
||
|
||
2
|
||
|
||
3abstract public function update($models);
|
||
|
||
4abstract public function delete($models);
|
||
|
||
5abstract public function search(Builder $builder);
|
||
|
||
6abstract public function paginate(Builder $builder, $perPage, $page);
|
||
|
||
7abstract public function mapIds($results);
|
||
|
||
8abstract public function map(Builder $builder, $results, $model);
|
||
|
||
9abstract public function getTotalCount($results);
|
||
|
||
10abstract public function flush($model);
|
||
|
||
|
||
use Laravel\Scout\Builder;
|
||
|
||
abstract public function update($models);
|
||
abstract public function delete($models);
|
||
abstract public function search(Builder $builder);
|
||
abstract public function paginate(Builder $builder, $perPage, $page);
|
||
abstract public function mapIds($results);
|
||
abstract public function map(Builder $builder, $results, $model);
|
||
abstract public function getTotalCount($results);
|
||
abstract public function flush($model);
|
||
|
||
You may find it helpful to review the implementations of these methods on the
|
||
`Laravel\Scout\Engines\AlgoliaEngine` class. This class will provide you with
|
||
a good starting point for learning how to implement each of these methods in
|
||
your own engine.
|
||
|
||
#### Registering the Engine
|
||
|
||
Once you have written your custom engine, you may register it with Scout using
|
||
the `extend` method of the Scout engine manager. Scout's engine manager may be
|
||
resolved from the Laravel service container. You should call the `extend`
|
||
method from the `boot` method of your `App\Providers\AppServiceProvider` class
|
||
or any other service provider used by your application:
|
||
|
||
|
||
|
||
1use App\ScoutExtensions\MySqlSearchEngine;
|
||
|
||
2use Laravel\Scout\EngineManager;
|
||
|
||
3
|
||
|
||
4/**
|
||
|
||
5 * Bootstrap any application services.
|
||
|
||
6 */
|
||
|
||
7public function boot(): void
|
||
|
||
8{
|
||
|
||
9 resolve(EngineManager::class)->extend('mysql', function () {
|
||
|
||
10 return new MySqlSearchEngine;
|
||
|
||
11 });
|
||
|
||
12}
|
||
|
||
|
||
use App\ScoutExtensions\MySqlSearchEngine;
|
||
use Laravel\Scout\EngineManager;
|
||
|
||
/**
|
||
* Bootstrap any application services.
|
||
*/
|
||
public function boot(): void
|
||
{
|
||
resolve(EngineManager::class)->extend('mysql', function () {
|
||
return new MySqlSearchEngine;
|
||
});
|
||
}
|
||
|
||
Once your engine has been registered, you may specify it as your default Scout
|
||
`driver` in your application's `config/scout.php` configuration file:
|
||
|
||
|
||
|
||
1'driver' => 'mysql',
|
||
|
||
|
||
'driver' => 'mysql',
|
||
|