狂神ElasticSearch(含仿京东搜索)学习笔记

ElasticSearch从入门到实战

本笔记基于【狂神说Java】ElasticSearch7.6.x最新完整教程通俗易懂

本笔记参考 ElasticSearch7.6入门学习笔记

JDK版本:1.8以上

ES,Head,Kibana,IK分词器版本:均为7.6.1

在学习ElasticSearch之前,先简单了解一下Lucene

  • Doug Cutting开发
  • apache软件基金会4 jakarta项目组的一个子项目
  • 是一个开放源代码全文检索引擎工具包
  • 不是一个完整的全文检索引擎,而是一个全文检索引擎的架构,提供了完整的查询引擎和索引引擎,部分文本分析引擎(英文与德文两种西方语言)
  • 当前以及最近几年最受欢迎的免费Java信息检索程序库

Lucene和ElasticSearch的关系:

ElasticSearch是基于Lucene 做了一下封装和增强

ElasticSearch简介

Elaticsearch,简称为es, es是一个开源的高扩展分布式全文检索引擎,它可以近乎实时的存储检索数据;本身扩展性很好,可以扩展到上百台服务器,处理PB级别(大数据时代)的数据。es也使用java开发并使用Lucene作为其核心来实现所有索引和搜索的功能,但是它的目的是通过简单的RESTful API来隐藏Lucene的复杂性,从而让全文搜索变得简单。

EliasticSearch和Solr的差别

Elasticsearch

  • Elasticsearch是一个实时分布式搜索和分析引擎。 它让你以前所未有的速度处理大数据成为可能。
  • 它用于全文搜索、结构化搜索、分析以及将这三者混合使用:
  • 维基百科使用Elasticsearch提供全文搜索高亮关键字,以及输入实时搜索(search-asyou-type)和搜索纠错(did-you-mean)等搜索建议功能。
  • 英国卫报使用Elasticsearch结合用户日志和社交网络数据提供给他们的编辑以实时的反馈,以便及时了解公众对新发表的文章的回应。
  • StackOverflow结合全文搜索与地理位置查询,以及more-like-this功能来找到相关的问题和答案。
  • Github使用Elasticsearch检索1300亿行的代码。
  • 但是Elasticsearch不仅用于大型企业,它还让像DataDog以及Klout这样的创业公司将最初的想法变成可扩展的解决方案。
  • Elasticsearch可以在你的笔记本上运行,也可以在数以百计的服务器上处理PB级别的数据。
  • Elasticsearch是一个基于Apache Lucene™的开源搜索引擎。无论在开源还是专有领域, Lucene可被认为是迄今为止最先进、性能最好的、功能最全的搜索引擎库。
  • 但是, Lucene只是一个库。 想要使用它,你必须使用Java来作为开发语言并将其直接集成到你的应用中,更糟糕的是, Lucene非常复杂,你需要深入了解检索的相关知识来理解它是如何工作的。
  • Elasticsearch也使用Java开发并使用Lucene作为其核心来实现所有索引和搜索的功能,但是它的目的是通过简单的RESTful API来隐藏Lucene的复杂性,从而让全文搜索变得简单。

Solr

  • Solr是Apache下的一个顶级开源项目,采用Java开发,它是基于Lucene的全文搜索服务器。Solr提供了比Lucene更为丰富的查询语言,同时实现了可配置可扩展,并对索引、搜索性能进行了优化
  • Solr可以独立运行,运行在letty. Tomcat等这些Selrvlet容器中 , Solr 索引的实现方法很简单,用POST方法向Solr服务器发送一个描述Field及其内容的XML文档, Solr根据xml文档添加、删除、更新索引。Solr 搜索只需要发送HTTP GET请求,然后对Solr返回xml、json等格式的查询结果进行解析,组织页面布局。
  • Solr不提供构建UI的功能, Solr提供了一个管理界面,通过管理界面可以查询Solr的配置和运行情况。
  • Solr是基于lucene开发企业级搜索服务器,实际上就是封装了lucene.
  • Solr是一个独立的企业级搜索应用服务器,它对外提供类似于Web-service的API接口。用户可以通过http请求,向搜索引擎服务器提交-定格式的文件,生成索引;也可以通过提出查找请求,并得到返回结果。

总结

