laravel框架下使用Elasticsearch服务

2021-03-26  本文已影响0人  安晓生

laravel下安装composer插件

根据自己的版本进行安装
composer require elasticsearch/elasticsearch

框架下进行配置,env是在你目录里面的.env文件下的。

'elasticsearch' => [
        // Elasticsearch 支持多台服务器负载均衡,因此这里是一个数组
        'hosts' => explode(',', env('ELASTICSEARCH_HOST')),
        'info'=>[
            'medicine_pro'=>env('MEDICINE_PRO_INFO'),//这两个是我配置的不同索引
            'medicine_store'=>env('MEDICINE_STORE_INFO'),
        ]

    ]

注意一个点:带账户密码的配置是这样的:在.env文件下的

ELASTICSEARCH_HOST=http://medicine:S7cxVXic@127.0.0.1:3307

配置完成之后我们来看一个类--自己写的

<?php


namespace App\Utils;
use Elasticsearch\ClientBuilder;


class Es
{
    private $client;

    const DISTANCE = "5000km";

    // 构造函数
    public function __construct()
    {
        $params =config('database.elasticsearch.hosts');
//            array(
//            '10.33.22.141:9200',
//        );
        $this->client = ClientBuilder::create()->setHosts($params)->build();
    }

    public function getClient() {
        return $this->client;
    }

    // 创建索引
    public function create_index($index_name = 'meitian', $properties = []) {
        $params  = [
            'index' => $index_name
        ];

        $exists = $this->client->indices()->exists($params);
        if(!$exists) {
            $params['body']['settings']['number_of_shards'] = 5;
            $params['body']['settings']['number_of_replicas'] = 0;

            if(!empty($properties)) {
                $params['body']['mappings'] = [
                    '_source' => [
                        'enabled' => true
                    ],
                    'properties' => $properties
                ];
            }
            echo 'create index: '.$index_name;

            $response = $this->client->indices()->create($params);
            return $response;
        }
    }

    // 删除索引
    public function delete_index($index_name = 'meitian') {
        $params  = [
            'index' => $index_name
        ];
        $response = $this->client->indices()->delete($params);
        return $response;
    }

    // 添加文档
    public function add_doc($id, $doc, $index_name = 'meitian') {
        $params = [
            'index' => $index_name,
            'id' => $id,
            'body' => $doc
        ];

        $response = $this->client->index($params);
        return $response;
    }

    // 批量添加文档
    public function bulk_doc($docs, $index_name = 'meitian') {
        $params = ['body' => []];
        $res = 0;

        foreach ($docs as  $i => $item) {
            if(!isset($item['id']) || empty($item['id'])) {
                continue;
            }

            $params['body'][] = [
                'index' => [
                    '_index' => $index_name,
                    '_id' => $item['id']
                ],
            ];
            unset($item['id']);
            $params['body'][] = $item;

            if ($i % 1000 == 0) {
                $responses = $this->client->bulk($params);
                $params = ['body' => []];
                unset($responses);
            }
            $res++;

        }

        if (!empty($params['body'])) {
            $responses = $this->client->bulk($params);
        }

        return $res;
    }

    //批量添加文档:带经纬度定制所属字段
    public function write($data , $index){

        foreach ($data as $i => $item) {
            $arr = $item->toESArray();

            $arr['location'] = [
                'lat' => $arr['lat'],
                'lon' => $arr['lng']
            ];
            unset($arr['lat']);
            unset($arr['lng']);
            $data[$i] = $arr;
        }

        $res = $this->bulk_doc($data, $index);
        return $res;
    }

    // 判断文档存在
    public function exists_doc($id, $index_name = 'meitian') {
        $params = [
            'index' => $index_name,
            'id' => $id
        ];

        $response = $this->client->exists($params);
        return $response;
    }


    // 获取文档
    public function get_doc($id, $index_name = 'meitian') {
        $params = [
            'index' => $index_name,
            'id' => $id
        ];

        $response = $this->client->get($params);
        return $response;
    }

    // 更新文档
    public function update_doc($id, $doc, $index_name = 'meitian') {
        $params = [
            'index' => $index_name,
            'id' => $id,
            'body' => [
                'doc' => $doc
            ]
        ];

        $response = $this->client->update($params);
        return $response;
    }
    // 删除文档
    public function delete_doc($id, $index_name = 'meitian') {
        $params = [
            'index' => $index_name,
            'id' => $id
        ];

        $response = $this->client->delete($params);
        return $response;
    }

