Home » Laravel » How to Use Elasticsearch in Laravel

How to Use Elasticsearch in Laravel

ElasticSearch is one of the most popular Open-Source search engines. It supports not only searching but also very complicated data filtering and data aggregating. If you develop online stores or blogs, you need to integrate the search feature in Laravel. Of course, you can use the official ElasticSearch client package and do everything by yourself, but there is a more convenient way.

Laravel team developed a unified interface for data searching and indexing. It is called laravel/scout. It can support different search engines, including ElasticSearch by drivers. In this article we will show how to use Elasticsearch in Laravel.

How to Use Elasticsearch in Laravel

1. Prepare Model and Migration

Scout uses data from regular Eloquent models for indexing. I will use the Article model for examples from this article. The model will have title and content fields. Let’s create a migration:

php artisan make:migration CreateArticlesTable

Paste this code to the up() method:

Schema::create("articles", function (Blueprint $table) { $table->id(); $table->string("title"); $table->text("content"); $table->timestamps(); });

And create the model:

php artisan make:model Article

Next, let’s create a factory, that will make some articles for testing:

php artisan make:factory Article

And paste the following code into it:

class ArticleFactory extends Factory { public function definition() { return [ "title" => $this->faker->realText(100), "content" => $this->faker->realText(200), ]; } }

Then, create a command or seeder that will run the factory using this code:

Article::factory() ->count(10) ->create();

2. Install Scout Package

First of all, install Scout. Run the following command to do this:

composer require laravel/scout

After installing, publish the Scout configuration file and other package resources:

php artisan vendor:publish --provider="Laravel\Scout\ScoutServiceProvider"

3. Install ElasticSearch Driver

All Scout drivers for ElasticSearch are developed by third-party developers. They often have some additional features that can be used only in ElasticSearch:

  • synergy/scout-elasticsearch-driver – simple ElasticSearch driver;
  • matchish/laravel-scout-elasticsearch – a driver that can search by multiple indexes;
  • jeroen-g/explorer – allows to building a very complicated filtering queries using query builder.

All these drivers support pretty same functionality with a few additional opportunities. I prefer using jeroen-g/explorer because it is more convenient for filtering and aggregating. Run this command to install the package:

composer require jeroen-g/explorer

Then, publish the package configuration:

php artisan vendor:publish --tag=explorer.config

Open the config/scout.php file and change the value of the driver setting to “elastic“:

4. Prepare Model

The model that will be indexed should implement the Explored interface and use the Searchable trait. Most methods from the interface are implemented in the Searchable trait. You should define only the mappableAs() method. Here, define a description for the index fields. This data will be used when Scout creates an index. You can return an empty array, and ElasticSearch will create fields automatically, but if you want to use filtering or want ElasticSearch to work faster, declare the fields and their types manually.

Also you can set index settings in the model, for example, configure the number of shards, max result window or create custom filters and analyzers. To do that, the model should implement the IndexSettings interface and have the indexSettings() method. For this example, let’s create a model that will index two fields: title and content. In addition, let’s create a custom analyzer for them that will divide the text into words depending on whitespaces:

