1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【Laravel10】Elasticserchを導入してみた

Posted at

はじめに

Elasticserchを導入しているプロジェクトがあって、どんな感じなのかイメージ掴むため、下記の参考記事にそって実装をすすめてみる

とりあえずLaravel Scout+Elasticsearchを動かしてみる

開発環境

Os:Mac
PHP:version 8.3.2
Laravel:version 10.45.1
Composer:version 2.2.4
Elasticsearch:5.6.16

導入

プロジェクトを作成

$ composer create-project --prefer-dist laravel/laravel Laravel-elasticserch

ライブラリインストール

$ composer require laravel/scout
$ composer require elasticsearch/elasticsearch

Scoutの設定ファイル生成して設定する

// config配下にscout.phpを生成
php artisan vendor:publish --provider="Laravel\Scout\ScoutServiceProvider"
config/scout.php
<?php

return [

// ~~~[略]~~~

    /*
    |--------------------------------------------------------------------------
    | Elasticsearch Configuration
    |--------------------------------------------------------------------------
    */
    'elasticsearch' => [
        'index' => env('ELASTICSEARCH_INDEX', 'scout'),
        'hosts' => [
            env('ELASTICSEARCH_HOST', 'http://localhost'),
        ],
    ],
];

プロバイダー作成

$ php artisan make:provider ElasticsearchServiceProvider
ElasticsearchServiceProvider.php
<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Laravel\Scout\EngineManager;
use App\Scout\ElasticsearchEngine;
use Elastic\Elasticsearch\ClientBuilder;

class ElasticsearchServiceProvider extends ServiceProvider
{
    /**
     * Register services.
     */
    public function register(): void
    {
        //
    }

    /**
     * Bootstrap services.
     */
    public function boot(): void
    {
        // ScoutのEngineManagerを拡張してElasticsearchをサポートす
        resolve(EngineManager::class)->extend('elasticsearch', function ($app) {
            // 適切なElasticsearchクライアントを使用してElasticsearchEngineの新しいインスタンスを作成
            return new ElasticsearchEngine(
                // Scoutの設定で指定されたインデックス名を使用
                config('scout.elasticsearch.index'),
                // Scoutの設定からElasticsearchのホスト情報を使用してElasticsearchクライアントを構築
                ClientBuilder::create()
                    ->setHosts(config('scout.elasticsearch.hosts'))
                    ->build()
            );
        });
    }
}

プロバイダー登録

config/app.php
'providers' => [

    // 中略

    /*
     * Package Service Providers...
     */
    App\Providers\ElasticsearchServiceProvider::class,
],

カスタムエンジンを作成

app\Scout\ElasticSearchEngine.php.php
<?php

namespace App\Scout;

use Elasticsearch\Client as Elastic;
use Laravel\Scout\Builder;
use Laravel\Scout\Engines\Engine;

class ElasticsearchEngine extends Engine
{
  protected $index;

  protected $elastic;

  public function __construct($index, $elastic)
  {
    $this->index = $index;
    $this->elastic = $elastic;
  }

  public function flush($model)
  {
    //
  }

  public function lazyMap(Builder $builder, $results, $model)
  {
    // Implement lazyMap method
  }

  public function createIndex($index, array $options = [])
  {
    // Implement createIndex method
  }

  public function deleteIndex($index)
  {
    // Implement deleteIndex method
  }

  public function update($models)
  {
    $params['body'] = [];
    $models->each(function ($model) use (&$params) {
      $params['body'][] = [
        'update' => [
          '_id' => $model->getKey(),
          '_index' => $this->index,
          '_type' => $model->searchableAs(),
        ]
      ];
      $params['body'][] = [
        'doc' => $model->toSearchableArray(),
        'doc_as_upsert' => true
      ];
    });
    $this->elastic->bulk($params);
  }

  public function delete($models)
  {
    $params['body'] = [];
    $models->each(function ($model) use (&$params) {
      $params['body'][] = [
        'delete' => [
          '_id' => $model->getKey(),
          '_index' => $this->index,
          '_type' => $model->searchableAs(),
        ]
      ];
    });
    $this->elastic->bulk($params);
  }

  public function search(Builder $builder)
  {
    // Implement search method
  }

  public function paginate(Builder $builder, $perPage, $page)
  {
    // Implement paginate method
  }

  public function mapIds($results)
  {
    // Implement mapIds method
  }

