Recherche full-text avec Laravel Scout et ElasticSearch

5min read • 2021-10-11Dev Full-stack IconTechnology
ElasticSearch

Pour effectuer une recherche avancée comme une recherche full-text sur une base de données, les fonctionnalités qu’offre un système de gestion de base de données relationnelle restent limitées. C’est pourquoi, dans ce cas d’usage, il faut privilégier l’utilisation de moteurs de recherche adaptés tels que Lucene, Solr, ou bien Elasticsearch.

Ces moteurs de recherche vont indexer les données de notre base de données et faciliter ainsi la recherche full-text. Si vous utilisez Laravel, celui-ci offre cette fonctionnalité à travers Scout.

travers Scout

Dans cet article, nous verrons en détail comment implémenter une recherche full-text (appelée aussi recherche plein texte, ou recherche en texte intégral) à travers l’utilisation du moteur de recherche Elasticsearch intégré avec Laravel Scout.

Laravel Scout

Laravel Scout

Laravel Scout est une fonctionnalité sortie dans la version 5.3 de Laravel. Elle propose une solution de recherche full-text sur un Model.

Il s’agit essentiellement d’une couche d’abstraction par rapport au moteur d’indexation et de recherche de données utilisé (Elasticsearch, Solr ou autre). Laravel Scout permet de :

  • Indexer une table de la base de données d’un projet déjà existant dans un moteur de recherche.
  • Effectuer une migration vers un autre moteur de recherche (de “Elasticsearch” vers “Algolia” par exemple) avec un impact minimal au niveau du code.

Installation d’Elasticsearch

*Avant d’installer Elasticsearch, assurez-vous que Java est déjà installé dans votre machine.

1- Télécharger la dernière version d’Elasticsearch depuis le site officiel :

https://www.elastic.co/fr/downloads/elasticsearch

2- Dézipper le fichier, puis se positionner dans le répertoire ./bin.

3- Lancer à l’aide d’un terminal la commande : elasticsearch (ou elasticsearch.bat pour Windows)

Une fois la commande est terminée, Elasticsearch sera accessible depuis http://localhost:9200

Création d’un nouveau projet Laravel & Installation des packages

Prérequis :

  • PHP (de préférence >= 7.2)
  • Composer

1- D’abord, créer un nouveau projet Laravel :

composer create-project --prefer-dist laravel/laravel Xelops-technology-blog

2- Installer le package Laravel Scout via Composer :

composer require laravel/scout

3- Ajouter la classe ScoutServiceProvider dans la liste des providers de config/app.php :

Laravel\Scout\ScoutServiceProvider::class,

4- Enfin publier la configuration Scout :

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

Elasticsearch Driver

Actuellement, Scout utilise par défaut un Algolia driver, toutefois Laravel donne la possibilité d’écrire des drivers personnalisés (Pour Apache Solr par exemple). Nous avons donc la possibilité d’étendre Scout avec ses propres implémentations de recherche.

Nous allons dans notre cas utiliser un driver Elasticsearch déjà développé :

composer require tamayo/laravel-scout-elastic

Puis mettre à jour la liste des providers dans config/app.php :

ScoutEngines\Elasticsearch\ElasticsearchProvider::class,

Maintenant nous devons changer la valeur du driver dans le fichier de configuration scout.php :

'driver' => env('SCOUT_DRIVER','elasticsearch'),

Et puis ajouter dans le même fichier en bas les lignes suivantes :

'elasticsearch' => [
'index' => env('ELASTICSEARCH_INDEX','laravel'),
'config' => [
'hosts' => [
env('ELASTICSEARCH_HOST','localhost'),
],
],
],

Dans le fichier .env, nous ajoutons la configuration d’Elasticsearch comme suit :

ELASTICSEARCH_INDEX=scout
ELASTICSEARCH_HOST=http://localhost:9200

Maintenant que toutes les installations/configurations sont en place, il ne reste qu’à tester l’intégration d’Elasticsearch avec Laravel.

