美文网首页
laravel框架下使用Elasticsearch服务

laravel框架下使用Elasticsearch服务

作者: 安晓生 | 来源:发表于2021-03-26 10:38 被阅读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可以找到我,如果你有好的建议可以找我一起探讨。我也在摸索中。很多地方不懂。

    相关文章

      网友评论

          本文标题:laravel框架下使用Elasticsearch服务

          本文链接:https://www.haomeiwen.com/subject/gtfjhltx.html