1、es基本是开箱即用(解压就可以用!) ,非常简单。Solr安装略微复杂一丢丢!
2、Solr 利用Zookeeper进行分布式管理,而Elasticsearch自身带有分布式协调管理功能。
3、Solr 支持更多格式的数据,比如JSON、XML、 CSV ,而Elasticsearch仅支持json文件格式
4、Solr 官方提供的功能更多,而Elasticsearch本身更注重于核心功能,高级功能多有第三方插件提供,例如图形化界面需要kibana友好支撑
5、Solr 查询快,但更新索引时慢(即插入删除慢) ,用于电商等查询多的应用;

  • ES建立索引快(即查询慢) ,即实时性查询快,用于facebook新浪等搜索。
  • Solr是传统搜索应用的有力解决方案,但Elasticsearch更适用于新兴的实时搜索应用。

6、Solr比较成熟,有一个更大,更成熟的用户、开发和贡献者社区,而Elasticsearch相对开发维护者较少,更新太快,学习使用成本较高。

ES安装

  1. JDK1.8就行,JDK版本过低只会警告不会报错
  2. 解压即用,非常方便,启动在bin目录下的.bat文件
bin 启动文件目录
config 配置文件目录
    1og4j2 日志配置文件
    jvm.options java 虚拟机相关的配置(默认启动占1g内存,内容不够需要自己调整)
    elasticsearch.ym1 elasticsearch 的配置文件! 默认9200端口!跨域!
1ib 
    相关jar包
modules 功能模块目录
plugins 插件目录
    ik分词器

http://localhost:9200/

Head安装

  1. 版本和ES一致
  2. 修改ES中的配置文件,这样就可以跨IP,跨地址访问到JSON数据了
http.cors.enabled: true
http.cors.allow-origin: "*"
  1. 进入到head目录中打开CMD运行cnpm install
  2. npm run start启动

http://localhost:9100/

Kibana安装

  1. 版本和ES一致
  2. 修改配置文件设置为中文i18n.locale: "zh-CN"

图1 设置Kibana为中文

在这里插入图片描述

  1. 启动在bin目录下的.bat文件

http://localhost:5601/

ES核心概念

  • 如果你是初学者

    • 索引 可以看做 “数据库”
    • 类型 可以看做 “表”
    • 文档 可以看做 “库中的数据(表中的行)”
  • 这个head,我们只是把它

    当做可视化数据展示工具

    ,之后

    所有的查询都在kibana中进行

    • 因为不支持json格式化,不方便

概述

1、索引(ElasticSearch)

  • 包多个分片

2、字段类型(映射)

  • 字段类型映射(字段是整型,还是字符型…)

3、文档

4、分片(Lucene索引,倒排索引)

ElasticSearch是面向文档,关系行数据库和ElasticSearch客观对比!一切都是JSON!

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

IK分词器(插件)

安装

  1. 版本一致
  2. 解压到ES中的plugins中,使用命令

图2 检测插件安装是否成功

在这里插入图片描述

如上图所示表示插件安装成功

图3 各个软件位置

在这里插入图片描述

测试

  1. 启动ES,Head,Kibana
  2. 使用Kibana工具,在控制台执行测试用例
GET _analyze
{
  "analyzer": "ik_smart", 
  "text": "中国共产党"
}	

GET _analyze
{
  "analyzer": "ik_max_word", 
  "text": "中国共产党"
}

图4 最粗粒度拆分ik_smart

在这里插入图片描述

图5 最细粒度划分ik_max_word

在这里插入图片描述

两次查询结果的不同在于analyzer属性的不同ik_smartik_max_word

  1. ik_max_word
    会将文本做最细粒度的拆分,比如会将“中华人民共和国人民大会堂”拆分为“中华人民共和国、中华人民、中华、华人、人民共和国、人民、共和国、大会堂、大会、会堂等词语。

  2. ik_smart
    会做最粗粒度的拆分,比如会将“中华人民共和国人民大会堂”拆分为中华人民共和国、人民大会堂。

自定义

  1. 先使用一个测试用例
GET _analyze
{
  "analyzer": "ik_smart", 
  "text": "赵富强编程Java"
}

GET _analyze
{
  "analyzer": "ik_max_word", 
  "text": "赵富强编程Java"
}

图6 添加分词器之前的ik_smart查询

在这里插入图片描述

