ElasticSearch 入门文档 2021-09-26 至 2021-10-06

分布式框架中间件总纲

https://www.jianshu.com/p/00aa796bb5b8

友情链接

ElasticSearch 安装(docker)

目录

一、ElasticSearch 概述
二、ES 核心概念
三、IK 分词器插件
四、Rest 风格说明
五、关于索引的基本操作
六、关于文档的基本操作
七、集成 SpringBoot
八、案例
      1、爬虫
      2、前后端分离
      3、搜索高亮

一、ElasticSearch 概述

1、什么是 ElasticSearch

The Elastic Stack, 包括 Elasticsearch、Kibana、Beats 和 Logstash(也称为 ELK Stack)。能够安全可靠地获取任何来源、任何格式的数据,然后实时地对数据进行搜索、分析和可视化。Elaticsearch,简称为ES, ES是一个开源的高扩展的分布式全文搜索引擎,是整个Elastic Stack技术栈的核心。它可以近乎实时的存储、检索数据;本身扩展性很好,可以扩展到上百台服务器,处理PB级别的数据。

2、全文搜索引擎

1、Google,百度类的网站搜索,它们都是根据网页中的关键字生成索引,我们在搜索的时候输入关键字,它们会将该关键字即索引匹配到的所有网页返回;还有常见的项目中应用日志的搜索等等。对于这些非结构化的数据文本,关系型数据库搜索不是能很好的支持。
2、一般传统数据库,全文检索都实现的很鸡肋,因为一般也没人用数据库存文本字段。进行全文检索需要扫描整个表,如果数据量大的话即使对SQL的语法优化,也收效甚微。建立了索引,但是维护起来也很麻烦,对于 insert 和 update 操作都会重新构建索引。
3、基于以上原因可以分析得出,在一些生产环境中,使用常规的搜索方式,性能是非常差的:
搜索的数据对象是大量的非结构化的文本数据。
文件记录量达到数十万或数百万个甚至更多。
支持大量基于交互式文本的查询。
需求非常灵活的全文搜索查询。
对高度相关的搜索结果的有特殊需求,但是没有可用的关系数据库可以满足。
对不同记录类型、非文本数据操作或安全事务处理的需求相对较少的情况。
4、为了解决结构化数据搜索和非结构化数据搜索性能问题,我们就需要专业,健壮,强大的全文搜索引擎
这里说到的全文搜索引擎指的是目前广泛应用的主流搜索引擎。它的工作原理是计算机索引程序通过扫描文章中的每一个词,对每一个词建立一个索引,指明该词在文章中出现的次数和位置,当用户查询时,检索程序就根据事先建立的索引进行查找,并将查找的结果反馈给用户的检索方式。这个过程类似于通过字典中的检索字表查字的过程。

3、Elasticsearch And Solr 的区别

Lucene是Apache软件基金会Jakarta项目组的一个子项目,提供了一个简单却强大的应用程式接口,能够做全文索引和搜寻。在Java开发环境里Lucene是一个成熟的免费开源工具。就其本身而言,Lucene是当前以及最近几年最受欢迎的免费Java信息检索程序库。但Lucene只是一个提供全文搜索功能类库的核心工具包,而真正使用它还需要一个完善的服务框架搭建起来进行应用。
目前市面上流行的搜索引擎软件,主流的就两款:Elasticsearch和Solr,这两款都是基于Lucene搭建的,可以独立部署启动的搜索引擎服务软件。由于内核相同,所以两者除了服务器安装、部署、管理、集群以外,对于数据的操作 修改、添加、保存、查询等等都十分类似。
在使用过程中,一般都会将Elasticsearch和Solr这两个软件对比,然后进行选型。这两个搜索引擎都是流行的,先进的的开源搜索引擎。它们都是围绕核心底层搜索库 - Lucene构建的 - 但它们又是不同的。像所有东西一样,每个都有其优点和缺点:


image.png

5、Elasticsearch Or Solr 的选型

