AppDividend
Latest Code Tutorials

Laravel Elasticsearch Tutorial: The Complete Guide

Elasticsearch is a search engine based on the Lucene library. It provides a distributed, multitenant-capable full-text search engine with an HTTP web interface and schema-free JSON documents.

Laravel Elasticsearch

To use Elasticsearch in Laravel, use the Elasticquent package. Elasticquent makes working with Elasticsearch and Eloquent models easier by mapping them to Elasticsearch types.

You can use the default settings or define how Elasticsearch should index and search your Eloquent models right in the model. The Elasticquent allows you to take an Eloquent model and quickly index and search its contents in Elasticsearch.

We begin this example by installing Elasticsearch on the mac and setting up the laravel development environment.

Step 1: Install Elasticsearch on Mac.

If you have not previously installed Elasticsearch on the mac, then you need this step. Otherwise, you can skip this step. Instead, type the following cmd in your terminal to install Elasticsearch via homebrew.

brew install elasticsearch

It will install it and now start the services using the following command.

brew services start elasticsearch

Step 2: Setup Laravel and Elasticsearch env.

Install Laravel 5.6 by the following command.

laravel new elasticlaravel

Go into the project folder.

cd elasticlaravel

Open the project in your editor.

code .

Configure the database inside the .env file.

Add the following line inside the composer.json file. We are installing the Elasticquent package.

"require": {
        "php": "^7.1.3",
        "fideloper/proxy": "^4.0",
        "laravel/framework": "5.6.*",
        "laravel/tinker": "^1.0",
        "elasticquent/elasticquent": "dev-master"
    },

Type the following command to install the elasticquent package.

composer update

Laravel Elasticsearch Tutorial Example

Once you’ve run a composer update command, you must register the Laravel service provider in your config/app.php file.

// config/app.php

'providers' => [
    ...
    Elasticquent\ElasticquentServiceProvider::class,
],

We also provide a facade for an elasticsearch-php client (connected using our settings); add the following to your config/app.php if necessary.

// config/app.php

'aliases' => [
    ...
    'Es' => Elasticquent\ElasticquentElasticsearchFacade::class,
],

Elasticsearch Configuration

Type the following Artisan command to publish the configuration file into your config directory for Laravel 5.6.

php artisan vendor:publish --provider="Elasticquent\ElasticquentServiceProvider"

Now go to the config file at app >> config >> elastiquent.php

We need to add the index name for our application. So let us change from default to articles.

<?php

// elastiquent.php

return array(

    /*
    |--------------------------------------------------------------------------
    | Custom Elasticsearch Client Configuration
    |--------------------------------------------------------------------------
    |
    | This array will be passed to the Elasticsearch client.
    | See configuration options here:
    |
    | http://www.elasticsearch.org/guide/en/elasticsearch/client/php-api/current/_configuration.html
    */

    'config' => [
        'hosts'     => ['localhost:9200'],
        'retries'   => 1,
    ],

    /*
    |--------------------------------------------------------------------------
    | Default Index Name
    |--------------------------------------------------------------------------
    |
    | This is the index name that Elasticquent will use for all
    | Elasticquent models.
    */

    'default_index' => 'articles',

);

Step 3: Create an Article model and migration.

We will search the articles using elasticsearch. For example, to create a model and migration using the following command.

php artisan make:model Article -m

In your <DATETIME>_create_articles_table.php file, add the schema code.

// create_articles_table.php

public function up()
{
   Schema::create('articles', function (Blueprint $table) {
       $table->increments('id');
       $table->string('title');
       $table->text('body');
       $table->string('tags');
       $table->timestamps();
   });
}

Migrate the table using the following command.

php artisan migrate

Step 4: Create the dummy data.

Create an ArticleTableSeeder using the following command.

php artisan make:seeder ArticleTableSeeder

For generating the fake data, we use the Faker library. However, before that, we need to add the protected $fillable fields inside the Article.php file to prevent the mass assignment exception.

// Article.php

class Article extends Model
{
    protected $fillable = ['title', 'body', 'tags'];
}

Now, add the following code inside the ArticleTableSeeder.php file.

<?php

// ArticleTableSeeder.php

use Illuminate\Database\Seeder;
use App\Article;

class ArticleTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        $faker = Faker\Factory::create();

        for($i=0; $i<50; $i++) {
          Article::create([
            'title' => $faker->sentence(3),
            'body' => $faker->paragraph(6),
            'tags' => join(',', $faker->words(4))
          ]);
        }
    }
}

Add the ArticleTableSeeder class inside the DatabaseSeeder.php file, which is located in the same directory.

<?php

// DatabaseSeeder.php

use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    /**
     * Seed the application's database.
     *
     * @return void
     */
    public function run()
    {
        $this->call(ArticleTableSeeder::class);
    }
}

Now run the seeder and create the fake data using the following command.

php artisan db:seed

Elasticsearch in Laravel

Step 5: Set up Elastiquent inside Article model.

Write the following code inside the Article.php file.

<?php

// Article.php

