PHP:Laravel 使用 elasticsearch 组件包
关于MySQL数据同步到ES:
有一些开源组件可以使用:go-mysql-elasticsearch,logstash,cancel 等...
然后就是同步双写,和异步双写(此文使用方式)。
关于Laravel的ES组件:(注意:安装组件请注意版本和ES相同)
composer require laravel/scout
Scout 是 Laravel 官方出的一个让 Eloquent 模型支持全文搜索的包,这个包封装好一批方法,通过这些方法就可以将数据索引到全文搜索引擎中、
以及使用关键字从搜索引擎搜索文档。这个包适用于一些简单的搜索场景,比如博客文章搜索,但无法发挥出全文搜索引擎全部威力。
composer require elasticsearch/elasticsearch (此文使用组件)
Elasticsearch 是Elasticsearch官方提供的composer包,它包含更丰富的操作查询语句,例如 should 语句、
模糊查询、分片查询等,根本不是 Scout 几个简单的方法能够覆盖的。
一:安装elasticsearch 组件及配置
composer require elasticsearch/elasticsearch "elasticsearch/elasticsearch":"7.6.1"
配置.env:
QUEUE_CONNECTION=redis
REDIS_HOST=192.168.244.110 REDIS_PASSWORD=123456 REDIS_PORT=6379
配置 config/database.php:
'elasticsearch' => [ // Elasticsearch 支持多台服务器负载均衡,因此这里是一个数组 'hosts' => explode(',', env('ES_HOSTS')), ],
初始化elasticsearch对象,并注入到 Laravel 容器中:
修改app/Providers/AppServiceProvider.php:
use Elasticsearch\ClientBuilder as ESClientBuilder; class AppServiceProvider extends ServiceProvider { /** * Register any application services. * * @return void */ public function register() { // // 注册一个名为 es 的单例 $this->app->singleton('es', function () { // 从配置文件读取 Elasticsearch 服务器列表 $builder = ESClientBuilder::create()->setHosts(config('database.elasticsearch.hosts')); // 如果是开发环境 if (app()->environment() === 'local') { // 配置日志,Elasticsearch 的请求和返回数据将打印到日志文件中,方便我们调试 $builder->setLogger(app('log')->driver()); } return $builder->build(); }); } /** * Bootstrap any application services. * * @return void */ public function boot() { // } }
命令行先测试一下:
二:使用
1,先建立一个索引,我在kibana里面创建的,也可以直接用 curl -XPUT http://192.168.244.100/ylt_drivers?pretty
# 创建一个索引 PUT /ylt_drivers # 设置索引规则 PUT /ylt_drivers/_mappings?pretty { "properties":{ "name":{ "type":"keyword" }, "mobile":{ "type":"keyword" }, "sex":{ "type":"integer" }, "driver_age":{ "type":"integer" }, "is_quit":{ "type":"boolean" }, "birthday":{ "type":"date", "format":"yyyy-MM-dd" }, "line":{ "type": "nested", "properties":{ "name":{ "type":"text", "analyzer":"ik_smart" }, "distance":{ "type":"scaled_float", "scaling_factor":100 } } } } }
2,创建一个laravel command命令同步MySQL现有数据
2.1 先在Model里面创建一个方法用来转换数据为es需要的格式
use Illuminate\Support\Arr; //创建一个导入es的数据格式 public function toESArray(){ $arr = Arr::only($this->toArray(),[ 'id','name','mobile','sex','age','driver_age','brithday' ]);
$arr['line'] = Arr::only($this->line->toArray(),['name','distance']); // 如果是列表可以用map() // $arr['lines'] = $this->line->map(function(Line $line){ // return Arr::only($line->toArray(),['name','distance']); // }); return $arr; }
2.2 先用命令行测试一下
2.3 创建一个artisan command命令同步数据
php artisan make:command Elasticsearch/SyncDrivers
打开 App\Console\Commands\Elasticsearch\SyncDrivers
namespace App\Console\Commands\Elasticsearch; use App\Models\Driver; use Illuminate\Console\Command; class SyncDrivers extends Command { /** * The name and signature of the console command. * * @var string */ protected $signature = 'es:sync-drivers'; /** * The console command description. * * @var string */ protected $description = '将drivers数据同步到es'; /** * Create a new command instance. * * @return void */ public function __construct() { parent::__construct(); } /** * Execute the console command. * * @return int */ public function handle() { // 获取 Elasticsearch 对象 $es = app('es'); Driver::query() // 预加载 line ->with(['line']) // 使用 chunkById 避免一次性加载过多数据 ->chunkById(100, function ($drivers) use ($es) { $this->info(sprintf('正在同步 ID 范围为 %s 至 %s 的商品', $drivers->first()->id, $drivers->last()->id)); // 初始化请求体 $req = ['body' => []]; // 遍历数据 foreach ($drivers as $driver) { // 将数据模型转为 Elasticsearch 所用的数组 $data = $driver->toESArray(); $req['body'][] = [ 'index' => [ '_index' => 'ylt_drivers', '_type' => '_doc', '_id' => $data['id'], ], ]; $req['body'][] = $data; } try { // 使用 bulk 方法批量创建 $es->bulk($req); } catch (\Exception $e) { $this->error($e->getMessage()); } }); $this->info('同步完成'); } }
先删除之前测试数据,再用命令执行测试。
3:将数据提交到redis队列,通过异步任务同步数据到es
3.1、先创建一个异步任务:
php artisan make:job SyncOneDriverToES
3.2、打开App\Jobs\SyncOneDriverToES
namespace App\Jobs; use App\Models\Driver; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldBeUnique; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; class SyncOneDriverToES implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; protected $driver; /** * Create a new job instance. * * @return void */ public function __construct(Driver $driver) { $this->driver = $driver; } /** * Execute the job. * * @return void */ public function handle() { $data = $this->driver->toESArray(); app('es')->index([ 'index' => 'ylt_drivers', 'type' => '_doc', 'id' => $data['id'], 'body' => $data, ]); } }
3.3、在数据编辑和新增的地方加入异步提交代码,这里使用的是laravel-admin,所以在form()方法里加入:
$form->saved(function (Form $form) { $driver = $form->model(); dispatch(new SyncOneDriverToES($driver)); });
3.4、启动队列
php artisan queue:work
然后可以通过es-head等工具查看信息否同步成功。