Google搜索趋势结果表明,与 Solr 相比,Elasticsearch具有很大的吸引力,但这并不意味着Apache Solr已经死亡。虽然有些人可能不这么认为,但Solr仍然是最受欢迎的搜索引擎之一,拥有强大的社区和开源支持。
与Solr相比,Elasticsearch易于安装且非常轻巧。此外,你可以在几分钟内安装并运行Elasticsearch。但是,如果Elasticsearch管理不当,这种易于部署和使用可能会成为一个问题。基于JSON的配置很简单,但如果要为文件中的每个配置指定注释,那么它不适合您。总的来说,如果你的应用使用的是JSON,那么Elasticsearch是一个更好的选择。否则,请使用Solr,因为它的schema.xml和solrconfig.xml都有很好的文档记录。
Solr拥有更大,更成熟的用户,开发者和贡献者社区。ES虽拥有的规模较小但活跃的用户社区以及不断增长的贡献者社区。
Solr贡献者和提交者来自许多不同的组织,而Elasticsearch提交者来自单个公司。
Solr更成熟,但ES增长迅速,更稳定。
Solr是一个非常有据可查的产品,具有清晰的示例和API用例场景。 Elasticsearch的文档组织良好,但它缺乏好的示例和清晰的配置说明。

由于易于使用,Elasticsearch在新开发者中更受欢迎。一个下载和一个命令就可以启动一切。
如果除了搜索文本之外还需要它来处理分析查询,Elasticsearch是更好的选择
如果需要分布式索引,则需要选择Elasticsearch。对于需要良好可伸缩性和以及性能分布式环境,Elasticsearch是更好的选择。
Elasticsearch在开源日志管理用例中占据主导地位,许多组织在Elasticsearch中索引它们的日志以使其可搜索。
如果你喜欢监控和指标,那么请使用Elasticsearch,因为相对于Solr,Elasticsearch暴露了更多的关键指标

6、Elasticsearch 应用案例

GitHub: 2013年初,抛弃了Solr,采取Elasticsearch 来做PB级的搜索。“GitHub使用Elasticsearch搜索20TB的数据,包括13亿文件和1300亿行代码”。
维基百科:启动以Elasticsearch为基础的核心搜索架构
SoundCloud:“SoundCloud使用Elasticsearch为1.8亿用户提供即时而精准的音乐搜索服务”。
百度:目前广泛使用Elasticsearch作为文本数据分析,采集百度所有服务器上的各类指标数据及用户自定义数据,通过对各种数据进行多维分析展示,辅助定位分析实例异常或业务层面异常。目前覆盖百度内部20多个业务线(包括云分析、网盟、预测、文库、直达号、钱包、风控等),单集群最大100台机器,200个ES节点,每天导入30TB+数据。
新浪:使用Elasticsearch分析处理32亿条实时日志。
阿里:使用Elasticsearch构建日志采集和分析体系。
Stack Overflow:解决Bug问题的网站,全英文,编程人员交流的网站。

二、ES 核心概念

1、ElasticSearch 是面向文档的,关系行数据库 和 ElasticSearch 客观对比 【一切皆为JSON】
image.png

elasticsearch(集群)中可以包含多个索引(数据库),每个索引中可以包含多个类型(表),每个类型下又包含多个文档(行),每个文档中又包含多个字段(列)。

2、物理设计:

elasticsearch在后台把每个索引划分成多个分片,每片分片可以在集群中的不同服务器间迁移一个人就是一个集群!默认的集群名称就是elaticsearh


image.png
3、逻辑设计:

一个索引类型中,包含多个文档,比如说文档1,文档2。当我们索引一篇文档时,可以通过这样的一各顺序找到它:
索引 -> 类型 -> 文档ID,通过这个组合我们就能索引到某个具体的文档。注意:ID不必是整数,实际上它是个字符串。

1、文档:就是一条数据

1       阿K    18      码农
2       K桑    32      CTO

之前说elasticsearch是面向文档的,那么就意味着索引和搜索数据的最小单位是文档,elasticsearch中,文档有几个重要属性:
(1)自我包含,一篇文档同时包含字段和对应的值,也就是同时包含key:value
(2)可以是层次型的,一个文档中包含自文档,复杂的逻辑实体就是这么来的!{就是一个json对象! fastijson进行自动转换!}
(3)灵活的结构,文档不依赖预先定义的模式,我们知道关系型数据库中,要提前定义字段才能使用,在elasticsearch中,对于字段是非常灵活的,有时候,我们可以忽略该字段,或者动态的添加一个新的字段。