namespace App;
use Elasticquent\ElasticquentTrait;

use Illuminate\Database\Eloquent\Model;

class Article extends Model
{
    use ElasticquentTrait;

    protected $fillable = ['title', 'body', 'tags'];

    protected $mappingProperties = array(
        'title' => [
          'type' => 'text',
          "analyzer" => "standard",
        ],
        'body' => [
          'type' => 'text',
          "analyzer" => "standard",
        ],
        'tags' => [
          'type' => 'text',
          "analyzer" => "standard",
        ],
      );
}

Here, we add our mapping configuration for ElasticSearch. The database table has three main fields. So we need to assign the type and analyzer, which in our case is standard. Remember, our index name is articles. The mappingProperties array is the fields that need to be indexed with the proper type to search and get the perfect results.

Each mapping has a kind and an analyzer. Type’s can be various data types, including strings, numbers, and dates. We will stick to the text type but be aware that different types allow you to take advantage of different things. You can find more datatype here at the original elasticsearch docs.

Okay, now we need to index the database. So inside the web.php file, write the following code. Remember, I have written the following code inside the web.php file, but in a real-time scenario, you need to write this inside controller or any other logical part of your app except the web.php file.

Indexing Documents

To index all the entries in an Eloquent model, use. addAllToIndex:

    Article::addAllToIndex();

You can also index a collection of models:

    $articles = Article::where('id', '<', 200)->get();
    $articles->addToIndex();

You can index individual entries as well:

    $articles = Article::find($id);
    $articles->addToIndex();

You can also reindex an entire model:

     Article::reindex()

Now, we index the whole model to write the indexing code inside the root route.

<?php

// web.php

use App\Article;

Route::get('/', function () {
    Article::createIndex($shards = null, $replicas = null);

    Article::putMapping($ignoreConflicts = true);

    Article::addAllToIndex();

    return view('welcome');
});

Here, we have created the index. The name of the index is already defined inside the config >> elastiquent.php file.

Then we have put the mapping, defined in the Article.php model, and finally, add that to the indexing.

Go to the browser and hit this URL: http://elasticlaravel.test/

You will get the welcome page, but our data is entirely indexed, and we can verify that by sending the following request using cURL. We can also use the postman, but I am sending the request via terminal.

curl 'localhost:9200/articles/_mapping?pretty'

Elastic search api for laravel with example

We can do a basic search using the following command.

curl 'localhost:9200/articles/articles/_search?q=title:Sed&pretty'

Here, our search term for the title is Sed. So it will fetch the records that have Sed term. We can see the prettify results inside a terminal.

Laravel Elasticsearch query example

Step 6: Search using Laravel eloquent methods.

Okay, so till now, we have used cURL for searching the data. Now, we will use the Elastiquent method to search the data. So let us create another route inside the web.php file and add the following code.

<?php

// Article.php

use App\Article;

Route::get('/', function () {
    Article::createIndex($shards = null, $replicas = null);

    Article::putMapping($ignoreConflicts = true);

    Article::addAllToIndex();

    return view('welcome');
});

Route::get('/search', function() {

    $articles = Article::searchByQuery(['match' => ['title' => 'Sed']]);

    return $articles;
});

I have passed the ‘Sed’ hardcoded, but in a real-time application, it will be our search term typed inside the search box. Now, switch to this URL: http://elasticlaravel.test/search. You can see the JSON response like this.

Search using elasticsearch in Laravel

 

Of course, you might get different data because it is randomly generated. So first check the database, pick the term, and then pass it to the search engine, and it will return something like this. So we have successfully indexed the data and fetched the data from the elasticsearch engine.

In this example, we have searched for the title, and you can also go for body and tags.

$articles = Article::searchByQuery(['match' => ['body' => 'eligendi']]);
    
return $articles;

And you can do the same for the tags as well.

Search collections

We can get the total hits to count using the following code.

$articles = Article::searchByQuery(['match' => ['title' => 'Heleium']]);
    
 return $articles->totalHits();

Access the shards array:

 $articles->shards();

Access the max score:

 $articles->maxScore();

Access the timed out boolean property:

 $articles->timedOut();

Access the taken property:

  $articles->took();

Access search aggregations – See Aggregations for details:

$articles->getAggregations();

Chunking results from Elastiquent.

To get the results in chunks, you can use the chunk() function.

$articles = Article::searchByQuery(['match' => ['title' => 'Similique']]);
    
return $articles->chunk(2);

That’s it for this tutorial. Thanks for taking it.

Related posts

Laravel Typehead search

Laravel filters

