共计 6683 个字符,预计需要花费 17 分钟才能阅读完成。
只需五步骤:
- 启动 集成 ik 中文分词插件的 Elasticsearch7.9 Docker 镜像
- Laravel7 配置 Scout
- 配置 Model 模型
- 导入数据
- 搜寻
演示地址
https://www.ar414.com
搜寻范畴
- 文章内容
- 题目
- 标签
后果权重
- 呈现关键词数量
- 呈现关键词次数
搜寻页面
- 高亮显示
- 分词显示
- 后果分页
前言
次要是博客刚好想做个搜寻, 顺便就整顿成文章
Laravel + Elasticsearch 很多前辈都写过教程和案例, 然而随着 Elasticsearch 和 laravel 的版本升级 以前的文章很多都不实用新版本的, 倡议大家应用任何开源我的项目时应该过一遍文档以以后应用的版本文档为主, 教程为辅
- Elasticsearch 7.9
- Laravel 7
- elasticsearch-analysis-ik v7.9
参考
- ik 中文分词插件
- elasticsearch 官网文档
应用集成 ik 中文分词
插件的 Elasticsearch
拉取 docker
$ docker pull ar414/elasticsearch-7.9-ik-plugin
创立日志和数据存储目录
本地映射到 docker 容器内,避免 docker 重启数据失落
$ mkdir -p /data/elasticsearch/data | |
$ mkdir -p /data/elasticsearch/log | |
$ chmod -R 777 /data/elasticsearch/data | |
$ chmod -R 777 /data/elasticsearch/log |
运行
docker run -d -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -v /data/elasticsearch/data:/var/lib/elasticsearch -v /data/elasticsearch/log:/var/log/elasticsearch ar414/elasticsearch-7.9-ik-plugin
验证
$ curl http://localhost:9200 | |
{ | |
"name" : "01ac21393985", | |
"cluster_name" : "docker-cluster", | |
"cluster_uuid" : "h8L336qcRb2i1aydOv04Og", | |
"version" : { | |
"number" : "7.9.0", | |
"build_flavor" : "default", | |
"build_type" : "docker", | |
"build_hash" : "a479a2a7fce0389512d6a9361301708b92dff667", | |
"build_date" : "2020-08-11T21:36:48.204330Z", | |
"build_snapshot" : false, | |
"lucene_version" : "8.6.0", | |
"minimum_wire_compatibility_version" : "6.8.0", | |
"minimum_index_compatibility_version" : "6.0.0-beta1" | |
}, | |
"tagline" : "You Know, for Search" | |
} |
测试中文分词
curl -X POST "http://localhost:9200/_analyze?pretty" -H 'Content-Type: application/json' -d'{"analyzer":"ik_max_word","text":"laravel 天下无敌 "}' | |
{ | |
"tokens" : [ | |
{ | |
"token" : "laravel", | |
"start_offset" : 0, | |
"end_offset" : 7, | |
"type" : "ENGLISH", | |
"position" : 0 | |
}, | |
{ | |
"token" : "天下无敌", | |
"start_offset" : 7, | |
"end_offset" : 11, | |
"type" : "CN_WORD", | |
"position" : 1 | |
}, | |
{ | |
"token" : "天下", | |
"start_offset" : 7, | |
"end_offset" : 9, | |
"type" : "CN_WORD", | |
"position" : 2 | |
}, | |
{ | |
"token" : "无敌", | |
"start_offset" : 9, | |
"end_offset" : 11, | |
"type" : "CN_WORD", | |
"position" : 3 | |
} | |
] | |
} | |
Laravel 我的项目中应用 Elasticsearch
Elasticsearch
官网有提供 SDK,在 Laravel 我的项目中能够更加 优雅
疾速的接入 Elasticsearch,Laravel 自身有提供 Scout 全文搜寻 的解决方案,咱们只需将默认的 Algolia 驱动 替换成ElasticSearch 驱动
。
装置
- laravel/scout
- matchish/laravel-scout-elasticsearch
$ composer require laravel/scout | |
$ composer require matchish/laravel-scout-elasticsearch |
配置
- 生成 Scout 配置文件(config/scout.php)
$ php artisan vendor:publish --provider="Laravel\Scout\ScoutServiceProvider" | |
Copied File [\vendor\laravel\scout\config\scout.php] To [\config\scout.php] | |
Publishing complete. |
- 指定 Scout 驱动
- 第一种:在
.env
文件中指定(倡议)
SCOUT_DRIVER=Matchish\ScoutElasticSearch\Engines\ElasticSearchEngine
- 第二种:在
config/scout.php
间接批改默认驱动
'driver' => env('SCOUT_DRIVER', 'algolia') | |
改为 | |
'driver' => env('SCOUT_DRIVER', 'Matchish\ScoutElasticSearch\Engines\ElasticSearchEngine') |
-
指定 Elasticsearch 服务 IP 端口
如果应用 docker 部署则应用
docker0
的 IP,Linux 通过 ifconfig 查看在
.env
中配置
ELASTICSEARCH_HOST=172.17.0.1:9200
- 注册服务
config/app.php
'providers' => [ | |
// Other Service Providers | |
\Matchish\ScoutElasticSearch\ElasticSearchServiceProvider::class | |
], |
- 革除配置缓存
$ php artisan config:clear
至此 laravel 曾经接入 Elasticsearch
理论业务中应用
需要
通过博客右上角的搜寻框能够搜寻到与关键词相干的文章,从以下几点匹配
- 文章内容
- 文章题目
- 文章标签
波及到 2 张 Mysql 表 以及字段
-
article
- title
- tags
-
article_content
- content
为文章配置 Elasticsearch 索引
- 创立索引配置文件(config/elasticsearch.php)
$ touch config/elasticsearch.php
- elasticsearch.php 配置字段映射
<?php | |
return [ | |
'indices' => [ | |
'mappings' => [ | |
'blog-articles' => [ | |
"properties"=> [ | |
"content"=> [ | |
"type"=> "text", | |
"analyzer"=> "ik_max_word", | |
"search_analyzer"=> "ik_smart" | |
], | |
"tags"=> [ | |
"type"=> "text", | |
"analyzer"=> "ik_max_word", | |
"search_analyzer"=> "ik_smart" | |
], | |
"title"=> [ | |
"type"=> "text", | |
"analyzer"=> "ik_max_word", | |
"search_analyzer"=> "ik_smart" | |
] | |
] | |
] | |
] | |
], | |
]; |
-
analyzer:字段文本的分词器
- search_analyzer:搜索词的分词器
-
依据具体业务场景抉择(颗粒小占用资源多, 个别场景 analyzer 应用 ik_max_word,search_analyzer 应用 ik_smart):
- ik_max_word:ik 中文分词插件提供, 对文本进行最大数量分词
laravel 天下无敌
->laravel
,天下无敌
,天下
,无敌
- ik_smart: ik 中文分词插件提供, 对文本进行最小数量分词
- ik_max_word:ik 中文分词插件提供, 对文本进行最大数量分词
`laravel 天下无敌 ` -> `laravel`,` 天下无敌 ` | |
配置文章模型
倡议先看一遍 Laravel Scout 应用文档
-
引入 Laravel Scout
namespace App\Models\Blog; use Laravel\Scout\Searchable; class Article extends BlogBaseModel {use Searchable;} -
指定索引(刚刚配置文件中的 elasticsearch.indices.mappings.blog-articles)
/** * 指定索引
*/ | |
public function searchableAs() | |
{return 'blog-articles';} | |
``` | |
-
设置导入索引的数据字段
/** * 设置导入索引的数据字段
*/ | |
public function toSearchableArray() | |
{ | |
return ['content' => ArticleContent::query() | |
->where('article_id',$this->id) | |
->value('content'), | |
'tags' => implode(',',$this->tags), | |
'title' => $this->title | |
]; | |
} | |
``` |
-
指定 搜寻索引中存储的惟一 ID
/** * 指定 搜寻索引中存储的惟一 ID
*/ | |
public function getScoutKey() | |
{return $this->id;} | |
/** | |
* 指定 搜寻索引中存储的惟一 ID 的键名 | |
* @return string | |
*/ | |
public function getScoutKeyName() | |
{return 'id';} | |
``` |
数据导入
其实是将数据表中的数据通过 Elasticsearch 导入到 Lucene
Elasticsearch 是 Lucene 的封装,提供了 REST API 的操作接口
- 一键主动导入:
php artisan scout:import
- 导入指定模型:
php artisan scout:import ${model}
$ php artisan scout:import "App\Models\Blog\Article" | |
Importing [App\Models\Blog\Article] | |
Switching to the new index | |
5/5 [⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬] 100% | |
[OK] All [App\Models\Blog\Article] records have been imported. |
导入失败, 常见起因:
-
Unresolvable dependency resolving [Parameter #0 [ <required> integer $retries]] in class ElasticsearchTransport
- 解决: 批改配置后, 没有革除配置缓存
-
invalid_index_name_exception
- 解决: searchableAs 配置谬误, 为索引创立别名后, 指定别名
查看索引是否正确
$ curl -XGET http://localhost:9200/blog-articles/_mapping?pretty | |
{ | |
"blog-articles_1598362919" : { | |
"mappings" : { | |
"properties" : { | |
"__class_name" : { | |
"type" : "text", | |
"fields" : { | |
"keyword" : { | |
"type" : "keyword", | |
"ignore_above" : 256 | |
} | |
} | |
}, | |
"content" : { | |
"type" : "text", | |
"analyzer" : "ik_max_word", | |
"search_analyzer" : "ik_smart" | |
}, | |
"tags" : { | |
"type" : "text", | |
"analyzer" : "ik_max_word", | |
"search_analyzer" : "ik_smart" | |
}, | |
"title" : { | |
"type" : "text", | |
"analyzer" : "ik_max_word", | |
"search_analyzer" : "ik_smart" | |
} | |
} | |
} | |
} | |
} |
测试
- 创立一个测试命令行
$ php artisan make:command ElasticTest
- 代码
<?php | |
namespace App\Console\Commands; | |
use App\Models\Blog\Article; | |
use App\Models\Blog\ArticleContent; | |
use Illuminate\Console\Command; | |
use Illuminate\Support\Carbon; | |
class ElasticTest extends Command | |
{ | |
/** | |
* The name and signature of the console command. | |
* | |
* @var string | |
*/ | |
protected $signature = 'elasticsearch {query}'; | |
/** | |
* The console command description. | |
* | |
* @var string | |
*/ | |
protected $description = 'elasticsearch test'; | |
/** | |
* Create a new command instance. | |
* | |
* @return void | |
*/ | |
public function __construct() | |
{parent::__construct(); | |
} | |
/** | |
* Execute the console command. | |
* | |
* @return mixed | |
*/ | |
public function handle() | |
{ | |
// | |
$startTime = Carbon::now()->getPreciseTimestamp(3); | |
$articles = Article::search($this->argument('query'))->get()->toArray(); | |
$userTime = Carbon::now()->getPreciseTimestamp(3) - $startTime; | |
echo "耗时(毫秒):{$userTime} \n"; | |
//content 在另外一张表中,不便察看测试 这里输入 | |
if(!empty($articles)) {foreach($articles as &$article) {$article = ArticleContent::query()->where('article_id',$article['id'])->value('content'); | |
} | |
} | |
var_dump($articles); | |
} | |
} |
- 测试
$ php artisan elasticsearch 周杰伦
- 高亮显示片段
高亮显示须要自定义查问,外围代码
//ONGR\ElasticsearchDSL\Highlight\Highlight | |
ArticleModel::search($query,function($client,$body) {$higlight = new Highlight(); | |
$higlight->addField('content',['type' => 'plain']); | |
$higlight->addField('title'); | |
$higlight->addField('tags'); | |
$body->addHighlight($higlight); | |
$body->setSource(['title','tags']); | |
return $client->search(['index' => (new ArticleModel())->searchableAs(), 'body' => $body->toArray()]); | |
})->raw(); |
自定义查问可参考以两个包灵便开发
- $client elasticsearch/elasticsearch package
- $body ongr/elasticsearch-dsl package