尽管我们可以随意的新增或者忽略某个字段,但是,每个字段的类型非常重要,比如一个年龄字段类型,可以是字符串也可以是整型。因为elasticsearch会保存字段和类型之间的映射及其他的设置。这种映射具体到每个映射的每种类型,这也是为什么在 elasticsearch中,类型有时候也称为映射类型。

2、类型:已经废弃,看看就好,没啥用


image.png

类型是文档的逻辑容器,就像关系型数据库一样,表格是行的容器。类型中对于字段的定义称为映射,比如name映射为字符串类型。我们说文档是无模式的,它们不需要拥有映射中所定义的所有字段,比如新增一个字段,那么elasticsearch是怎么做的呢?elasticsearch会自动的将新字段加入映射,但是这个字段的不确定它是什么类型,elasticsearch就开始猜,如果这个值是18,那么elasticsearch会认为它是整形。但是elasticsearch也可能猜不对,所以最安全的方式就是提前定义好所需要的映射,这点跟关系型数据库殊途同归了,先定义好字段,然后再使用,别整什么鸡儿。

3、索引:就是数据库
索引是映射类型的容器,elasticsearch中的索引是一个非常大的文档集合。索引存储了映射类型的字段和其他设置。然后它们被存储到了各个分片上了。臭宝,我们来研究下分片是如何工作的吧。

物理设计: 节点和分片如何工作?
一个集群至少有一个节点,而一个节点就是一个elasricsearch进程,节点可以有多个索引默认的,如果你创建索引,那么索引将会有个5个分片( primary shard ,又称主分片)构成的,每一个主分片会有一个副本( replica shard ,又称复制分片)


image.png

上图是一个有3个节点的集群,可以看到主分片和对应的复制分片都不会在同一个节点内,这样有利于某个节点挂掉了,数据也不至于丢失。实际上,一个分片是一个Lucene索引,一个包含倒排索引的文件目录,倒排索引的结构使得elasticsearch在不扫描全部文档的情况下,就能告诉你哪些文档包含特定的关键字。不过,等等,倒排索引是什么呢?

4、倒排索引
elasticsearch 使用的是一种称为倒排索引的结构,采用 Lucene倒排索作为底层。这种结构适用于快速的全文搜索,一个索引由文档中所有不重复的列表构成,对于每一个词,都有一个包含它的文档列表。例如,现在有两个文档,每个文档包含如下内容:

study every day,good good up to forever     # 文档1包含的内容
To forever,study every day,good good up    #文档2包含的内容

为了创建倒排索引,我们首先要将每个文档拆分成独立的词(或称为词条或者tokens),然后创建一个包含所有不重复的词条的排序列表,然后列出每个词条出现在哪个文档:

image.png

现在,我们试图搜索to forever,只需要查看包含每个词条的文档 分数(权重)


image.png

两个文档都匹配,但是第一个文档比第二个匹配程度更高。如果没有别的条件,现在,这两个包含关键字的文档都将返回

案例2:
image.png

image.png

如果要搜索含有python标签的文章,那相对于查找所有原始数据而言,查找倒排索引后的数据将会快的多。只需要查看标签这一栏,然后获取相关的文章ID即可。|

★ elasticsearch的索引和Lucene的索引对比

在elasticsearch中,索引这个词被频繁使用,这就是术语的使用。在elasticsearch中,索引被分为多个分片,每份分片是一个Lucene的索引。所以一个elasticsearch索引是由多个Lucene索引组成的。别问为什么,谁让elasticsearch使用Lucene作为底层呢!如无特指,说起索引都是指elasticsearch的索引。

三、IK 分词器插件

1、什么是 IK 分词器
分词:即把一段中文或者别的划分成一个个的关键字,我们在搜索时候会把自己的信息进行分词,会把数据库中或者索引库中的数据进行分词,然后进行一个匹配操作,默认的中文分词是将每个字看成一个词,比如“我爱阿K"会被为"我""爱""阿""K”,这显然是不符合要求的,所以我们需要安装中文分词器ik来解决这个问题。如果要使用中文,建议使用ik分词器