use JeroenG\Explorer\Application\Explored; use JeroenG\Explorer\Application\IndexSettings; use Laravel\Scout\Searchable; class Article extends Model implements Explored, IndexSettings { use Searchable; //..... public function indexSettings(): array { return [ "analysis" => [ "analyzer" => [ "custom_analyzer" => [ "type" => "custom", "tokenizer" => "whitespace", "filter" => ["lowercase"], ], ], ], ]; } public function mappableAs(): array { return [ "title" => [ "type" => "text", "analyzer" => "custom_analyzer", "search_analyzer" => "custom_analyzer", "fields" => [ "keyword" => [ "type" => "keyword", "ignore_above" => 256, ], ], ], "content" => [ "type" => "text", "analyzer" => "custom_analyzer", "search_analyzer" => "custom_analyzer", "fields" => [ "keyword" => [ "type" => "keyword", "ignore_above" => 256, ], ], ], ]; } }

This is a full Elasticsearch field declaration. Don’t be afraid of the nested keyword field in the property configuration, it’s default definition for text fields. The keyword field can be used for aggregations or filtering. Also, you can use simplified declaration from this driver. For example:

return [ "title" => "text", "content" => "text", ];

In this case, you can’t configure nested fields and analyzers, but the code is simpler. Here are some popular field types:

  • boolean
  • long
  • double
  • integer
  • keyword
  • text
  • date

By default, Scout imports all model fields into the index. But in most cases, it is not required. You can override the toSearchableArray() method and choose data that will appear in the index:

public function toSearchableArray() { return [ "title" => $this->title, "content" => $this->content, ]; }

5. Configure Driver and Connection

You can configure this Elasticsearch driver in the config/explorer.php file. First of all, configure the connection to the Elasticsearch in the connection section. Specify host, port and scheme here. Also, if your Elasticsearch requires authentication, you can add the auth section. For example:

return [ //... "connection" => [ "host" => "localhost", "port" => "9200", "scheme" => "http", "auth" => [ "username" => "elastic", "password" => "elastic", ], ], //... ];

Finally, you should tell the package about the Article model that can be indexed. Just add the model class into the indexes section:

'indexes' => [ \App\Models\Article::class ],

6. Create Index

This Scout supports these commands for index management:

  • scout:index – create an index.
  • scout:index-delete – delete an index.

The disadvantage of these commands is that you need to specify the index name. If you want Elasticsearch to use index settings and mappings that were configured in the model, create index manually. Run this command to create articles index:

php artisan scout:index articles

Now, you can open Kibana and ensure that your index was created and has required settings and properties:

7. Import Data into Index

Scout has a special command for indexing data. It is called scout:import. Let’s import data for the Article model:

php artisan scout:import App\\Models\\Article

The command will display the last id of the added article, and you can switch to Kibana and view how many records appeared in the index. There are ten records in this example:

The advantage of Scout is that all newly-created models will be automatically added to the index. Also, Scout listens to the Update event, and updates the index record when the model is updated. But you should modify model fields and call the save() method to get this result, as Scout can’t track changes in the database. In addition, Scout will delete the record from the index when you remove the model using the delete() method.

Also, you can write your own custom indexer. Just call the searchable() method for the Eloquent query builder. For example, use this code to index all records with non-empty content field from the articles table:

Article::query() ->whereNotNull("content") ->searchable();

8. Search Data

You can search data in the ElasticSearch index using the static search() method in the model class. For example, let’s find articles with the word “He”:

$results = Article::search("He")->get();

With Scout this is very convenient because you will get a standard Eloquent collection of models that satisfy the search request. By default, Elasticsearch will search in all fields that are available for indexing. If you want to use only a specific field, you can create a custom query and add it into the search query builder:

$query = Article::search(); $match = new Matching("title", "He"); $match->setOperator("and"); $query->must($match); $results = $query->get();

Note, that this is a driver-specific functionality, that will not work with other Scout drivers. In the same way, you can build more complicated queries. Use the must() method for queries that require to be matched and should() method where you want only one of the queries set to be matched. Use BoolQuery to group several queries. For example:

$query = Article::search(); $match = new Matching("title", "He"); $group = new BoolQuery(); $includeHer = new Matching("title", "her"); $includeThey = new Matching("title", "they"); $group->should($includeHer); $group->should($includeThey); $query->must($match); $query->must($group); $results = $query->get();

You can add other filters in the same way. Official documentation includes more information about the supported queries and their settings. You can found it here.

Wrapping Up

In this article, we have explained how to use ElasticSearch with Laravel. As you can see, with the Scout package it is very simple and convenient. You don’t need to write your own implementation for indexing models and keeping them updated. What search engine for Laravel do you prefer? Tell us about it using the comments section below.

Your Reaction

1 thought on “How to Use Elasticsearch in Laravel”

Leave a Comment