您现在的位置是:首页 > 技术杂谈 > Laravel > php > elasticsearch elasticsearch
1、数据准备
参考之前的项目,本文用订单商品的数据的做搜索
1)、订单表order_info,主要字段有:
id(主键)、order_no(订单号)、user_name(下单人)、consignee(收货人)、addresses(收货地址)、order_status(订单状态1~6)
created_at(下单时间)、store_id(店铺id)、order_amount(订单总额)
2)、订单商品表order_goods,主要字段有:
id(主键)、order_id(订单id)、goods_name(商品名称)、goods_spec(商品规格)、goods_price(商品价格)
3)、店铺表store,主要字段有:
id(主键)、store_name(店铺名称)
4)、测试数据:
5)、构造数据库模型,Order.php文件
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\DB; use Illuminate\Support\Arr; use App\Models\OrderGoods; /** * @property mixed pay_type 支付方式 1支付宝, 2微信,3余额 * @property mixed user */ class Order extends Model { protected $table = 'order_info'; public $timestamps = false; protected $fillable = ['order_no', 'store_id', 'user_id', 'user_name', 'consignee', 'phone', 'province', 'district', 'city', 'address', 'addresses', 'leave_msg', 'order_status', 'return_status', 'is_lock', 'goods_amount', 'order_amount', 'logistics_amount','discount_amount','commission_amount','reward_wheat', 'remark', 'pay_type', 'source', 'is_show', 'pay_at', 'delivery_at', 'finish_at', 'cancel_at', 'delete_at', 'created_at', 'updated_at']; public function goods() { return $this->hasMany('App\Models\OrderGoods', 'order_id', 'id'); } public function store(){ return $this->belongsTo('App\Models\Store','store_id','id'); } public function toESArray() { // 只取出需要的字段 $arr = Arr::only($this->toArray(), [ 'id', 'order_no', 'user_name', 'consignee', 'addresses', 'order_status', 'order_amount', 'created_at', ]); $arr['store_name'] = $this->store->store_name; // 只取出需要的 goods 字段 $arr['goods'] = $this->goods->map(function (OrderGoods $goods){ return Arr::only($goods->toArray(),['goods_name', 'goods_spec', 'goods_price']); }); //随机生成属性 $arr['properties'] = collect($this->getProperties())->map(function($properties,$key){ return array_merge($properties, [ 'search_value' => $properties['name'].':'.$properties['value'], ]); }); return $arr; } //构造商品属性规格数据 public function getProperties(){ $params = []; $params1 = [ ["name" => "品牌名称", "value" => "金士顿"], ["name" => "品牌名称", "value" => "海盗船"], ["name" => "品牌名称", "value" => "三星"], ["name" => "品牌名称", "value" => "影驰"], ["name" => "品牌名称", "value" => "威刚"], ]; $params2 = [ ["name" => "内存容量", "value" => "8GB"], ["name" => "内存容量", "value" => "4GB"], ["name" => "内存容量", "value" => "16GB"], ]; $params3 = [ ["name" => "传输类型", "value" => "DDR4"], ["name" => "传输类型", "value" => "DDR3"], ]; $params[] = $params1[rand(0,4)]; $randNum = rand(1,3); foreach($params2 as $k=> $val){ if($k<$randNum){ $params[] = $val; }else{ break; } } $params[] = $params3[rand(0,1)]; return $params; } }
OrderGoods.php文件
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; class OrderGoods extends Model { protected $table = 'order_goods'; protected $fillable = ['order_id','goods_id','sku_id','goods_name','goods_image', 'goods_num','original_price','goods_price','goods_spec','logistics_amount','receive_amount','goods_amount','is_after_sale','status']; }
Store.php文件
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; class Store extends Model { protected $table = 'store'; // 指定表 protected $fillable = ['id', 'phone_number', 'store_name', 'password', 'store_logo', 'store_domain_name', 'store_grade', 'store_duration', 'store_type', 'passed', 'refuse_reason', 'is_usable', 'remark', 'reason', 'invoice', 'payment_voucher', 'remember_token', 'submit_time', 'first_trial_time', 'upload_documents_time', 'pass_time', 'open_time']; public function orders(){ $this->hasMany('App\Models\Order','store_id','id'); } }
2、创建索引orders
通过创建一个 Artisan 命令来实现,创建Elasticsearch目录,执行命令
php artisan make:command Elasticsearch/CreateIndex
CreateIndex.php文件内容
<?php namespace App\Console\Commands\Elasticsearch; use Illuminate\Console\Command; class CreateIndex extends Command { /** * The name and signature of the console command. * * @var string */ protected $signature = 'es:create-index {index}'; /** * The console command description. * * @var string */ protected $description = '创建Elasticsearch索引'; protected $index; /** * Create a new command instance. * * @return void */ public function __construct() { parent::__construct(); } /** * Execute the console command. * * @return mixed */ public function handle() { //删除之前创建的索引 $this->index = $this->argument('index'); if ($this->existIndex()) { $this->deleteIndex(); $this->info('已删除索引:'.$this->index); } //创建索引 app('es')->indices()->create([ 'index' => $this->index, 'body' => [ 'settings' => [ 'analysis' => [ 'analyzer' => [ 'ik_smart_synonym' => [ 'type' => 'custom', 'tokenizer' => 'ik_max_word', 'filter' => ['synonym_filter'], ], ], 'filter' => [ 'synonym_filter' => [ 'type' => 'synonym', 'synonyms_path' => 'analysis/synonyms.txt', ], ], ], ], ] ]); $this->info('已创建索引:'.$this->index); } /** * @desc 检测索引 * @method existIndex * @return bool * @time 2020/8/7 17:05 */ public function existIndex(){ return app('es')->indices()->exists(['index' => $this->index]); } /** * @desc 删除索引 * @method deleteIndex * @return array * @time 2020/8/7 17:05 */ public function deleteIndex(){ return app('es')->indices()->delete(['index' => $this->index]); } }
创建索引时要先判断之前是否已创建,如果已创建就先删除后在重新创建,创建所以时settings设置了同义词配置,关于同义词的安装之前已经讲过,这里就不再叙述了。
执行命令创建orders索引
php artisan es:create_index orders
3、创建mappings
通过创建一个 Artisan 命令来实现,执行命令
php artisan make:command Elasticsearch/CreateMapping
CreateMapping.php文件内容
<?php namespace App\Console\Commands\Elasticsearch; use Illuminate\Console\Command; class CreateMapping extends Command { /** * The name and signature of the console command. * * @var string */ protected $signature = 'es:create-mapping {index}'; /** * The console command description. * * @var string */ protected $description = '创建Elasticsearch映射'; protected $index; /** * Create a new command instance. * * @return void */ public function __construct() { parent::__construct(); } /** * Execute the console command. * * @return mixed */ public function handle() { $this->index = $this->argument('index'); $params = $this->getParams(); app('es')->indices()->putMapping($params); $this->info('已创建映射:'.$this->index); } public function getParams(){ $params = [ 'index'=>$this->index, 'type'=>'_doc', 'custom'=>['include_type_name'=>true], 'body'=>[ '_doc'=>[ 'properties'=>[ ] ] ] ]; switch ($this->index){ case 'orders': $params['body']['_doc']['properties'] = [ 'id'=>['type'=>'integer'], 'order_no'=>['type'=>'keyword'], 'order_amount'=>['type'=>'scaled_float','scaling_factor'=>100], 'order_status'=>['type'=>'byte'], 'created_at'=>['type'=>'keyword'], 'user_name'=>['type'=>'text',"analyzer"=>"ik_max_word", "search_analyzer"=>"ik_smart_synonym"], 'consignee'=>['type'=>'text',"analyzer"=>"ik_max_word", "search_analyzer"=>"ik_smart_synonym"], 'store_name'=>['type'=>'text',"analyzer"=>"ik_max_word", "search_analyzer"=>"ik_smart_synonym"], 'addresses'=>['type'=>'text',"analyzer"=>"ik_max_word", "search_analyzer"=>"ik_smart_synonym"], 'goods'=>[ 'type'=>'nested', 'properties'=>[ 'goods_name'=>['type'=>'text',"analyzer"=>"ik_max_word", "search_analyzer"=>"ik_smart_synonym",'copy_to'=>'goodsName'], 'goods_spec'=>['type'=>'text',"analyzer"=>"ik_max_word", "search_analyzer"=>"ik_smart_synonym",'copy_to'=>'goodsSpec'], 'goods_price'=>['type'=>'scaled_float','scaling_factor'=>100] ] ], 'properties'=>[ 'type'=>'nested', 'properties'=>[ 'name'=>['type'=>'keyword','copy_to'=>'properties_name'], 'value'=>['type'=>'keyword','copy_to'=>'properties_value'], 'search_value'=>['type'=>'keyword'] ] ] ]; break; } return $params; } }
创建mapping要指定索引名称
Elasticsearch 的多字段匹配查询是不支持查询 Nested 对象的字段,如果不设置copy_to是查询不到数据的,所以这里加了copy_to参数,查询的时候可以通过copy_to参数值来查询数据
search_analyzer在搜索查询时,使用了自定义的ik_smart_synonym,可以同义词查询
执行命令
php artisan es:create_mapping orders
4、同步订单数据到Elasticsearch中
通过创建一个 Artisan 命令来实现,执行命令
php artisan make:command Elasticsearch/SyncOrders
SyncOrders.php文件内容
<?php namespace App\Console\Commands\Elasticsearch; use App\Models\Order; use Illuminate\Console\Command; class SyncOrders extends Command { protected $signature = 'es:sync-orders'; protected $description = '将订单数据同步到 Elasticsearch'; public function __construct() { parent::__construct(); } public function handle() { // 获取 Elasticsearch 对象 $es = app('es'); Order::with(['goods']) // 使用 chunkById 避免一次性加载过多数据 ->chunkById(100, function ($orders) use ($es) { $this->info(sprintf('正在同步 ID 范围为 %s 至 %s 的订单', $orders->first()->id, $orders->last()->id)); // 初始化请求体 $req = ['body' => []]; // 遍历订单 foreach ($orders as $order) { // 将订单模型转为 Elasticsearch 所用的数组 $data = $order->toESArray(); $req['body'][] = [ 'index' => [ '_index' => 'orders', '_id' => $data['id'], ], ]; $req['body'][] = $data; } try { // 使用 bulk 方法批量创建 $es->bulk($req); } catch (\Exception $e) { $this->error($e->getMessage()); } }); $this->info('同步完成'); } }
执行命令
php artisan es:sync-orders
文章评论

评论列表