IK提供了两个分词算法:∶ ik_smart和ik_max_word,其中ik_smart为最少切分,ik_max_word为最细粒度划分!

2、安装:直接解压到 es 的 plugins 文件夹下
下载地址:https://github.com/medcl/elasticsearch-analysis-ik/releases

(1)进入es 控制台:docker exec -it 71a0 /bin/bash

image.png

(2)进入 plugins 文件夹下 创建 IK文件夹,下载文件
创建文件夹:mkdir /plugins /plugins

安装wget:yum install wget
下载版本:wget https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.4.2/elasticsearch-analysis-ik-7.4.2.zip

(3)解压:unzip elasticsearch-analysis-ik-7.4.2.zip

(4)删除:rm -rf elasticsearch-analysis-ik-7.4.2.zip

(5)重启镜像:docker restart 71a0

3、用 kibana 测试 分词器、
(1)测试ik_smart (最少切分) and ik_max_word (最细粒度划分)

GET _analyze
{
   "analyzer":"ik_max_word" ,   
   "text": "我喜欢一个人"
}
 
image.png
GET _analyze
{
  "analyzer":"ik_smart" ,   
   "text": "我喜欢一个人"
}
 

(2)测试 阿K,吹牛逼 词条,发现用最少切分也被拆开;随意需要自定义词典


image.png

4、配置自定义分词器
(1)找到配置文件


image.png
image.png

(2)创建对应的自定义词典,内容:阿K,吹牛逼;文件名为 ak.dic,在 ik 文件夹下
vi plugins/ik/config/ak.dic

image.png

(3)修改配置 IKAnalyzer.cfg.xml,引入 ak.dic
打开配置文件:vi IKAnalyzer.cfg.xml
如果用 vi打开乱码::set encoding=utf8,调整
写入配置:

image.png

(4)重启镜像:docker restart 71a0

四、Rest 风格说明

一种软件架构风格,而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。

基本rest命令:


image.png

1、创建一个索引

PUT  /索引名/类型名字/文档 id
{
      请求体
}
image.png

2、创建索引规则,声明字段类型


image.png

3、查看 创建好的索引规则


image.png

4、7x版本后,废弃了类型,默认类型为 _doc,默认为字段提供类型


image.png

4、查看索引(库)信息


image.png

5、修改一:前面的饿值会被覆盖


image.png

6、修改二:灵活性更好,指定修改对应值


image.png

7、删除


image.png

五、关于文档的基本操作(查询)

1、简单查询

插入测试数据

PUT /mykk/user/1
{
  "name":"阿K11",
  "age":18,
  "bir":"1997-06-06",
  "tags":["英俊","帅气","执着"]
}

PUT /mykk/user/2
{
  "name":"阿K22",
  "age":22,
  "bir":"1997-06-06",
  "tags":["英俊","帅气","渣男"]
}

PUT /mykk/user/3
{
  "name":"阿K33",
  "age":23,
  "bir":"1997-06-06",
  "tags":["英俊","帅气","美丽"]
}

(1)最简单的查询 :


image.png

(2)条件查询:


image.png
2、复杂查询 select(排序,分页,高亮,模糊,精准查询)

(1)简单条件的变种写法


image.png

(2)输出指定字段


image.png

(3)排序:


image.png

(4)分页:from 是从0开始的


image.png

(5)多条件匹配【and】:must 所有条件都要符合 eg: where id =1 and name =mykk


image.png

(6)多条件匹配【or 】:should 所有条件都要符合 eg: where id =1 or name =mykk


image.png

(7)多条件匹配【not 】:must_not 所有条件都要符合 eg: where id !=1


image.png

(8)多条件匹配【 filter 】:filter 所有条件都要符合 【 gt 是大于,gte 是大于等于,lt 小于, lte 小于等于 】


image.png
3、精确查询详解

term 查询是通过倒排索引指定的词条进程精确查找

1、关于分词
(1)term ,直接查询精确的
(2)match,会使用分词器解析(先分析文档,然后再通过分析的文档进行查询)

两个类型 text , keyword
text:会被分词器解析,拆分掉查询
keyword:不会被分词器解析,不可再分

