ElasticsSearch7.6.1学习笔记【狂神说Java】


学习视频来源: 【狂神说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 DBElasticsearch
数据库(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命令说明:

methodurl地址描述
PUTlocalhost:9200/索引名称/类型名称/文档id创建文档(指定文档id)
POSTlocalhost:9200/索引名称/类型名称创建文档(指定文档id)
POSTlocalhost:9200/索引名称/类型名称/文档id/_update修改文档
DELETElocalhost:9200/索引名称/类型名称/文档id删除文档
GETlocalhost:9200/索引名称/类型名称/文档id查询文档通过文档id
POSTlocalhost: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解析
在这里插入图片描述
测试查看
在这里插入图片描述

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

XL's妃妃

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值