图7 添加分词器之前的ik_max_word查询

在这里插入图片描述

  1. 增加数据字典并且修改配置文件

图8 添加分词器过程

  1. 重启所有服务

图9 es启动窗口看到了ccy.idc文件配置成功

在这里插入图片描述

可以看到ccy.dic已经配置成功

  1. 重新测试

图10 添加分词器后的ik_smart查询

在这里插入图片描述

图11 添加分词器后的ik_max_word查询

在这里插入图片描述

Rest风格

https://www.elastic.co/guide/en/elasticsearch/reference/current/keyword.html

图12 rest风格

在这里插入图片描述

  • 字符串类型

text,keyword

  • 数值类型

long,integer,short,byte,double,float,half float,scaled float

  • 日期类型

date

  • 布尔值类型

boolean

  • 二进制类型

binary

  • 等等…

关于索引的基本操作

  1. 创建索引
PUT /索引名/类型名/文档ID
{请求体}

图13 添加test索引

在这里插入图片描述

实际效果

图14 创建test索引效果

在这里插入图片描述

  1. 删除索引
DELETE test2 // DELETE 索引名称
  1. 修改索引

这里test3索引是用另一种方式创建的

图15 用另一种方式创建索引

在这里插入图片描述

  • 通过PUT
PUT /test3/_doc/1
{
  "name": "CCY123",
  "age": 20,
  "birthday": "2001-03-25"
}

图16 默认类型

在这里插入图片描述

图17 更新一次version自增1

在这里插入图片描述

可以执行更新操作之后,version会发生了变化

  • 通过POST
POST /test3/_doc/1/_update
{
  "doc": {
    "name": "赵富双"
  }
}

图18 POST更新方式

在这里插入图片描述

  1. 查看索引信息
GET test3

关于文档的基本操作

基本操作

  1. 添加文档
PUT /ccy/usr/3
{
  "name": "ZFS",
  "age": 21,
  "desc": "最怕你一生碌碌无为还安慰自己平凡可贵",
  "tags": ["用友","字节跳动","努力"]
}

图19 添加文档

在这里插入图片描述

后续又添加了两条数据

  1. 获得文档
GET /ccy/usr/1

图20 获得文档

在这里插入图片描述

  1. 更新文档
  • 通过PUT

图21 通过PUT更新文档

在这里插入图片描述

如果修改的数据不全面,其他的文档内容就会被置空所以推荐使用POST

  • 通过POST
POST ccy/usr/2/_update{  "doc": {    "name": "Ccy"  }}

如果去除_update,那么除了修改的文档,其他的内容也会被置空

加上_update,自由度很高,除了修改的文档,其他内容不会改变

  1. 查询文档
GET ccy/usr/_search?q=name:赵富双GET ccy/usr/_search // 上面的全限定名称{  "query": {    "match": {      "name": "赵富双"    }  }}

图22 查询文档

在这里插入图片描述

  1. 复杂查询文档

  2. 过滤结果查询

GET ccy/usr/_search{  "query": {    "match": {      "name": "赵富双"    }  },  "_source": ["name","age"]}

图23 过滤结果查询

在这里插入图片描述

  1. 排序查询