4、高亮搜索
image.png

六、集成 SpringBoot

1、看文档:https://www.elastic.co/guide/index.html

(1)找到es的客户端 用于开发连接


image.png

(2)找到java rest版的


image.png

(3)选择高级版


image.png

(4)找到maven依赖


image.png

image.png
        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>elasticsearch-rest-high-level-client</artifactId>
            <version>7.4.2</version>
        </dependency>

(5)初始化对象


image.png
2、实操【索引】

(1)创建springboot新工程勾选


image.png

(2)编写配置

@Configuration
public class ElasticSearchClientConfig {

    @Bean
    public RestHighLevelClient restHighLevelClient() {
        return new RestHighLevelClient (
                RestClient.builder (
                        new HttpHost ("101.34.180.131", 9200, "http")));
    }
}

(3)创建索引

@SpringBootTest
class ElasticshedemoApplicationTests {

    @Autowired
    @Qualifier("restHighLevelClient")
    private RestHighLevelClient client;
    
    // 测试创建索引   request   PUT  mykk_demo1
    @Test
    void testCreateIndex() throws Exception{
        // 1、创建索引请求
        CreateIndexRequest request = new CreateIndexRequest ("mykk_demo1");
        // 2、客户端执行请求 IndicesClient ,请求后获取响应
        CreateIndexResponse createIndexResponse = client.indices ( ).create (request, RequestOptions.DEFAULT);

        System.out.println (createIndexResponse );
    }

}

(4)索引是否存在

    // 测试获取索引
    // 存在返回  true
    @Test
    void testExistIndex()throws Exception{
        GetIndexRequest request = new GetIndexRequest ("mykk_demo1");
        boolean exists = client.indices ( ).exists (request, RequestOptions.DEFAULT);
        System.out.println (exists );
    }

(5)删除索引

    // 测试删除索引
    @Test
    void testDeleteIndex()throws Exception{
        DeleteIndexRequest req = new DeleteIndexRequest ("mykk_demo1");
        AcknowledgedResponse delete = client.indices ( ).delete (req, RequestOptions.DEFAULT);
        System.out.println (delete.isAcknowledged () );
    }
3、实操【文档】