    //查看映射
    public function get_mapping($index_name = "meitian") {
        $params = [
            'index' => $index_name,
        ];
        $response = $this->client->indices()->getMapping($params);
        return $response;
    }


    
}

接下来我们进行创建需要的索引,索引名字,跟映射字段。
这个是我数据表的字段,你可以根据你自己的需求来,
你也可以把2张以上的表字段都这样弄。
geo_point这个类型是经纬度。下面写入数据的时候回说。

//创建索引,映射字段,可以在控制写,也可以在服务层写完调用这个。
 public static function storeCrate (){
        $store = [
            'id' =>[
                'type'=>'integer'
            ],
            'title' =>[
                'type'=>'keyword'
            ],
            'mobile' =>[
                'type'=>'keyword'
            ],
          
            'location' => [
                'type' => 'geo_point'   //地理坐标
            ],
            'status'=>[
                'type' => 'integer',
            ],
            'created_ad'=>[
                'type' => 'keyword',
            ],
            'updated_at'=>[
                'type' => 'keyword',
            ],
        ];
        $es = new Es();
        $res = $es->create_index(config('database.elasticsearch.info.medicine_store'), $store);
        return $res;

    }

接下来我们看如何写入数据。--我们先来看批量写入---多表字段写入

//这个是多表的--调用的是上面的类。
//批量写入ES类里面这个方法:write 可以根据你自己业务写,如果不需要你可以删除,直接调用这个bulk_doc。这样你可以自己在model里面这样写。
//切记:$arr = $item->toESArray();   这个是必须的。在模型里面。看下面的代码。
public static function Write(){
        $es = new Es();
        //添加门店数据
        $pro = ArticleModel::query()
            // 预加载 门店库存 和 商品基本库,门店表
            ->with(['goods'=>function($query){
                $query->select('id','uniacid','pcate','ccate',
                    'tcate','status','title','thumb','goodssn','productsn','marketprice',
                    'createtime','updatetime','allcates',
                    'displayorder','common_name','approval',
                    'specifications','manufactor','type','subtitle','cates');
            }])->with(['store'=>function($query){
                $query->select('id','title','address','is_invoice','is_intra_city',
                    'is_express','is_store','lat','lng');
            }])->select('id','store_id','goods_id','total','originalprice','price','status')
            ->get();
        $res = $es->write($pro,config('elasticsearch.elasticsearch.info.medicine_pro'));
        return $res;
    }

model模型操作。这个是三张表的。单张的表的例子下面。

<?php
//这里我全部列出来。三张表操作的。
namespace App\Models;

use App\MyIndexConfigurator;
use Illuminate\Database\Eloquent\Model;

use Illuminate\Support\Arr;
use Laravel\Scout\Searchable;


class ArticleModel extends Model
{
    //你的表
    protected $table="mshop_store_product";

    use Searchable;

    public function goods()
    {
        return $this->hasOne(GoodsModel::class, 'id', 'goods_id');
    }

    public function store()
    {
        return $this->hasOne(StoreModel::class, 'id', 'store_id');
    }