  public function map(Builder $builder, $results, $model)
  {
    // Implement map method
  }

  public function getTotalCount($results)
  {
    // Implement getTotalCount method
  }

  protected function performSearch(Builder $builder, $options = [])
  {
    // Implement performSearch method
  }

  public function filters(Builder $builder)
  {
    // Implement filters method
  }

  protected function sort(Builder $builder)
  {
    // Implement sort method
  }
}

DB作成

$ mysql -u root

MariaDB> CREATE DATABASE `findhome`
MariaDB> CHARACTER SET utf8mb4
MariaDB> COLLATE utf8mb4_unicode_ci;

$ php artisan make:migration create_shops_table
$ php artisan migrate

モデル作成

$ php artisan make:model Models/Shop
Models/Shop.php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Laravel\Scout\Searchable;
use Illuminate\Database\Eloquent\Factories\HasFactory;

class Shop extends Model
{
    use Searchable;
    use HasFactory;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name',
        'building_type',
        'phone_number',
        'address',
        'latitude',
        'longitude',
        'nearest_station',
        'open',
        'close',
        'wifi',
    ];
}


Elasticsearchの起動

ルート配下にdocker-compose.ymlを作成

docker-compose.yml
version: '2'
services:
  elasticsearch:
    image: elasticsearch:5.6
    volumes:
      - ./elasticsearch-data:/usr/share/elasticsearch/data
    ports:
      - "9200:9200"

DockerでElasticsearchコンテナを起動

// コンテナ起動
$ docker-compose up -d

// 起動しているか確認
$ docker ps
  • 起動できていることを確認

スクリーンショット 2024-02-26 21.44.07.png

Elasticsearchの動作確認

  • curlを使用してデータを追加する
$ curl -X PUT http://localhost:9200/test_index/httpd_access/1 -d '{"host": "localhost", "response": "200", "request": "/"}'
  • 追加したデータの取得
$ curl -X GET http://localhost:9200/test_index/_search -d '{"query": { "match_all": {} } }'
  • ステータス200で期待されている結果どおり取得できている
{
  "took": 120,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 1,
    "max_score": 1.0,
    "hits": [
      {
        "_index": "test_index",
        "_type": "httpd_access",
        "_id": "1",
        "_score": 1.0,
        "_source": {
          "host": "localhost",
          "response": "200",
          "request": "/"
        }
      }
    ]
  }
}

テスト用データの作成

Elasticsearchに登録するテスト用のデータを生成する

$ php artisan make:factory ShopFactory

database/factories/ShopFactory.phpを追加

database/factories/ShopFactory.php
<?php

namespace Database\Factories;

use App\Models\Shop;
use Illuminate\Database\Eloquent\Factories\Factory;

class ShopFactory extends Factory
{
    protected $model = Shop::class;

    public function definition()
    {
        return [
            'name' => $this->faker->streetName . $this->faker->randomElement(['マンション', 'ハウス', 'レーベン', 'レジデンス']),
            'phone_number' => $this->faker->phoneNumber,
            'address' => $this->faker->address,
            'nearest_station' => $this->faker->randomElement(['東京', '表参道', '錦糸町']),
            'open' => $this->faker->randomElement(['9:00', '10:00', '11:00', '12:00']),
            'close' => $this->faker->randomElement(['19:00', '20:00', '21:00', '22:00', '23:00']),
            'wifi' => $this->faker->randomElement(['あり', 'なし']),
        ];
    }
}


  • tinker使用せず、通常通りseederで作成してしまった。データが取得できているか確認
curl -X GET "localhost:9200/_cat/indices?v"
curl -X GET http://localhost:9200/scout/_search -d '{"query": { "match_all": {} } }'

  • データみにくいけど、取得できている

スクリーンショット 2024-03-04 21.48.42.png

  • 検索してみる
curl -X GET http://localhost:9200/scout/_search -d '{
  "query": {
    "match": {
      "nearest_station": "表参道"
    }
  }
}' | jq
  • 取得できている

スクリーンショット 2024-03-04 22.11.34.png

  • 下記のエラーになる場合は、jqコマンドを使用できるようにインストールする
zsh: command not found: jq
# Macの方
$ brew install jq

まとめ

なんとなくElasticsearch触る時は、こなんな感じか〜ってイメージ掴むために実装すすめてみましたが、よく理解できていない。

参考

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?