(1)添加文档

   // 测试文档添加
    @Test
    void testAddDocument()throws Exception{
        User user = new User ("阿K", 18);

        // 创建请求
        IndexRequest request = new IndexRequest ("mykk_demo1");

        // 请求体  put /mykk_demo1/_doc/1
        request.id ("1");
        request.timeout ("1s"); // 设置超时

        // 将数据放入请求  json
        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 testExitsDocument() throws Exception{
        GetRequest getRequest = new GetRequest ("mykk_demo1", "1");

        // 不获取返回的 _source 上下文信息(提高效率)
        getRequest.fetchSourceContext (new FetchSourceContext (false));
        getRequest.storedFields ("_none_");

        boolean exists = client.exists (getRequest, RequestOptions.DEFAULT);
        System.out.println (exists );
    }

(3)获取文档记录

    // 测试获取文档信息
    @Test
    void testGetDocument() throws Exception{
        GetRequest getRequest = new GetRequest ("mykk_demo1", "1");

        // 获取文档内容
        GetResponse response = client.get (getRequest, RequestOptions.DEFAULT);
        // 打印文档的内容
        System.out.println (response.getSourceAsString () );
        System.out.println (response );
    }

(4)更新文档记录

    // 测试更新文档信息
    @Test
    void testUpdateDocument() throws Exception{
        // 获取文档
        UpdateRequest updateRequest = new UpdateRequest ("mykk_demo1", "1");
        updateRequest.timeout ("1s");


        User user = new User ("阿K", 23);
        updateRequest.doc (JSON.toJSONString (user),XContentType.JSON);

        UpdateResponse updateResponse = client.update (updateRequest, RequestOptions.DEFAULT);
        System.out.println (updateResponse.status () );
    }

(6)删除文档记录

    // 测试删除文档信息
    @Test
    void testDeleteDocument() throws Exception{
        DeleteRequest deleteRequest = new DeleteRequest ("mykk_demo1", "1");
        deleteRequest.timeout ("1s");

        DeleteResponse deleteResponse = client.delete (deleteRequest, RequestOptions.DEFAULT);
        System.out.println (deleteResponse.status () );
    }

(5)插入文档记录

    // 测试批量文档记录
    @Test
    void testBulkDocument() throws Exception{
        BulkRequest bulkRequest = new BulkRequest ( );
        bulkRequest.timeout ("10s");

        ArrayList<User> userList = new ArrayList<> ( );
        userList.add (new User ("阿K1",1));
        userList.add (new User ("阿K2",3));
        userList.add (new User ("阿K3",5));
        userList.add (new User ("阿K4",7));
        userList.add (new User ("阿K5",9));

        // 批处理请求
        for (int i = 0; i < userList.size ( ); i++) {
            // 批量更新和删除,在这里修改对应请求即可
            bulkRequest.add (new IndexRequest ("mykk_demo1")
            .id (""+(i+1))
            .source (JSON.toJSONString (userList.get (i)),XContentType.JSON));
        }

        BulkResponse bulkResponse = client.bulk (bulkRequest, RequestOptions.DEFAULT);
        System.out.println (bulkResponse.hasFailures () );// 是否成功,false 代表成功
    }

(7)查询

    // 查询
    // SearchRequest 搜索请求
    // SearchSourceBuilder 条件构造
    //  HighlightBuilder 构建高亮
    //  TermQueryBuilder 精确查询
    //  MatchAllQueryBuilder 精确所有
    @Test
    // 记住存入的要查询的不能是中文,因为还没有处理
    void testSearch() throws Exception{

        SearchRequest searchRequest = new SearchRequest ("mykk_demo1");
        // 构建搜索条件
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder ( );
        sourceBuilder.highlighter ();

        // 查询条件:可以使用 QueryBuilders 工具来实现
        // QueryBuilders.termQuery 精确
        // QueryBuilders.matchAllQuery () 匹配所有
        TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery ("name", "ak1");
        // MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery ( );
        sourceBuilder.query (termQueryBuilder);
        sourceBuilder.timeout (new TimeValue (60, TimeUnit.SECONDS));

        searchRequest.source (sourceBuilder);

        SearchResponse searchResponse = client.search (searchRequest, RequestOptions.DEFAULT);
        System.out.println (JSON.toJSONString (searchResponse.getHits ()) );
        System.out.println ("===========================================" );
        for (SearchHit documentFields : searchResponse.getHits ( ).getHits ( )) {
            System.out.println (documentFields.getSourceAsMap () );
        }
    }

七、案例(模拟京东)

搭建项目


image.png

导入静态文件
狂神百度云链接:https://pan.baidu.com/s/1PT3jLvCksOhq7kgAKzQm7g 提取码:s824

1、爬虫

(1)去京东官网,找到url搜索规则:https://search.jd.com/Search?keyword=java

image.png

(2)编写配置文件

# 应用名称
spring.application.name=jingdongelactichserchdemo
# 应用服务 WEB 访问端口
server.port=8080
# THYMELEAF (ThymeleafAutoConfiguration)
# 开启模板缓存(默认值: true )
spring.thymeleaf.cache=false
spring.elasticsearch.rest.uris= 101.34.180.131:9200

(3)导入网页解析的依赖,不支持视频的

        <!--解析网页-->
        <dependency>
            <groupId>org.jsoup</groupId>
            <artifactId>jsoup</artifactId>
            <version>1.10.2</version>
        </dependency>

(4)封装对象用于映射每个爬取的商品(li标签)


image.png

(5)爬取京东的搜索工具方法

@Component
public class HtmlParseUtil {

    public static void main(String[] args)throws Exception {
        parseJD ("阿K").forEach (System.out::println);
    }

    /**
     * 用于搜索返回商品爬取数据
     * @param keyword     用于搜索
     * @return            返回商品集合爬取的数据
     * @throws Exception
     */
    public static List<Content> parseJD(String keyword) throws Exception{
        // 爬取的url :https://search.jd.com/Search?keyword=java
        // &enc=urt8 解决中文传参问题
        String url = "https://search.jd.com/Search?keyword="+keyword+"&enc=urt8";
        // Document 既是 html的 Document对象
        Document document = Jsoup.parse (new URL (url), 30000);
        // 所有 js操作 dom的方法这里都有
        Element element = document.getElementById ("J_goodsList");
        // 获取所有的 li元素
        Elements elements = element.getElementsByTag ("li");

        // 根据打印找出懒加载图片的 赋值标签,data-lazy-img
        //System.out.println (elements );

        // 获取元素中的内容,这里 el 就是每一个 li 标签
        ArrayList<Content> contents = new ArrayList<> ( );
        for (Element el : elements) {
            // 关于这种图片特别多的网站,采用的是延迟加载
            // source-data-lazy-img
            String img = el.getElementsByTag ("img").eq (0).attr ("data-lazy-img");
            String price = el.getElementsByClass ("p-price").eq (0).text ();
            String title = el.getElementsByClass ("p-name").eq (0).text ();
            contents.add (new Content (title,img,price));
        }
        return contents;
    }
}

(6)编写搜索爬的数据,存储到 es 中

Controller
@RestController
public class ContextController {

    @Autowired
    private ContextService contextService;


    // 爬数据,存es
    @GetMapping("/parse/{keyword}")
    public Boolean parse(@PathVariable("keyword") String keyword)throws Exception{
        return contextService.parseContext (keyword);
    }

    // 分页查询
    @GetMapping("/search/{keyword}/{pageNo}/{pageSize}")
    public List<Map<String,Object>> search(
            @PathVariable("keyword")  String keyword,
            @PathVariable("pageNo")   int pageNo,
            @PathVariable("pageSize") int pageSize
    )throws Exception{
        return contextService.searchPage (keyword,pageNo,pageSize);
    }
}
Servoce
@Service
public class ContextService {

    @Autowired
    private RestHighLevelClient client;

    // 1、解析数据放入 es 索引中
    public boolean parseContext(String keyword) throws Exception{
        List<Content> contents = HtmlParseUtil.parseJD (keyword);

        // 将查询的数据存入 es中
        BulkRequest bulkRequest = new BulkRequest ( );
        bulkRequest.timeout ("2m");// 超时2分钟放弃

        for (int i = 0; i < contents.size ( ); i++) {
            bulkRequest.add (new IndexRequest ("jd_goods")
            .source (JSON.toJSONString (contents.get (i)),XContentType.JSON));
        }

        BulkResponse bulk = client.bulk (bulkRequest, RequestOptions.DEFAULT);
        return !bulk.hasFailures ();
    }

    // 2、搜索获取数据,分页功能
    public List<Map<String,Object>> searchPage(String keyword,int pageNo,int pageSize)throws Exception{
        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);无法解决中文搜索
        MatchQueryBuilder termQueryBuilder = QueryBuilders.matchQuery ("title", keyword);
        sourceBuilder.query (termQueryBuilder);
        sourceBuilder.timeout (new TimeValue (60, TimeUnit.SECONDS));

        // 执行搜索
        searchRequest.source (sourceBuilder);
        SearchResponse searchResponse = client.search (searchRequest, RequestOptions.DEFAULT);
        // 解析结果
        ArrayList<Map<String, Object>> list = new ArrayList<> ( );
        for (SearchHit documentFields : searchResponse.getHits ( ).getHits ( )) {
            list.add (documentFields.getSourceAsMap ());
        }
        return list;
    }
2、前后端分离

(1)运行两条命令

npm install vue

npm install axios

(2)将 node.js 初始化的,找到 两个js 放入工程的静态文件中


image.png

(3)引入编写对应js代码

<script th:src="@{/js/vue.min.js}"></script>
<script th:src="@{/js/axios.min.js}"></script>

<script>
    new Vue({
        el:"#app",
        data:{
            keyword:'',
            results:[]
        },
        methods:{
            searchKey(){
                var keyword = this.keyword;
                axios.get('search/'+keyword+'/1/10').then(response=>{
                    console.log(response);
                    this.results = response.data;
                });
            }
        }
    });
</script>
image.png

image.png
3、搜索高亮

1、改写 service的搜索分页


image.png

2、改写页面的显示


image.png

3、效果


image.png

文章参考B站:狂神说,尚硅谷以及一些博客

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容