    public function toESArray()
    {
        // 只取出需要的字段 门店商品库表
        $arr = Arr::only($this->toArray(), [
            'id',
            'goods_id',
            'store_id',
            'uniacid',
            'originalprice',
            'price',
            'total',
            'status'
        ]);

        //商品goods表的数据
        $arr['uniacid'] = $this->goods->uniacid;//连锁id
        $arr['pcate'] = $this->goods->pcate;//一级分类
        $arr['ccate'] = $this->goods->ccate;//二级分类
        $arr['tcate'] = $this->goods->tcate;//三级分类
        $arr['goods_status'] = $this->goods->status;//商品状态
        $arr['title'] = $this->goods->title;//商品标题
        $arr['redundancy_title'] = $this->goods->common_name.' '.$this->goods->specifications.' '.$this->goods->manufactor;//冗余的商品标题
        $arr['thumb'] = $this->goods->thumb;//商品图
        $arr['goodssn'] = $this->goods->goodssn;//商品编号
        $arr['productsn'] = $this->goods->productsn;//商品条码
        $arr['marketprice'] = $this->goods->marketprice;//商品现价
        $arr['createtime'] = $this->goods->createtime;//建立时间
        $arr['updatetime'] = $this->goods->updatetime;//创建时间
        $arr['allcates'] = ','.$this->goods->allcates.',';//商品分类集合
        $arr['displayorder'] = $this->goods->displayorder;//排序
        $arr['common_name'] = $this->goods->common_name;//通用名
        $arr['approval'] = $this->goods->approval;//批准文号
        $arr['specifications'] = $this->goods->specifications;//规格
        $arr['manufactor'] = $this->goods->manufactor;//厂家
        $arr['subtitle'] = $this->goods->subtitle;//子标题
        $arr['type'] = $this->goods->goods_type;//类型
        $arr['cates'] = ','.$this->goods->cates.',';//多重分类数据集
        //门店详情表+经纬度
        $arr['store_title'] = $this->store->title;
        $arr['address'] = $this->store->address;
        $arr['is_invoice'] = $this->store->is_invoice;
        $arr['is_intra_city'] = $this->store->is_intra_city;
        $arr['is_intra_city_self'] = $this->store->is_intra_city_self;//新增同城配送(店员自己送)
        $arr['is_express'] = $this->store->is_express;
        $arr['is_store'] = $this->store->is_store;
        $arr['lat'] = $this->store->lat;
        $arr['lng'] = $this->store->lng;
        $arr['store_status'] = $this->store->status;//门店状态
        $arr['num_total'] = $this->total;//多冗余字段
        return $arr;
    }
}

数量打的话,你可以写commands任务进行跑。
*接下来我们看单张表操作,这个很简单--我用commands给你演示

<?php

namespace App\Console\Commands;

use App\Models\StoreModel;
use App\Utils\Es;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redis;

class ESstore extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'es:storeCreate';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = '导入门店数据';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()

    {

        // 获取 Elasticsearch 对象
        $es = new Es();
        StoreModel::query()
            // 使用 chunkById 避免一次性加载过多数据
            ->chunkById(10000, function ($products) use ($es) {
                $this->info(sprintf('正在同步 ID 范围为 %s 至 %s 的商品', $products->first()->id, $products->last()->id));
                // 遍历商品
                foreach ($products as $i => $item) {
                    $arr = $item->toStore();
                    $arr['location'] = [
                        'lat' => $arr['lat'],
                        'lon' => $arr['lng']
                    ];
                    unset($arr['lat']);
                    unset($arr['lng']);
                    $data[$i] = $arr;
                }
                try {
                    // 使用 bulk 方法批量创建
                    $res = $es->bulk_doc($data, config('database.elasticsearch.info.medicine_store'));
                } catch (\Exception $e) {
                    $this->error($e->getMessage());
                    Log::info($e->getMessage());
                }
            },'id');
        $this->info('同步完成');
        Log::info('成功');
        //插入redis
        Redis::set('mshop:es:storeCreate',time());
    }
}

这下面这个是在你model里面的

$arr = $item->toStore();

就这样的,是不是很简单

//导入门店的数据
    public function toStore(){
        $arr = Arr::only($this->toArray(), [
            'id',
            'thrid_id',
            'uniacid',
            'title',
            'mobile',
            'saletime',
            'address',
            'lng',
            'lat',
            'status',
            'is_invoice',
            'is_intra_city',
            'is_intra_city_self',//新增同城配送(店员自己送)
            'is_express',
            'is_store',
            'created_at',
            'updated_at',
        ]);
        return $arr;
    }

都准备完成后,是不是要测试如何跑commands呢?执行下面命令
在你的项目目录下执行这个,会看到很多命令,然后你找一个:es:storeCreate 这个可以自己定义。

php artisan

然后找到执行这个:就可以跑了。

php artisan es:storeCreate  

es可以跑百万的,你可以根据自己需求来。建议都是单张表,这样速度会快。
下一篇文章会讲如何update更新ES数据。shuai_664可以找到我,如果你有好的建议可以找我一起探讨。我也在摸索中。很多地方不懂。

上一篇下一篇

猜你喜欢

热点阅读