19 Comments
  1. Amit K Sharma says

    Why default_index required and what is the use of it, as I am confused little.

  2. Inna Tarasyan says

    Thanks, Great job

  3. Gabriel Lacerda says

    For the beginners, this is the best tutorial in my opinion

  4. jeff says

    Thanks, @Krunal for such amazing tutorials. I’ve been following this website and I can undoubtedly say its been helpful to me.

    I’m trying to work on a website with multi-search form, where a user is prompted/can search multiple columns, I’ll really appreciate if you make a tutorial on that. Thank you.

    E.g. User can search say a real estate website, location + cost

  5. jeff ngugi says

    Thanks, @Krunal for such amazing tutorials. I’ve been following this website and I can undoubtedly say its been helpful to me.

    I’m trying to work on a website with multi-search form, where a user is prompted/can search multiple columns, I’ll really appreciate if you make a tutorial on that. Thank you.

    E.g. User can search say a real estate website, location + cost.

  6. Vivek says

    It’s really awesome. Need one more help How can we search with multiple fields. In this example we can match only one field i.e.
    $articles = Article::searchByQuery([‘match’ => [‘title’ => ‘Test’]]); but i want to match title as well as body and tag. How can i set route Or any other idea ? Please suggest.

  7. Mosam says

    It is very useful for beginner.

  8. vikram says

    HI ,i am getting below error.
    “message”: “No alive nodes found in your cluster”,
    “exception”: “Elasticsearch\Common\Exceptions\NoNodesAvailableException

    1. Issac says

      Hello Vikram, could you resolve this problem?

  9. Othmane says

    Thx very good tutorial, but the query
    Article::search(‘Quis’);
    Is giving me nothing :s

  10. srikanth kudikala says

    Can we do search on relationships

  11. Vahid says

    Hi, I can’t use this tutorial for two or three models, how use for several models?!
    I want to run: Video::addAllToIndex();, Article::addAllToIndex();, Course::addAllToIndex(); and ..

  12. Max Kale says

    {“error”:{“root_cause”:[{“type”:”mapper_parsing_exception”,”reason”:”Root mapping definition has unsupported parameters: [articles : {_source={enabled=true}, properties={title={analyzer=standard, type=text}, body={analyzer=standard, type=text}, tags={analyzer=standard, type=text}}}]”}],”type”:”mapper_parsing_exception”,”reason”:”Failed to parse mapping [_doc]: Root mapping definition has unsupported parameters: [articles : {_source={enabled=true}, properties={title={analyzer=standard, type=text}, body={analyzer=standard, type=text}, tags={analyzer=standard, type=text}}}]”,”caused_by”:{“type”:”mapper_parsing_exception”,”reason”:”Root mapping definition has unsupported parameters: [articles : {_source={enabled=true}, properties={title={analyzer=standard, type=text}, body={analyzer=standard, type=text}, tags={analyzer=standard, type=text}}}]”}},”status”:400}

  13. Max Kale says

    Hi i have getting these error

    {“error”:{“root_cause”:[{“type”:”mapper_parsing_exception”,”reason”:”Root mapping definition has unsupported parameters: [articles : {_source={enabled=true}, properties={title={analyzer=standard, type=text}, body={analyzer=standard, type=text}, tags={analyzer=standard, type=text}}}]”}],”type”:”mapper_parsing_exception”,”reason”:”Failed to parse mapping [_doc]: Root mapping definition has unsupported parameters: [articles : {_source={enabled=true}, properties={title={analyzer=standard, type=text}, body={analyzer=standard, type=text}, tags={analyzer=standard, type=text}}}]”,”caused_by”:{“type”:”mapper_parsing_exception”,”reason”:”Root mapping definition has unsupported parameters: [articles : {_source={enabled=true}, properties={title={analyzer=standard, type=text}, body={analyzer=standard, type=text}, tags={analyzer=standard, type=text}}}]”}},”status”:400}

  14. Mithilesh Kumar Jha says

    I am getting this error
    Using Laravel version : 5.8.3
    Elastic – 7
    {“error”:{“root_cause”:[{“type”:”mapper_parsing_exception”,”reason”:”Root mapping definition has unsupported parameters: [articles : {_source={enabled=true}, properties={title={analyzer=standard, type=text}, body={analyzer=standard, type=text}, tags={analyzer=standard, type=text}}}]”}],”type”:”mapper_parsing_exception”,”reason”:”Failed to parse mapping [_doc]: Root mapping definition has unsupported parameters: [articles : {_source={enabled=true}, properties={title={analyzer=standard, type=text}, body={analyzer=standard, type=text}, tags={analyzer=standard, type=text}}}]”,”caused_by”:{“type”:”mapper_parsing_exception”,”reason”:”Root mapping definition has unsupported parameters: [articles : {_source={enabled=true}, properties={title={analyzer=standard, type=text}, body={analyzer=standard, type=text}, tags={analyzer=standard, type=text}}}]”}},”status”:400}

  15. Mithilesh Kumar Jha says

    Getting same error what Max Kales says

    1. dorskie says

      same error using laravel version 5.7 and elasticsearch version 7.5.1

  16. egot says

    Everything is very open with a precise description of the challenges.

    It was truly informative. Your site is very useful.

    Many thanks for sharing!

  17. cloude says

    I get this error:
    Elasticsearch\Common\Exceptions\NoNodesAvailableException
    No alive nodes found in your cluster

    how can I fix it? thanks!

Leave A Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.