Exemple de recherche full-text

Ajout de la base de données :

Dans cet exemple, nous allons utiliser sqlite comme base de données.

Allons dans le fichier .env et faisons les modifications suivantes:

DB_CONNECTION=sqlite
#DB_HOST=127.0.0.1
#DB_PORT=3306
#DB_DATABASE=laravel
#DB_USERNAME=root
#DB_PASSWORD=

Pour le stockage des données nous allons créer un fichier :

touch database/database.sqlite

Configuration du Model :

Pour ce test, nous allons créer un Model “Post” via la commande:

php artisan make:model Post --all

A l’intérieur du Model Post nous devons ajouter Laravel Scout Searchable trait :

<?phpnamespace App;use Illuminate\Database\Eloquent\Model;
use Laravel\Scout\Searchable;class Post extends Model
{
 use Searchable;
 protected $fillable = [
 ‘subject’
 ];
}

Dans le fichier de migration, nous allons créer la table “posts” avec un champ “subject” :

<?phpuse Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;class CreatePostsTable extends Migration
{
 /**
 * Run the migrations.
 *
 * @return void
 */
 public function up()
 {
 Schema::create(‘posts’, function (Blueprint $table) {
 $table->bigIncrements(‘id’);
 $table->string(‘subject’);
 $table->timestamps();
 });
 } /**
 * Reverse the migrations.
 *
 * @return void
 */
 public function down()
 {
 Schema::dropIfExists(‘posts’);
 }
}

Ensuite, nous lançons la migration qui doit créer la table dans la base de données :

php artisan migrate

Indexation Scout :

Il est nécessaire d’exécuter la commande “scout:import” pour créer l’index sur Elasticsearch et éventuellement synchroniser les données entre la table (SGBDR) et l’index (Moteur de recherche):

php artisan scout:import “App\Post”

Tester la recherche :

Pour remplir la table des Posts, nous devons créer des fakes data. Pour ceci, nous utilisons le factory PostFactory précédemment généré par la commande de création du Model:

<?php/** @var \Illuminate\Database\Eloquent\Factory $factory */use App\Post;
use Faker\Generator as Faker;$factory->define(Post::class, function (Faker $faker) {
 return [
 “subject” => $faker->sentence(5),
 ];
});

Maintenant, nous allons ouvrir le console de laravel (Tinker) et créer 200 posts :

php artisan tinker
factory(App\Post::class, 200)->create()

Finalement, nous allons lancer une recherche sur le mot “et” en prenant les 2 premiers enregistrements du résultat:

App\Post::search(‘et’)->take(2)->get()

Et voilà, notre moteur de recherche fonctionne bien et le résultat est comme suit :

Illuminate\Database\Eloquent\Collection {#3252
 all: [
 App\Post {#3267
 id: “6”,
 subject: “Iure doloremque aut voluptatum et ullam eligendi sit.”,
 created_at: “2019–10–16 14:09:18”,
 updated_at: “2019–10–16 14:09:18”,
 },
 App\Post {#3268
 id: “8”,
 subject: “Aperiam est repellat laboriosam et ab.”,
 created_at: “2019–10–16 14:09:18”,
 updated_at: “2019–10–16 14:09:18”,
 },
 ],
 }

Nous pouvons aussi faire la recherche full-text avec un filtre de plus, comme l’exemple suivant où on filtre par id = 32.

App\Post::search(‘qui a’)->where(‘id’, 32)->get()

De la même façon, nous obtenons le résultat suivant :

Illuminate\Database\Eloquent\Collection {#3084
 all: [
 App\Post {#3086
 id: “32”,
 subject: “Veritatis qui a possimus et dolore ut.”,
 created_at: “2019–10–16 14:09:19”,
 updated_at: “2019–10–16 14:09:19”,
 },
 ],
 }
Khalid-BannouriWritten By Khalid BannouriTechnical Leader / Full-Stack DeveloperXelops Technology