Elasticsearch Eloquent Integration for Laravel
Dendi Handian
Posted on April 5, 2020
I decided to do a simple demo of integrating elasticsearch into laravel. To get started quickly, I just found these packages:
By looking at each overview in their GitHub page, I will have quick analyzes. cviebrock/laravel-elasticsearch
seems to do the low-level integration that you can customize easily later, but it's not quite simple for this demo. Then sleimanx2/plastic
and elasticquent/Elasticquent
seems will do a simple integration without too much code, but which one will I use? I can't do both at once for the moment, so I will compare it based on their last maintenance updates (last commits) and the repository stars. And elasticquent/Elasticquent
is worth to try.
Prerequisites
Don't forget to prepare your own laravel app (the database should be configured as well) and set up the elasticsearch server. If you have no idea how to set up the elasticsearch server, I have solutions for you:
- Elasticsearch in Laradock (if you're using laradock environment)
- ELK Stack Local Development using Docker ELK (If you're not using laradock (WAMP, Laragon, etc), but we still use docker after all. You have to admit docker is popular these days...)
And you should be able to access it using Postman, Curl or etc.
Installing elasticquent/elasticquent
and configure
Go to your project directory using CLI and execute this composer
command:
composer require elasticquent/elasticquent
It seems by the time I used this package, the laravel auto-discovery doesn't work in this package and in the package's github page was cleary not stated that this package is discoverable or not. So, we need to add it the provider:
config/app.php
:
'providers' => [
...
Elasticquent\ElasticquentServiceProvider::class,
],
And the vendor:publish
command for this package should be work now:
php artisan vendor:publish --provider="Elasticquent\ElasticquentServiceProvider"
The config\elasticquent.php
file should be generated, but I would like to customize it into like this:
config\elasticquent.php
:
<?php
$elasticsearch_host = env('ELASTICSEARCH_HOST', 'localhost');
$elasticsearch_port = env('ELASTICSEARCH_PORT', '9200');
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' => [ "{$elasticsearch_host}:{$elasticsearch_port}" ],
'retries' => 1,
],
/*
|--------------------------------------------------------------------------
| Default Index Name
|--------------------------------------------------------------------------
|
| This is the index name that Elasticquent will use for all
| Elasticquent models.
*/
'default_index' => env('ELASTICQUENT_DEFAULT_INDEX', 'elasticquent'),
);
If you're using any elasticsearch service that accessible by your app at localhost:9200
, then it should be fine.
If you're using Laradock, then add these parameters to the app's .env
:
ELASTICSEARCH_HOST=elasticsearch
ELASTICSEARCH_PORT=9200
Preparing the code
We need to create a model, migration, and factory to find out whether this package is working or not. So first, let's create Book
model using php artisan make:model Book
and modify the generated file:
app\Book.php
:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Elasticquent\ElasticquentTrait;
class Book extends Model
{
use ElasticquentTrait;
/**
* The elasticsearch settings.
*
* @var array
*/
protected $indexSettings = [
'analysis' => [
'char_filter' => [
'replace' => [
'type' => 'mapping',
'mappings' => [
'&=> and '
],
],
],
'filter' => [
'word_delimiter' => [
'type' => 'word_delimiter',
'split_on_numerics' => false,
'split_on_case_change' => true,
'generate_word_parts' => true,
'generate_number_parts' => true,
'catenate_all' => true,
'preserve_original' => true,
'catenate_numbers' => true,
]
],
'analyzer' => [
'default' => [
'type' => 'custom',
'char_filter' => [
'html_strip',
'replace',
],
'tokenizer' => 'whitespace',
'filter' => [
'lowercase',
'word_delimiter',
],
],
],
],
];
function getIndexName()
{
return 'books';
}
function getTypeName()
{
return 'books';
}
}
and then let's create the migration using php artisan make:migration create_books_table
and modify it into like this:
database\migrations\xxxx_yy_zz_tttttt_create_books_table.php
:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateBooksTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('books', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('title');
$table->string('slug');
$table->unsignedInteger('year')->nullable();
$table->text('description')->nullable();
$table->unsignedBigInteger('author_id')->nullable();
$table->unsignedBigInteger('publisher_id')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('books');
}
}
And finally to create the book factory using php artisan make:factory BookFactory
and modify it into like this:
<?php
/** @var \Illuminate\Database\Eloquent\Factory $factory */
use App\Book;
use Faker\Generator as Faker;
use Illuminate\Support\Str;
$factory->define(Book::class, function (Faker $faker) {
$title = Str::title($faker->words($nb = 5, $asText = true));
$slug = Str::slug($title);
$description = $faker->paragraphs($nb = 3, $asText = true);
$year = rand(1900, 2020);
return [
'title' => $title,
'slug' => $slug,
'description' => $description,
'year' => $year,
'author_id' => rand(1, 1000),
'publisher_id' => rand(1, 1000),
];
});
Indexing and Mapping the data to elasticsearch and do the search demo
Now all the preparations should be completed, let's make some data using factory and laravel's tinker console. Use php artisan tinker
to enter the console and execute this code to create books data:
factory('App\Book', 5)->create();
Then add the created books data to the elasticsearch using this code:
\App\Book::addAllToIndex();
If there is no error that occurred during the above processes, then everything should stored to the elasticsearch. Let's use Postman to check it out:
Raw result here
I have tried the \App\Book::search()
and it doesn't get the result, but if I try using \App\Book::complexSearch()
it still work like this:
>>> \App\Book::complexSearch(array(
... 'body' => array(
... 'query' => array(
... 'match' => array(
... 'title' => 'Odio'
... )
... )
... )
... ));
=> Elasticquent\ElasticquentResultCollection {#3155
all: [
App\Book {#3154
id: 8,
title: "Odio Et Consectetur Nobis Est",
slug: "odio-et-consectetur-nobis-est",
year: 2001,
description: """
Alias et dolorem natus blanditiis et at ducimus. Rerum suscipit dolorem nesciunt voluptates quia quas omnis. Veniam est qui quis.\n
\n
Minima nemo deserunt omnis nemo assumenda qui omnis. Sed ullam reiciendis vitae rerum amet. Deserunt officiis reprehenderit beatae minus. Possimus incidunt quia et.\n
\n
Officiis voluptas natus culpa voluptates deserunt. Possimus et doloribus quis sint. Sed magni in dolorem in consequatur quibusdam. Et ipsum neque rerum sed. Quas saepe dolorem rerum rerum quis velit sit.
""",
author_id: 638,
publisher_id: 794,
created_at: "2020-04-05 00:25:11",
updated_at: "2020-04-05 00:25:11",
},
],
}
>>>
And here is the search result in Postman:
I think that's all to give you an idea about how laravel and elasticsearch works. Have fun exploring and experimenting it!
versions used:
- laravel: 6.0 LTS
- elasticquent/elasticquent: "^1.0" (dev-master)
- elasticsearch: 7.5.1
Posted on April 5, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
March 12, 2020