GET ccy/usr/_search{  "query": {    "match": {      "name": "赵富双"    }  },  "sort": [    {      "age": {        "order": "asc" //升序      }    }  ]}
  1. 相当于mysql的limit查询
GET ccy/usr/_search{  "query": {    "match": {      "name": "赵富双"    }  },  "sort": [    {      "age": {        "order": "asc"      }    }  ],  "from": 0, // 初始  "size": 1 // 单页面数据条数}
  1. 布尔值查询

must查询,must中的条件必须全部满足

GET ccy/usr/_search{  "query": {    "bool": {      "must": [        {          "match": {            "name": "双"          }        },        {          "match": {            "age": "23"          }        }      ]    }  }}

should查询,should中的条件只要有满足就可以

GET ccy/usr/_search{  "query": {    "bool": {      "should": [        {          "match": {            "name": "双"          }        },        {          "match": {            "age": "23"          }        }      ]    }  }}
  1. filter过滤器
GET ccy/usr/_search{  "query": {    "bool": {      "must": [        {          "match": {            "name": "双"          }        }      ],      "filter": {        "range": {          "age": {            "gt": 1,             "lt": 30  // lt小于 lte小于等于 gt大于 gte 大于等于          }        }      }    }  }}
  1. tags查询
GET ccy/usr/_search{  "query": {    "match": {      "tags": "JAVA 宅男" // 中间拿空格隔开    }  }}

图24 tags查询多个tag用空格间隔

在这里插入图片描述

  1. 精确查询

term查询时通过倒排索引指定的词条进行精确查找到的

关于分词

keyword关键字不能被分词器解析

  1. 高亮查询
GET ccy/usr/_search
{
  "query": {
    "match": {
      "name": "双"
    }
  },
  "highlight": {
    "pre_tags": "<p class='key' style='color:red'>",  // 自定义标签默认为<em>
    "post_tags": "</p>", 
    "fields": {
      "name":{}
    }
  }
}

集成spring boot

  1. 创建springboot项目并且修改elasticsearch的版本确保和本地一致
<properties>
    <java.version>1.8</java.version>
    <!--        自定义版本依赖,保证和本地一致-->
    <elasticsearch.version>7.6.1</elasticsearch.version>
</properties>
<dependencies>
    <!--        elasticsearch-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
    </dependency>
    <!--        web-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--        热部署-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <!--        配置类-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
        <optional>true</optional>
    </dependency>
    <!--        lombok-->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <!--        test-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>
  1. 编写配置类
@Configurationpublic class ElasticSearchClientConfig {    @Bean    public RestHighLevelClient restHighLevelClient() {        RestHighLevelClient client = new RestHighLevelClient(                RestClient.builder(                        new HttpHost("127.0.0.1", 9200, "http")));        return client;    }}
  1. 测试创建索引,查询索引是否存在,删除索引
@SpringBootTestclass EsApiApplicationTests {    @Autowired    public RestHighLevelClient restHighLevelClient;    //    创建索引    @Test    void test1() throws IOException {//        创建索引请求        CreateIndexRequest request = new CreateIndexRequest("ccy_index");//        执行创建请求        CreateIndexResponse createIndexResponse = restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);        System.out.println(createIndexResponse);    }    //    获得索引,只能判断索引是否存在    @Test    void test2() throws IOException {        GetIndexRequest request = new GetIndexRequest("ccy_index");        boolean exists = restHighLevelClient.indices().exists(request, RequestOptions.DEFAULT);        System.out.println(exists);    }    //    删除索引    @Test    void test3() throws IOException {        DeleteIndexRequest request = new DeleteIndexRequest("ccy_index");        AcknowledgedResponse delete = restHighLevelClient.indices().delete(request, RequestOptions.DEFAULT);        System.out.println(delete.isAcknowledged());    }}
  1. 文档的一系列操作已经复杂操作(批量插入,高亮查询,精确查询)
    //    添加文档
    @Test
    void test4() throws IOException {
//        创建对象
        User user = new User("CCY", 3);
//        创建请求
        IndexRequest request = new IndexRequest("ccy_index");
//        规则 put/ccy_index/_doc/1
        request.id("1");
        request.timeout(TimeValue.timeValueSeconds(1));
        request.timeout("1s");
//        将我们的数据放到请求    json
        request.source(JSON.toJSONString(user), XContentType.JSON);
//        客户端发生请求,获取相应的结果
        IndexResponse indexResponse = restHighLevelClient.index(request, RequestOptions.DEFAULT);
        System.out.println(indexResponse.toString());
        System.out.println(indexResponse.status());
    }

    //    获取文档判断是否存在
    @Test
    void test5() throws IOException {
        GetRequest request = new GetRequest("ccy_index", "1");
//        不获取返回的_source
        request.fetchSourceContext(new FetchSourceContext(false));
        request.storedFields("_none_");
        boolean exists = restHighLevelClient.exists(request, RequestOptions.DEFAULT);
        System.out.println(exists);
    }

    //    获取文档信息
    @Test
    void test6() throws IOException {
        GetRequest request = new GetRequest("ccy_index", "1");
        GetResponse getResponse = restHighLevelClient.get(request, RequestOptions.DEFAULT);
        System.out.println(getResponse.getSourceAsString());
        System.out.println(getResponse);
    }

    //    更新文档的信息
    @Test
    void test7() throws IOException {
        UpdateRequest updateRequest = new UpdateRequest("ccy_index", "1");
        updateRequest.timeout("1s");
        User user = new User("赵富双", 20);
        updateRequest.doc(JSON.toJSONString(user), XContentType.JSON);
        UpdateResponse response = restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT);
        System.out.println(response.status());
    }

    //    删除文档记录
    @Test
    void test8() throws IOException {
        DeleteRequest request = new DeleteRequest("ccy_index", "1");
        request.timeout("1s");
        DeleteResponse deleteResponse = restHighLevelClient.delete(request, RequestOptions.DEFAULT);
        System.out.println(deleteResponse.status());
    }

    //    特殊的,真的项目一般都会批量插入数据
    @Test
    void test9() throws IOException {
        BulkRequest bulkRequest = new BulkRequest();
        bulkRequest.timeout("10s");

        ArrayList<User> userList = new ArrayList<>();
        userList.add(new User("ccy1", 21));
        userList.add(new User("ccy2", 22));
        userList.add(new User("ccy3", 23));

        for (int i = 0; i < userList.size(); i++) {
            bulkRequest.add(
//                    批量更新和批量删除,修改对应的请求就可以了
                    new IndexRequest("ccy_index")
                            .id("" + (i + 1))
                            .source(JSON.toJSONString(userList.get(i)), XContentType.JSON));
        }
        BulkResponse bulkResponse = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
        System.out.println(bulkResponse.hasFailures()); // 判断是否失败
    }

    //    查询
//    搜索请求 searchRequest
//    条件构造 SearchSourceBuilder
//    构建精确查询
    @Test
    void test10() throws IOException {
        SearchRequest searchRequest = new SearchRequest("ccy_index");
//        构建搜索条件
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
//        查询条件,我们可以使用querybuilders 工具来实现
//        querybuilders.termquery 精准
//        querybuilders.matchallquery() 匹配所有
        TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("name", "ccy1");
//        MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
        sourceBuilder.query(termQueryBuilder);
        sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
        searchRequest.source(sourceBuilder);

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

实战

构建项目骨架

  1. 构建springboot项目引入相关依赖

图25 相关依赖

在这里插入图片描述

    <properties>        <java.version>1.8</java.version>        <!--        自定义版本依赖,保证和本地一致-->        <elasticsearch.version>7.6.1</elasticsearch.version>    </properties><!--        为了将对象转换为json数据--><dependency>    <groupId>com.alibaba</groupId>    <artifactId>fastjson</artifactId>    <version>1.2.62</version></dependency>
  1. 引入前端页面,并且编写controller层访问
@Controllerpublic class IndexController {    @GetMapping({"/", "/index"})    public String index() {        return "index";    }}

图26 页面展示

在这里插入图片描述

爬虫

爬取数据:获取请求返回的页面信息,筛选出我们想要的数据就可以了

  1. 导入jsoup包依赖
<!--        jsoup解析网页-->
<dependency>
    <groupId>org.jsoup</groupId>
    <artifactId>jsoup</artifactId>
    <version>1.10.2</version>
</dependency>
  1. 编写工具类,用来爬取京东界面
@Component
public class HtmlParseUtil {

    public static List<Content> parseJD(String keywords) throws Exception {
//        获取请求 https://search.jd.com/Search?keyword=java
        String url = "https://search.jd.com/Search?keyword=" + keywords;
//        解析网页(Jsoup返回Document就是浏览器Document对象)
        Document document = Jsoup.parse(new URL(url), 30000);
//        所有你在js中可以使用的方法这里都能使用
        Element element = document.getElementById("J_goodsList");
//        获取所有li元素
        Elements elements = element.getElementsByTag("li");
//        获取元素中的内容,这里el,就是每个li标签

        ArrayList<Content> goodslist = new ArrayList<>();
        for (Element el : elements
        ) {
            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();

            Content content = new Content(img, price, title);
            goodslist.add(content);
        }
        return goodslist;
    }
}
  1. 创建service层,并且将爬取的数据导入es库中(再次之前要创建config层,连接本地es)
@Service
public class ContentService {
    @Autowired
    private RestHighLevelClient restHighLevelClient;

    public Boolean parseContent(String keywords) throws Exception {
        List<Content> contents = new HtmlParseUtil().parseJD(keywords);
//        把查询到的数据批量添加放到es中
        BulkRequest bulkRequest = new BulkRequest();
        bulkRequest.timeout("2m");

        for (int i = 0; i < contents.size(); i++) {
            bulkRequest
                    .add(new IndexRequest("jd_goods")
                            .source(JSON.toJSONString(contents.get(i)), XContentType.JSON));
        }
        BulkResponse bulk = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
        return !bulk.hasFailures();
    }
}
  1. 心添controller层方法
@RestController
public class ContentController {
    @Autowired
    private ContentService contentService;

    @GetMapping("/parse/{keywords}")
    public Boolean parse(@PathVariable("keywords") String keywords) throws Exception {
        return contentService.parseContent(keywords);
    }
}

图27 添加文档成功

在这里插入图片描述

前后端分离

  1. 添加pojo类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Content {
    private String img;
    private String price;
    private String title;
}
  1. 添加service层方法
//    获取数据实现搜索功能
    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);
        sourceBuilder.query(termQueryBuilder);
        sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
//        执行搜索
        searchRequest.source(sourceBuilder);
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
//        解析结果
        ArrayList<Map<String, Object>> list = new ArrayList<>();
        for (SearchHit documentFields : searchResponse.getHits().getHits()
        ) {
            list.add(documentFields.getSourceAsMap());
        }
        return list;
    }
  1. 新添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 Exception {    return contentService.searchPage(keyword, pageNO, pagesize);}
  1. 导入vue和axios的js代码,在任意文件夹下执行命令下载vue和axios的js代码

图28 下载vue和js代码

在这里插入图片描述

  1. 编写逻辑代码
<!--前端使用vue,实现前后端分离-->
<script th:src="@{/js/vue.min.js}"></script>
<script th:src="@{/js/axios.min.js}"></script>
<script>
    new Vue({
        el: '#app', // 在最大的<div标签中添加 id = "app">
        data: {
            keyword: '', // 搜索关键字
            results: [] // 搜索结果
        },
        methods: {
            searchKey() {
                var keyword = this.keyword;
                console.log(keyword);
                //对接后端的接口
                axios.get('search/' + keyword + "/1/10").then(response => {
                    console.log(response);
                    this.results = response.data; // 绑定数据
                })
            }
        }
    })
</script>
  1. 修改html代码为vue格式
<div class="product" v-for="result in results">
    <div class="product-iWrap">
        <!--商品封面-->
        <div class="productImg-wrap">
            <a class="productImg">
                <img :src="result.img">
            </a>
        </div>
        <!--价格-->
        <p class="productPrice">
            <em> {{result.price}} </em>
        </p>
        <!--标题-->
        <p class="productTitle">
            <a> {{result.title}} </a>
        </p>
        <!-- 店铺名 -->
        <div class="productShop">
            <span>店铺: 狂神说Java </span>
        </div>
        <!-- 成交信息 -->
        <p class="productStatus">
            <span>月成交<em>999笔</em></span>
            <span>评价 <a>3</a></span>
        </p>
    </div>
</div>

高亮搜索

修改service层搜索方法

 //    获取数据实现高亮搜索
    public List<Map<String, Object>> searchLightPage(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);
        sourceBuilder.query(termQueryBuilder);
        sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
//        高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.requireFieldMatch(true); // 多个高亮显示
        highlightBuilder.field("title");
        highlightBuilder.preTags("<span style='color:red'>");
        highlightBuilder.postTags("</span>");
        sourceBuilder.highlighter(highlightBuilder);
//        执行搜索
        searchRequest.source(sourceBuilder);
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
//        解析结果
        ArrayList<Map<String, Object>> list = new ArrayList<>();
        for (SearchHit hit : searchResponse.getHits().getHits()
        ) {
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            HighlightField title = highlightFields.get("title");
            Map<String, Object> sourceAsMap = hit.getSourceAsMap(); // 原来的结果
//            解析高亮的字段,将原来的字段换位我们高亮的字段即可
            if (title != null) {
                Text[] fragments = title.fragments();
                String n_title = "";
                for (Text text:fragments) {
                    n_title += text;
                }
                sourceAsMap.put("title",n_title); // 高亮字段替换掉原来的内容即可
            }
            list.add(sourceAsMap);
        }
        return list;
    }
  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

存量美团骑手

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

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

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

打赏作者

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

抵扣说明:

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

余额充值