文章目录
- 一、ElasticSearch概述
- 二、安装elasticsearch-7.6.1,基于windows 10
- 1、解压安装包以及目录结构介绍
- 2、安装可视化插件elasticsearch-head
- 3、解决跨域问题
- 三、安装Kibana
- 四、核心概念
- 五、IK分词器插件
- 1、什么是ik分词器
- 2、解压
- 3、进入kibana测试
- 4、自定义扩展词汇
- 六、Rest风格说明
- 1、基本Rest命令说明:
- 2、添加索引
- 3、基本数据类型
- 4、其他命令
- 5、修改索引
- 6、查询
- 1、简单查询
- 2、复杂查询
- 1、条件查询
- 2、指定只显示的字段
- 3、排序分页查询
- 4、must、must_not、should
- 5、数据过滤
- 6、匹配多个条件
- 7、精确查询
- 8、高亮查询
- 七、集成spring boot
- 1、创建springboot项目
- 2、创建elasticsearch客户端构造器
- 3、测试索引库的API操作
- 1、测试创建索引库
- 2、测试判断索引库是否存在
- 3、测试删除索引库
- 4、测试文档API的操作
- 1、添加文档
- 2、获取文档内容
- 3、更新文档数据
- 4、删除文档数据
- 5、批量导入数据
- 6、批量查询
- 八、模拟京东商品搜索
- 1、新建springboot项目,导入静态资源
- 2、获取京东网页上的数据
- 3、将数据写入elasticSearch中
- 4、精准匹配查询es的数据,并分页显示
- 5、修改前端,使用vue、axios
- 6、对接后端的接口
- 7、高亮显示
学习视频来源: 【狂神说Java】ElasticSearch7.6.x最新完整教程通俗易懂
一、ElasticSearch概述
Elaticsearch ,简称为es,es是一个开源的高扩展的分布式全文检索引擎,它可以近乎实时的存储、检索数据,本身扩展性很好可以扩展到上百台服务器,处理PB级别( 大数据时代 )的数据。es也使用ava开发并使用Lucene作为其核心来实现所有索引和搜索的功能,但是它的目的是通过简单的RESTful API来隐藏Lucene的复杂性,从而让全文搜索变得简单。
据国际权威的数据库产品评测机构DB Engines的统计,在2016年1月,ElasticSearch已超过Sor等,成为排名第一的搜索引擎类应用。
历史:
多年前,一个叫做Shay Banon的刚结婚不久的=-失业开发者,由于妻子要去伦敦学习厨师,他便跟着也去了。在他找工作的过程中,为了给妻子构建一个食谱的搜索引擎,他开始构建一个早期版本的Lucene。
直接基于Lucene工作会比较困难,所以shay开始抽象Lucene代码以便/ava程序员可以在应用中添加搜索功能。他发布了他的第-个开源项目,叫做“Compass”。
后来Shav找到一份工作,这份工作处在高性能和内存数据网格的分布式环境中,因此高性能的、实时的、分布式的搜索引整也是理所当然需要的。然后他决定重写Compass库使其成为一个独立的服务叫做Elasticsearch。
第一个公开版本出现在2010年2月,在那之后Elasticsearch已经成为Github上最受欢迎的项目之一,代码贡献者超过300人。一5主营Elasticsearch的公司就此成立,他们一边提供商业支持一边开发新功能,不过Elastisearch将永远开源对所有人可用Shay的妻子依旧等待着她的食谱搜索…
谁在使用:
1、维基百科,类似百度百科,全文检索,高亮,搜索推荐/2 ( 权重,百度!)
2、The Guardian(国外新闻网站),类似搜狐新闻,用户行为日志(点击,浏览,收藏,评论)+社交网络数据( 对某某新闻的相关看法 ),数据分析,给到每篇新闻文章的作者,让他知道他的文章的公众反馈(好,坏,热门,垃圾,鄙视,崇拜)
3、Stack Overflow(国外的程序异常讨论论坛),T问题,程序的报错,提交上去,有人会跟你讨论和回答,全文检索,搜索相关问题和答案,程序报错了,就会将报错信息粘贴到里面去,搜索有没有对应的答案
4、GitHub(开源代码管理),搜索上千亿行代码
5、电商网站,检索商品
6、日志数据分析,logstash采集日志,ES进行复杂的数据分析,ELK技术,elasticsearch+logstash+kibana
7、商品价格监控网站,用户设定某商品的价格闽值,当低于该 值的时候,发送通知消息给用户,比如说订阅牙膏的监控,如果高露洁牙膏的家庭套装低于50块钱,就通知我,我就去买
8,BI系统,商业智能,Business lnteligene。比如说有个大型商场集团,BL,分析一下某某区过最近3年的用户消费金额的趋势以及用户群体的组成构成,产出相关的数张报表,**区,最近3年,每年消费金额呈现100%的增长,而且用户群体85%是高级白领,开一个新商场。ES执行数据分析和挖掘,Kibana进行数据可视化
9、国内:站内搜索(电商,招聘,门户,等等),IT系统搜索(OA,CRM,ERP,等等),数据分析(ES热门的一个使用场景)
二、安装elasticsearch-7.6.1,基于windows 10
安装包狂神说公众号里面回复elasticsearch即可获取
1、解压安装包以及目录结构介绍
解压:elasticsearch-7.6.1-windows-x86_64.zip
1、目录介绍:
2、熟悉目录!
bin 启动文件
config
log4j2.properties :日志配置文件
jvm.options:java虚拟机相关配置,资源占用配置
以下两个参数,可以根据自己的硬件资源配置ES的资源占用率,默认是要求1核1G
-Xms1g
-Xmx1g
elasticsearch.yml:elasticsearch的配置文件
#http.port: 9200:默认9200端口
lib 相关jar包
modules:功能模块
plugins:插件! 如一会用的ik分词器
logs:日志保存
3、启动
双击:conf/elasticsearch.bat启动即可
4、测试是否启动成功
http://127.0.0.1:9200/
2、安装可视化插件elasticsearch-head
1、解压:elasticsearch-head-master.zip
2、由于是前端vue项目,所以需要进入目录使用node.js安装依赖和启动
#进入cmd命令窗口
#用cnpm安装相关依赖
D:\Program Files\elasticsearch\elasticsearch-head-master>cnpm install
#启动elasticsearch-head
D:\Program Files\elasticsearch\elasticsearch-head-master>npm run start
3、测试
3、解决跨域问题
1、修改elasticsearch.yml配置文件
打开文件追加以下两行
#开启跨域
http.cors.enabled: true
#允许所有人访问
http.cors.allow-origin: "*"
保存重启elasticsearch
2、测试
elasticsearch-head连接elasticsearch成功
三、安装Kibana
Kibana是一个什对Elastisearch的开源分析及可视化平台,用来搜索、查看交与存储在Elasticsearch索引中的数据。使用Kibana可以通过各种图表进行高级数据分析及展示。Kbana让海量数据更容易理解。它操作简单,甚于浏览器的用户界面可以快速创建仪表板( dashboard)实时显示Elasticsearch查询动态。设置Kibana非常简单。无需编码或者额外的基础架构,几分钟内就可以完成Kibana安装并启动Elasticsearch索引临测。
1、解压kibana-7.6.1-windows-x86_64.zip
2、解压后的目录
3、启动
双击:kibana-7.6.1-windows-x86_64\bin\kibana.bat
4、访问测试:http://localhost:5601/
5、将kibana改为中文版的
打开:kibana-7.6.1-windows-x86_64\config\kibana.yml
在最后追加:i18n.locale: “zh-CN”
四、核心概念
概念
在前面的学习中,我们已经掌握了es是什么,同时也把es的服务已经安装启动,那么es是如何去存储数据,数据结构是什么,又是如何实现搜索的呢?我们先来聊聊ElasticSearch的相关概念吧!
集群,节点,索引,类型,文档,分片,映射是什么 ?
elasticsearch是面向文档,关系行数据库和 elasticsearch 客观的对比!一切都是]SON!
Relational DB | Elasticsearch |
---|---|
数据库(database) | 索引(indices) |
表(tables) | types |
行(rows) | documents |
字段(columns) | fields |
elasticsearch(集群)中可以包含多个索引(数据库),每个索引中可以包含多个类型(表),每个类型下又包含多 个文档(行),每个文档中又包含多个字段(列)。
物理设计 :
elasticsearch 在后台把每个索引划分成多个分片,每分分片可以在集群中的不同服务器间迁移
逻辑设计 :
个索引类型中,包含多个文档,比如说文档1,文档2。当我们索引一篇文档时,可以通过这样的一各顺序找到 它:索引>类型>文档ID,通过这个组合我们就能索引到某个具体的文档。注意ID不必是整数,实际上它是个字 符串。
倒排索引
elasticsearch使用的是一种称为倒排索引的结构,采用Lucene倒排索作为底层。这种结均活用于快速的全立拽索,一个索引由文档中所有不重复的列表构成,对于每一个词,都有一个包含它的文档列表。例如,现在有两个文档,每个文档包含如下内容:
study every day, good good up to forever# 文档1包含的内容
To forever,study every day,good good up # 文档2包含的内容
为了创建倒排索引,我们首先要将每个文档拆分成独立的词(或称为词条或者tokens),然后创建一个包含所有不重 复的词条的排序列表,然后列出每个词条出现在哪个文档:
现在,我们试图搜索 to forever ,只需要查看包含每个词条的文档
五、IK分词器插件
1、什么是ik分词器
分词:即把一段中文或者别的划分成一个个的关键字,我们在搜索时候会把自己的信息进行分词,会把数据库中或者索引库中的数据进行分词,然后进行一个匹配操作,默认的中文分词是将每个字看成一个词,比如“我爱狂神”会被分为“我”,爱”““狂”,神”,这显然是不符合要求的,所以我们需要安装中文分词器ik来解决这个问题。
K提供了两个分词算法: iksmart和 ikmax_word,其中 iksmart 为最少切分,ikmax word为最细粒度划分!一会我们测试!
2、解压
elasticsearch-analysis-ik-7.6.1.zip
解压到:elasticsearch-7.6.1\plugins\ik下,ik目录自己创建
重启elasticsearch,kibana
特别提示:elasticsearch的ik路径不要有空格 不然kibana会报错
3、进入kibana测试
1、查看分词器
最少切分
最细拆分
默认的分词器有个小问题,就是无法识别自创的词汇,或者不常见的词汇,就会导致这些词汇被一个一个拆成单个词,此时就需要手动添加一些自己需要的词汇
4、自定义扩展词汇
1、查看默认词汇
进入ik\config
随便点开一个***.dic文件
因此我们也可以自己创建一个“*.dic”的文件来存放我们自己的词汇
2、创建自己的词汇字典文件“kuang.dic”
3、将自己的字典文件配置到ik中
打开IKAnalyzer.cfg.xml文件
保存,重启ES,回kibana测试
六、Rest风格说明
一种软件架构风格,而不是标准,只是提供了一组设计原则和约束条件,它主要用于客户端的服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制
1、基本Rest命令说明:
method | url地址 | 描述 |
---|---|---|
PUT | localhost:9200/索引名称/类型名称/文档id | 创建文档(指定文档id) |
POST | localhost:9200/索引名称/类型名称 | 创建文档(指定文档id) |
POST | localhost:9200/索引名称/类型名称/文档id/_update | 修改文档 |
DELETE | localhost:9200/索引名称/类型名称/文档id | 删除文档 |
GETlocalhost:9200/索引名称/类型名称/文档id | 查询文档通过文档id | |
POST | localhost:9200/索引名称/类型名称/_search | 查询所有数据 |
2、添加索引
DSL语句的使用
#添加文档数据
PUT user/userinfo/1
{
"name":"lisi",
"age":22,
"city":"chengdu",
"description":"welcome chengdu"
}
3、基本数据类型
上面直接添加的索引数据,会自动创建索引类型,但也可以自己指定数据类型
#添加索引库
PUT /user2
{
"mappings":{
"properties": {
"name":{
"type": "text",
"analyzer": "ik_smart",
"search_analyzer": "ik_smart",
"store": false
},
"city":{
"type": "text",
"analyzer": "ik_smart",
"search_analyzer": "ik_smart",
"store": false
},
"age":{
"type": "long",
"store": false
},
"description":{
"type": "text",
"analyzer": "ik_smart",
"search_analyzer": "ik_smart",
"store": false
}
}
}
}
4、其他命令
通过get _cat/ 可以获得es的当前很少信息,包括健康检查
5、修改索引
1、方法一 put
2、方法二post
6、查询
1、简单查询
2、复杂查询
1、条件查询
2、指定只显示的字段
指定要显示的字段: “_source”: [“name”,“age”]
3、排序分页查询
#搜索排序
GET /user/userinfo/_search
{
"query":{
"match": {
"name":"李"
}
},
"sort":[
{
"age":{
"order":"desc"
}
}
],
}
#搜索查询并分页
get /user/userinfo/_search
{
"query":{
"match_all": {}
},
"sort":[
{
"age":{
"order":"desc"
}
}
],
"from":0, #从第几个数据开始
"size":2 #单页显示的数据条数
}
4、must、must_not、should
#多条件查询
#must :多个查询条件完全匹配,相当于and
#must_not:多个查询条件的相反匹配,相当于Not
#should:至少一个查询条件匹配,相当于or
GET /user/userinfo/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"city": "成都"
}
},
{
"match":{
"age":20
}
}
]
}
}
}
5、数据过滤
- gt 大于
- gte 大于等于
- lt 小于
- lte 小于等于
GET /user/userinfo/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"name": "李"
}
}
],
"filter": {
"range": {
"age": {
"gte": 20,
"lte": 30
}
}
}
}
}
}
6、匹配多个条件
单个词包含匹配
多个词包含匹配
#多个域匹配
#搜索description,city两个域中都包含“changdu”的数据
GET user/userinfo/_search
{
"query": {
"multi_match": {
"query": "成",
"fields": [
"city",
"description"
]
}
}
}
7、精确查询
term查询时直接通过倒排索引指定的词条进程精确查找的!
关于分词:
- term,直接查询精确的
- match 会使用分词器解析(先分析文档,然后在通过分析的文档进行查询!)
两个类型 text keyword
- text类型的索引,查询时能被分词模糊查询
- keyword类型的索引,只能使用倒排索引进行精确查询
8、高亮查询
GET /user/userinfo/_search
{
"query": {
"match": {
"name": "李"
}
},
"highlight": {
"fields": {
"name":{} #要高亮的字段
}
}
}
自定义高亮标签
如果不想用< em >标签,也可以自定义标签
GET /user/userinfo/_search
{
"query": {
"match": {
"name": "李"
}
},
"highlight": {
"pre_tags":"<p class='key' 'style'='color:red'>", #前缀
"post_tags": "</p>", #后缀
"fields": {
"name":{}
}
}
}
七、集成spring boot
1、创建springboot项目
由于是springboot默认生成的elasticsearch client版本与我们使用的elasticsearch版本不一致,所以需要自定义elasticsearch的maven版本
2、创建elasticsearch客户端构造器
官方文档
创建ElasticSearchClientConfig类,调用RestHighLevelClient方法,创建构造器并注入到springboot中
package com.jjl.config;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ElasticSearchClientConfig {
@Bean
public RestHighLevelClient restHighLevelClient(){
RestHighLevelClient client = new RestHighLevelClient(
RestClient.builder(
new HttpHost("127.0.0.1", 9200, "http")));
return client;
}
}
3、测试索引库的API操作
1、测试创建索引库
进入springboot的测试类
package com.jjl;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
import java.io.IOException;
/*
* es7.6.1 高级客户端测试*/
@SpringBootTest
class KuangshenEsApiApplicationTests {
@Autowired
//默认只用使用”restHighLevelClient“这个名字,但是如果觉得不想用这个名字,
// 则可以使用@Qualifier绑定,绑定之后就可以随意定义名字了
@Qualifier("restHighLevelClient")
private RestHighLevelClient client;
//测试索引创建 request
@Test
void testCreateIndex() throws IOException {
//1.创建索引请求
CreateIndexRequest request = new CreateIndexRequest("kuang_index");
//2.执行创建请求,类型使用Java执行刚才在kibana里面执行的请求
//CreateIndexResponse 请求后获得响应
CreateIndexResponse createIndexResponse = client.indices().create(request, RequestOptions.DEFAULT);
System.out.println(createIndexResponse);
}
}
允许查看输出
进入elasticsearch-head检查是否创建成功
2、测试判断索引库是否存在
//测试获取索引
@Test
void testExistIndex() throws IOException {
//获取"kuang_index"索引库的请求
GetIndexRequest request = new GetIndexRequest("kuang_index");
//判断索引库是否存在
boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
System.out.println(exists);
}
3、测试删除索引库
//删除索取库请求
@Test
void testDeleteIndex() throws IOException {
//获取"kuang_index"索引库的请求
DeleteIndexRequest request = new DeleteIndexRequest("kuang_index");
//获取删除之返回的结果
AcknowledgedResponse delete = client.indices().delete(request, RequestOptions.DEFAULT);
//isAcknowledged()如果删除成功则会返回true,否则false
System.out.println(delete.isAcknowledged());
}
4、测试文档API的操作
导入阿里的fastjson依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.20</version>
</dependency>
1、添加文档
//测试添加文档
@Test
void testAddDocument() throws IOException {
//创建对象
User user = new User("狂神说", 3);
//创建请求
IndexRequest request = new IndexRequest("kuang_index");
//规则
request.id("1");
//过期时间,1秒
request.timeout(TimeValue.timeValueSeconds(1));
//将数据放入请求
request.source(JSON.toJSONString(user), XContentType.JSON);
//客户端发送请求,获取响应结果
IndexResponse indexResponse = client.index(request, RequestOptions.DEFAULT);
System.out.println(indexResponse.toString());
System.out.println(indexResponse.status());
}
查看输出
查看创建结果
2、获取文档内容
// 获取文档
//判断是否存在
@Test
void testIsExists() throws IOException {
GetRequest getRequest = new GetRequest("kuang_index", "1");
// 不获取返回的上下文
getRequest.fetchSourceContext(new FetchSourceContext(false));
getRequest.storedFields("_none");
boolean exists = client.exists(getRequest, RequestOptions.DEFAULT);
System.out.println(exists);//返回true或者false
}
//获取文档信息
@Test
void testGetDocument() throws IOException {
GetRequest getRequest = new GetRequest("kuang_index", "1");
GetResponse getResponse = client.get(getRequest, RequestOptions.DEFAULT);
//打印文档内容
System.out.println(getResponse.getSourceAsString());
System.out.println(getResponse);//返回全部内容,和命令是一样的
}
3、更新文档数据
@Test
void testUpdateDocument() throws IOException {
UpdateRequest updateRequest = new UpdateRequest("kuang_index", "1");
updateRequest.timeout("1s");
User user = new User("狂神说Java", 25);
//JSON.toJSONString(user):调用阿里的JSON将user转换为json,
//XContentType.JSON:指定传输类型为json
updateRequest.doc(JSON.toJSONString(user), XContentType.JSON);
UpdateResponse update = client.update(updateRequest, RequestOptions.DEFAULT);
System.out.println(update.status());//返回OK
}
4、删除文档数据
//删除文档信息
@Test
void testDeleteDocument() throws IOException {
DeleteRequest request = new DeleteRequest("kuang_index", "25");
//超时时间,超时1秒就不执行了
request.timeout("1s");
DeleteResponse deleteResponse = client.delete(request, RequestOptions.DEFAULT);
System.out.println(deleteResponse.status());
}
5、批量导入数据
@Test
//批量导入数据
void testBulkRequest() throws IOException{
BulkRequest bulkRequest = new BulkRequest();
bulkRequest.timeout("10s");
ArrayList<Object> userList = new ArrayList<>();
userList.add(new User("jjl",5));
userList.add(new User("jjl1",25));
userList.add(new User("jjl2",15));
userList.add(new User("jjl3",35));
userList.add(new User("jfan1",12));
userList.add(new User("jfan3",10));
userList.add(new User("jfan5",11));
for (int i = 0; i < userList.size(); i++) {
//批量删除或者批量更新,就在这里调用相应方法即可
bulkRequest.add(
new IndexRequest("kuang_index")
.id(""+(i+1))//这里如果不指定生成id,则会默认生成无序不重复的id
.source(JSON.toJSONString(userList.get(i)),XContentType.JSON));
}
BulkResponse response = client.bulk(bulkRequest, RequestOptions.DEFAULT);
//.hasFailures()是否失败,返回”false“表示没有失败,则是成功
System.out.println(response.hasFailures());
}
6、批量查询
@Test
//批量查询
void testSearch() throws IOException {
SearchRequest searchRequest = new SearchRequest("kuang_index");
//构建条件
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
//查询条件,我们可以使用QueryBuilders工具来实现
//termQuery精确匹配查询条件
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("name", "jjl");
//matchAllQuery:查询所有数据
// MatchAllQueryBuilder allQuery = QueryBuilders.matchAllQuery();
sourceBuilder.query(termQueryBuilder);
//设置超时时间
sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
//分页
/* sourceBuilder.from();//从哪里开始分页
sourceBuilder.size();//单页显示条数*/
searchRequest.source(sourceBuilder);
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
System.out.println(JSON.toJSONString(searchResponse.getHits()));
//遍历输出
for (SearchHit documentFields : searchResponse.getHits().getHits()) {
System.out.println(documentFields.getSourceAsMap());
}
}
}
八、模拟京东商品搜索
1、新建springboot项目,导入静态资源
修改端口、关闭thymeleaf缓存
导入静态资源
解压:搜索页面.rar
创建controller类测试
启动测试
2、获取京东网页上的数据
获取京东网页上的数据(获取请求返回的页面信息,筛选出我们想要的数据就是可以)
jsoup包
1、导入依赖
<!-- 解析网页 仅用于解析网页-->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.10.2</version>
</dependency>
2、编写测试类
先查看要获取的商品信息,可以看出商品信息都在 id为“J_goodsList”所属的div里面
package com.jjl.util;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import java.net.URL;
import java.io.IOException;
public class HtmlParseUtil {
public static void main(String[] args) throws IOException {
//获取请求
// 前提,
String url = "https://search.jd.com/Search?keyword=java";
//解析网页 (Jsoup返回Document就是浏览器Document
Document document = Jsoup.parse(new URL(url), 30000);
// 所有js中可以用的方法,这里都可以用
// 获取J_goodsList下的数据
Element jGoodsList = document.getElementById("J_goodsList");
System.out.println(jGoodsList.html());
}
}
进一步筛选出商品的图片url、商品title、商品的价格
package com.jjl.util;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.net.URL;
import java.io.IOException;
import java.util.List;
public class HtmlParseUtil {
public static void main(String[] args) throws IOException {
//获取请求
// 前提
String url = "https://search.jd.com/Search?keyword=java";
//解析网页 (Jsoup返回Document就是浏览器Document
Document document = Jsoup.parse(new URL(url), 30000);
// 所有js中可以用的方法,这里都可以用
Element jGoodsList = document.getElementById("J_goodsList");
// 获取所有的li元素
Elements elements = jGoodsList.getElementsByTag("li");
// 获取元素中的内容,这里el 就是每一li标签
for (Element element : elements) {
String img = element.getElementsByTag("img").eq(0).attr("data-lazy-img");
String price = element.getElementsByClass("p-price").eq(0).text();
String title = element.getElementsByClass("p-name").eq(0).text();
System.out.println("===================");
System.out.println(img);
System.out.println(price);
System.out.println(title);
}
}
3、封装成工具类
先创建一个商品的pojo
package com.jjl.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Document(indexName = "skuinfo",indexStoreType = "docs")
public class Content {
@Field(type = FieldType.Text, analyzer = "ik_max_word",searchAnalyzer = "ik_max_word")
private String title;
private String img;
private String price;
}
提取工具类
@Component
public class HtmlParseUtil {
//提取为方法
//String keywords:要搜索的关键字, int age:要搜索的页数
public List<Content> parseJD(String keywords, int age) throws IOException {
age =age+2;
ArrayList<Content> goodsList = new ArrayList<>();
for (int i = 2; i <age ; i++) {
//获取请求
// 前提
String url = "https://search.jd.com/Search?keyword=" + keywords + "&page=" + i;
//解析网页 (Jsoup返回Document就是浏览器Document
Document document = Jsoup.parse(new URL(url), 30000);
// 所有js中可以用的方法,这里都可以用
Element jGoodsList = document.getElementById("J_goodsList");
// 获取所有的li元素
Elements elements = jGoodsList.getElementsByTag("li");
// 获取元素中的内容,这里el 就是每一li标签
for (Element element : elements) {
String img = element.getElementsByTag("img").eq(0).attr("data-lazy-img");
String price = element.getElementsByClass("p-price").eq(0).text();
String title = element.getElementsByClass("p-name").eq(0).text();
Content content = new Content();
content.setTitle(title);
content.setPrice(price);
content.setImg(img);
goodsList.add(content);
}
}
return goodsList;
}
}
测试工具类
3、将数据写入elasticSearch中
1、导入阿里的fastjson依赖
2、新建config文件夹创建elasticsearch配置类
3、编写service层
package com.jjl.service;
import com.alibaba.fastjson2.JSON;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.client.indices.CreateIndexResponse;
import com.jjl.pojo.Content;
import com.jjl.util.HtmlParseUtil;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.common.xcontent.XContentType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
//业务编写
public class ContentService {
@Autowired
private RestHighLevelClient restHighLevelClient;
@Autowired
private HtmlParseUtil htmlParseUtil;
//1.解析数据放入 es 索引中
public Boolean parseContent(String index,String keywords,int page)throws Exception{
CreateIndexRequest jdGoods = new CreateIndexRequest(index);
CreateIndexResponse createIndexResponse = restHighLevelClient.indices().create(jdGoods, RequestOptions.DEFAULT);
if (createIndexResponse.isAcknowledged()) {
List<Content> contents = htmlParseUtil.parseJD(keywords, page);
BulkRequest bulkRequest = new BulkRequest();
bulkRequest.timeout("2m");
for (int i = 0; i < contents.size(); i++) {
bulkRequest.add(new IndexRequest(index)
.source(JSON.toJSONString(contents.get(i)), XContentType.JSON)
);
}
BulkResponse bulk = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
return !bulk.hasFailures();
}else {
System.out.println("索引库"+index+"创建失败");
return true;
}
}
}
4、编写controller层
package com.jjl.controller;
import com.jjl.service.ContentService;
import org.elasticsearch.common.recycler.Recycler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
//前端的请求编写
@RestController
public class ContentController {
@Autowired
private ContentService contentService;
@GetMapping("/parse/{index}/{keywords}/{page}")
public Boolean parse(@PathVariable("index") String index,
@PathVariable("keywords") String keywords,
@PathVariable("page") int page) throws Exception {
return contentService.parseContent(index,keywords,page);
}
}
5、启动springboot,测试
测试:创建jd_good索引库,搜到关键字java的商品信息,搜索10页的数据
6、查看elasticsearch-head是否成功执行并获取数据
4、精准匹配查询es的数据,并分页显示
1、编写service层
// 2、获取这些数据实现搜索功能
public List<Map<String,Object>> searchPage(String keyword,int pageNo,int pageSize) throws IOException {
if (pageNo<=1){
pageNo=1;
}
//条件搜索
SearchRequest searchRequest = new SearchRequest("jd_goods");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.from(pageNo);
sourceBuilder.size(pageSize);
//精准匹配
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("title", keyword);
sourceBuilder.query(termQueryBuilder);
sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
//执行搜索
searchRequest.source(sourceBuilder);
SearchResponse search = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
ArrayList<Map<String,Object>> list = new ArrayList<>();
for (SearchHit documentFields : search.getHits().getHits()) {
list.add(documentFields.getSourceAsMap());
}
return list;
}
2、controller层调用
@GetMapping("/search/{keyword}/{pageNo}/{pageSize}")
public List<Map<String,Object>> search(@PathVariable("keyword") String keyword,
@PathVariable("pageNo") int pageNo,
@PathVariable("pageSize") int pageSize) throws IOException {
if (pageNo==0){
pageNo=1;
}
return contentService.searchPage(keyword, pageNo, pageSize);
}
3、重启测试
5、修改前端,使用vue、axios
1、下载vue和axios的package包
建议下载vue2版本的
3、将vue.min.js和axios.min.js文件复制到static/js/下
vue.min.js路径:node_modules\vue\dist
axios.min.js路径:node_modules\axios\dist\
复制到static/js/下
4、修改index.html页面
-
注释jquery
-
引入vue和axios
-
绑定标签,创建keyword和results存放数据
-
双向绑定获取keyword的文本框
-
绑定搜索时的鼠标单击事件
-
测试当单击搜索时,控制台能否获取到输入的keyword
6、对接后端的接口
1、测试使用axios向后端发起,将结果输出到控制台
2、将数据展示到页面上
3、展示
7、高亮显示
1、修改八、4精准匹配查询的类
// 2、获取这些数据实现搜索功能
public List<Map<String,Object>> searchPage(String keyword,int pageNo,int pageSize) throws IOException {
if (pageNo<=1){
pageNo=1;
}
//条件搜索
SearchRequest searchRequest = new SearchRequest("jd_goods");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.from(pageNo);
sourceBuilder.size(pageSize);
//精准匹配
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("title", keyword);
sourceBuilder.query(termQueryBuilder);
sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
//高亮
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.field("title");
//是否显示多个高亮,列如(同一个title中包含了多个要搜索的词),关闭之后值显示第一个
highlightBuilder.requireFieldMatch(false);
highlightBuilder.preTags("<span style='color:red'>");
highlightBuilder.postTags("</span>");
sourceBuilder.highlighter(highlightBuilder);
//执行搜索
searchRequest.source(sourceBuilder);
SearchResponse search = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
ArrayList<Map<String,Object>> list = new ArrayList<>();
for (SearchHit documentFields : search.getHits().getHits()) {
Map<String, HighlightField> highlightFields = documentFields.getHighlightFields();
//指定要高亮的字段
HighlightField title = highlightFields.get("title");
//原本的结果
Map<String, Object> sourceAsMap = documentFields.getSourceAsMap();
//解析高亮的字段
if (title!=null){
//如果title这个高亮的字段存在,这就赋给fragments
Text[] fragments = title.fragments();
String n_title= "";
//已经被处理过的高亮title遍历给n_title
for (Text text:fragments){
n_title += text;
}
//将原来的title替换为n_title
sourceAsMap.put("title",n_title);
}
list.add(sourceAsMap);
}
return list;
}
2、修改index.html
使用vue解析后端传过来的高亮html标签
使用v-html解析
测试查看