Springboot整合Elasticsearch

1.创建工程

目录结构

 2.导入依赖

注意依赖版本和安装的版本一致
<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>
        <!--fastjson-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.80</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

3.创建并编写配置类

package com.wzh.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;

/**
 * @ProjectName: Elasticsearch
 * @Package: com.wzh.config
 * @ClassName: ElasticSearchConfig
 * @Author: 王振华
 * @Description:
 * @Date: 2022/8/16 18:21
 * @Version: 1.0
 */
@Configuration
public class ElasticSearchConfig {
    //该对象可以对我们的ES进行相关的操作
    @Bean
    public RestHighLevelClient restHighLevelClient(){
        RestHighLevelClient client = new RestHighLevelClient(
                RestClient.builder(new HttpHost("127.0.0.1",9200,"http")));
        return client;
    }
}

4.创建并编写实体类

package com.wzh.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @ProjectName: Elasticsearch
 * @Package: com.wzh.entity
 * @ClassName: User
 * @Author: 王振华
 * @Description:
 * @Date: 2022/8/16 18:23
 * @Version: 1.0
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private String name;
    private String address;
    private Integer age;

}

5、测试

所有测试均在ElasticsearchApplicationTests 中编写
注入 RestHighLevelClient
//@Autowired 
 //直接将@Autowired换成@Resource注解,此注解是JDK中的注解,不会向@Autowired那样去Spring容器中寻找bean。
@Resource
public RestHighLevelClient client;

(1条消息) SpringBoot中@Autowired爆红原理及解决方案_riun、的博客-优快云博客_@autowired 爆红https://blog.youkuaiyun.com/hanhanhanxu/article/details/103530519

5.1 索引的操作

5.1.1 创建索引

    //创建索引  PUT /索引名称
    @Test
    public void testCreateIndex() throws IOException {
        //该类把创建索引的信息都封装到该类中
        CreateIndexRequest createIndexRequest = new CreateIndexRequest("test-index");

        CreateIndexResponse createIndexResponse = client.indices().create(createIndexRequest, RequestOptions.DEFAULT);
        System.out.println(createIndexResponse.isAcknowledged());//查看是否创建成功
        
        client.close();
    }

5.1.2 判断索引是否存在

    //判断索引是否存在
    @Test
    public void testIndexExists() throws IOException {
        GetIndexRequest getIndexRequest = new GetIndexRequest("test-index");

        boolean exists = client.indices().exists(getIndexRequest, RequestOptions.DEFAULT);
        System.out.println(exists);
        client.close();
    }

5.1.3 删除索引

    //删除索引
    @Test
    public void testDeleteIndex() throws IOException {
        DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest("test-index");

        AcknowledgedResponse delete = client.indices().delete(deleteIndexRequest, RequestOptions.DEFAULT);
        System.out.println(delete.isAcknowledged());
        client.close();
    }

5.2 文档的操作

5.2.1 添加文档

    //添加文档  PUT /索引/1 {name:"",age:"",address:""}
    @Test
    public void testInsertDoc() throws IOException {
        IndexRequest indexRequest = new IndexRequest("test-index");
        indexRequest.id("2"); //指定文档的id,不写则随机生成
        //指定文档的内容:String文档的json内容,XContentType xContentType:以什么格式
        indexRequest.source(JSON.toJSONString(new User("张三","郑州",22)), XContentType.JSON);

        IndexResponse indexResponse = client.index(indexRequest, RequestOptions.DEFAULT);
        System.out.println(indexResponse.getResult());
        client.close();
    }

5.2.2 判断文档是否存在

    //判断文档是否存在
    @Test
    public void testDocExist() throws IOException {
        GetRequest getRequest = new GetRequest("test-index");
        getRequest.id("2");
        boolean exists = client.exists(getRequest, RequestOptions.DEFAULT);
        System.out.println(exists);
    }

5.2.3 根据id查询文档

    //根据id查询文档
    @Test
    public void testGetDoc() throws IOException {
        GetRequest getRequest = new GetRequest("test-index");
        getRequest.id("2");
        GetResponse response = client.get(getRequest, RequestOptions.DEFAULT);

        String source = response.getSourceAsString();//转为字符串
        User user = JSON.parseObject(source, User.class);  //转为Java对象
        System.out.println(user.getAddress());

        Map<String, Object> map = response.getSourceAsMap();    //转为map
        System.out.println(map.get("address"));
        client.close();
    }

5.2.4 删除文档

    //删除文档
    @Test
    public void testDeleteDoc() throws IOException {
        DeleteRequest deleteRequest = new DeleteRequest("test-index");
        deleteRequest.id("1");
        DeleteResponse delete = client.delete(deleteRequest, RequestOptions.DEFAULT);
        System.out.println(delete.getResult());
        client.close();
    }

5.2.5 修改文档(1条消息) com.alibaba.fastjson.JSONObject之对象与JSON转换方法_beidaol的博客-优快云博客_com.alibaba.fastjson.jsonobjecthttps://blog.youkuaiyun.com/beidaol/article/details/104491950

    //修改文档
    // POST /test01/_doc/2/_update
    //{
    //  "doc":{
    //    "name":"周星驰"
    //  }
    //}
    @Test
    public void testUpdateDoc() throws IOException {
        UpdateRequest updateRequest = new UpdateRequest("test-index","2");
        User user = new User();
        user.setName("李四");
        //updateRequest.doc(JSON.toJSONString(user),XContentType.JSON);
        updateRequest.doc((JSONObject)JSONObject.toJSON(user));
        UpdateResponse update = client.update(updateRequest, RequestOptions.DEFAULT);
        System.out.println(update.getResult());
        client.close();
    }

5.2.6 批量添加文档

    //批量添加文档
    @Test
    public void testBulkDoc() throws IOException {
        BulkRequest bulkRequest = new BulkRequest("test-index");
        List<User> list = new ArrayList<>();
        list.add(new User("张三","上海",21));
        list.add(new User("李四","浙江",22));
        list.add(new User("王五","深圳",23));
        list.add(new User("赵六","北京",24));
        list.add(new User("孙七","南京",25));
      /*  for(User user:list){
            IndexRequest indexRequest=new IndexRequest();
            indexRequest.source(JSON.toJSONString(user),XContentType.JSON);
            bulkRequest.add(indexRequest);
        }*/
        //(JSONObject)JSONObject.toJSON(item) java对象转为json对象
        list.stream().forEach(item->bulkRequest.add(new IndexRequest().source((JSONObject)JSONObject.toJSON(item))));
        BulkResponse bulkResponse = client.bulk(bulkRequest, RequestOptions.DEFAULT);
        System.out.println(bulkResponse.hasFailures());
        client.close();
    }

5.2.7 复杂查询

//复杂查询  -GET /索引/_search
    // {
    //     "query":{
    //         "":{}
    //      },
    //    "from":
    //    "size":
    //     "_source":["",""],
    //     "sort":{}

    // }
    //1. 搜索请求对象SearchRequest
    //2. 构建一个条件对象SearchSourceBuilder
    //3. 把条件对象放入搜索请求对象中
    //4. 执行搜索功能
    @Test
    public void testSearch() throws IOException {
        // 1.创建查询请求对象
        SearchRequest searchRequest = new SearchRequest("test-index");
        // 2.创建一个条件对象
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        // (1)查询条件 使用QueryBuilders工具类创建
        //匹配查询
        //MatchQueryBuilder matchQuery = QueryBuilders.matchQuery("name", "张 ");
        //精准查询
        TermQueryBuilder queryBuilder = QueryBuilders.termQuery("name", "张");

        // 条件投入
        sourceBuilder.query(queryBuilder);
        // (2)其他<可有可无>:(可以参考 SearchSourceBuilder 的 字段部分)
        //分页
        sourceBuilder.from(0);
        sourceBuilder.size(1);

        //排序
        sourceBuilder.sort("age", SortOrder.ASC);

        //高亮显示
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("name");
        highlightBuilder.preTags("<font color='red'>");
        highlightBuilder.postTags("</font>");
        sourceBuilder.highlighter(highlightBuilder);


        //3. 把条件对象放入搜索请求对象中
        searchRequest.source(sourceBuilder);
        //4. 执行搜索功能
        SearchResponse search = client.search(searchRequest, RequestOptions.DEFAULT);
        System.out.println("总条数:"+search.getHits().getTotalHits().value);
        SearchHit[] hits = search.getHits().getHits();
        Arrays.stream(hits).forEach(item-> System.out.println(item.getSourceAsString()));
        Arrays.stream(hits).forEach(item-> System.out.println(item.getHighlightFields()));
        client.close();
    }

多条件查找

//多条件查询
    @Test
    public void testSearch02() throws IOException {
        SearchRequest searchRequest = new SearchRequest("qy151-index");
        //创建一个条件对象
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery().must(QueryBuilders.matchQuery("name", "李 "))
                .should(QueryBuilders.termQuery("name", "三"));
        sourceBuilder.query(queryBuilder);
        //把条件对象放入搜索请求对象中
        searchRequest.source(sourceBuilder);
        SearchResponse search = client.search(searchRequest, RequestOptions.DEFAULT);
        SearchHit[] hits = search.getHits().getHits();
        Arrays.stream(hits).forEach(item-> System.out.println(item.getSourceAsMap()));

        client.close();
    }

6.IK分词器:中文分词器

6.1 什么是IK分词器

ElasticSearch 几种常用分词器如下:

分词器分词方式
StandardAnalyzer单字分词
CJKAnalyzer二分法
IKAnalyzer词库分词

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

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

ik_max_word: 会将文本做最细粒度的拆分,比如会将"中华人民共和国国歌"拆分为"中华人民共和国,中华人民,中华,华人,人民共和国,人民,人,民,共和国,共和,和,国国,国歌",会穷尽各种可能的组合;

ik_smart: 会做最粗粒度的拆分,比如会将"中华人民共和国国歌"拆分为"中华人民共和国,国歌"。

6.2 Ik分词器的下载安装

下载地址:GitHub - medcl/elasticsearch-analysis-ik: The IK Analysis plugin integrates Lucene IK analyzer into elasticsearch, support customized dictionary.

注意:IK分词器插件的版本要和ElasticSearch的版本一致

 下载完后,解压安装包到 ElasticSearch 所在文件夹中的plugins目录中:

 再启动ElasticSearch,查看IK分词器插件是否安装成功:

安装成功!

6.3 使用Kibana测试IK

1、启动Kibana

 2、访问请求:http://localhost:5601/

 3、选择开发工具Dev Tools,点击控制台

4、在控制台编写分词请求,进行测试

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

测试 ik_smart 分词算法,最少切分

测试 ik_max_word 分词算法,最细粒度划分

分词请求:

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

 分词结果:

{
  "tokens" : [
    {
      "token" : "我",
      "start_offset" : 0,
      "end_offset" : 1,
      "type" : "CN_CHAR",
      "position" : 0
    },
    {
      "token" : "爱",
      "start_offset" : 1,
      "end_offset" : 2,
      "type" : "CN_CHAR",
      "position" : 1
    },
    {
      "token" : "中国共产党",
      "start_offset" : 2,
      "end_offset" : 7,
      "type" : "CN_WORD",
      "position" : 2
    },
    {
      "token" : "中国",
      "start_offset" : 2,
      "end_offset" : 4,
      "type" : "CN_WORD",
      "position" : 3
    },
    {
      "token" : "国共",
      "start_offset" : 3,
      "end_offset" : 5,
      "type" : "CN_WORD",
      "position" : 4
    },
    {
      "token" : "共产党",
      "start_offset" : 4,
      "end_offset" : 7,
      "type" : "CN_WORD",
      "position" : 5
    },
    {
      "token" : "共产",
      "start_offset" : 4,
      "end_offset" : 6,
      "type" : "CN_WORD",
      "position" : 6
    },
    {
      "token" : "党",
      "start_offset" : 6,
      "end_offset" : 7,
      "type" : "CN_CHAR",
      "position" : 7
    }
  ]
}

比较两个分词算法对同一句中文的分词结果,ik_max_word比ik_smart得到的中文词更多(从两者的英文名含义就可看出来),但这样也带来一个问题,使用ik_max_word会占用更多的存储空间。

6.4 扩展字典

我们用分词器对 “万里顾一程” 进行分词:先使用 ik_smart 分词算法

 在使用 ik_max_word分词算法,进行细粒度的划分:

GET _analyze
{
  "analyzer": "ik_max_word",
  "text": "万里顾一程"
}

分词结果:

{
  "tokens" : [
    {
      "token" : "万里",
      "start_offset" : 0,
      "end_offset" : 2,
      "type" : "CN_WORD",
      "position" : 0
    },
    {
      "token" : "万",
      "start_offset" : 0,
      "end_offset" : 1,
      "type" : "TYPE_CNUM",
      "position" : 1
    },
    {
      "token" : "里",
      "start_offset" : 1,
      "end_offset" : 2,
      "type" : "COUNT",
      "position" : 2
    },
    {
      "token" : "顾",
      "start_offset" : 2,
      "end_offset" : 3,
      "type" : "CN_CHAR",
      "position" : 3
    },
    {
      "token" : "一程",
      "start_offset" : 3,
      "end_offset" : 5,
      "type" : "CN_WORD",
      "position" : 4
    },
    {
      "token" : "一",
      "start_offset" : 3,
      "end_offset" : 4,
      "type" : "TYPE_CNUM",
      "position" : 5
    },
    {
      "token" : "程",
      "start_offset" : 4,
      "end_offset" : 5,
      "type" : "CN_CHAR",
      "position" : 6
    }
  ]
}

使用上面两种分词算法后,发现 “万里顾一程”被分成了“万里”、“顾”、“一程”,这是因为在IK自带的字典中没有“顾一程”这个词,如果想得到“顾一程”这个词,怎么办呢?

这就要配置自己的扩展字典了,就是在IK分词器字典中加入我们自定义的字典,在词典中加入想要的词。

在ik分词器文件的config目录中新建自定义的字典文件,以.dic为后缀,并在文件中加入“顾一程”:

 然后打开 IKAnalyzer.cfg.xml 文件,把自定义的字典添加到IK的字典中:

 重启ES和Kibana,再用分词器对 “万里顾一程” 进行分词:此时“顾一程”就是一个词了。

 ElasticSearch——IK分词器的下载及使用_万里顾—程的博客-优快云博客_ik分词器下载ElasticSearch——IK分词器的下载及使用1、什么是IK分词器ElasticSearch 几种常用分词器如下:分词器分词方式StandardAnalyzer单字分词CJKAnalyzer二分法IKAnalyzer词库分词分词∶即把一段中文或者别的划分成一个个的关键字,我们在搜索时候会把自己的信息进行分词,会把数据库中或者索引库中的数据进行分词,然后进行一个匹配操作,默认的中文分词是将每个字看成一个词,比如“我爱中国"会被分为"我"“爱”“中”"国”https://blog.youkuaiyun.com/wpc2018/article/details/121156880

7. 综合案例---京东搜索

7.1 创建springboot项目

7.2 引入依赖

jsoup是一款Java的html解析工具,主要是对html和xml文件进行解析
在写爬虫的时候,当我们用HttpClient之类的框架,得到目标网页的源码后,需要从网页源码中取得我们想要的内容。就可以使用jsoup轻松获取想要的内容。

        <dependency>
            <groupId>org.jsoup</groupId>
            <artifactId>jsoup</artifactId>
            <version>1.11.3</version>
        </dependency>

7.3 创建实体类

package com.wzh.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @ProjectName: jd-ES
 * @Package: com.wzh.entity
 * @ClassName: Product
 * @Author: 王振华
 * @Description:
 * @Date: 2022/8/16 20:23
 * @Version: 1.0
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Product {
    private String title;

    private String price;

    private String imgUrl;
}

7.4 创建公共类

jsoup使用教程_justLym的博客-优快云博客_jsoup教程jsoup是一款Java的html解析工具,主要是对html和xml文件进行解析在写爬虫的时候,当我们用HttpClient之类的框架,得到目标网页的源码后,需要从网页源码中取得我们想要的内容。就可以使用jsoup轻松获取想要的内容。jsoup的中文开发文档获取jsoup的maven方式<!-- https://mvnrepository.com/artifact/org.jsoup...https://blog.youkuaiyun.com/justLym/article/details/105715516

package com.wzh.util;

import com.wzh.entity.Product;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

/**
 * @ProjectName: jd-ES
 * @Package: com.wzh.util
 * @ClassName: HtmlparseUtil
 * @Author: 王振华
 * @Description:
 * @Date: 2022/8/16 20:26
 * @Version: 1.0
 */
public class HtmlparseUtil {
    public static List<Product> parseJd(String keyword) throws IOException {
        String path = "https://search.jd.com/Search?keyword="+keyword;
        //从文件中获取html来解析
        Document document = Jsoup.parse(new URL(path), 30000);
        // 通过元素id值来获取对应的节点
        Element jGoodsList = document.getElementById("J_goodsList");
        // 通过标签名来获取
        Elements li = jGoodsList.getElementsByTag("li");
        List<Product> list = new ArrayList<>();
       /* for (Element element:li){
            String pprice = element.getElementsByClass("p-price").eq(0).text();
            String pname = element.getElementsByClass("p-name").eq(0).text();
            String img = element.getElementsByTag("img").eq(0).attr("data-lazy-img");
            list.add(new Product(pname,pprice,img));
        }*/
        li.stream().forEach(item->{
            // 通过类名来获取
            String pprice = item.getElementsByClass("p-price").eq(0).text();
            String pname = item.getElementsByClass("p-name").eq(0).text();
            //Elements pimg = item.getElementsByClass("p-img").eq(0).text();
            String img = item.getElementsByTag("img").attr("data-lazy-img");
            list.add(new Product(pname,pprice,img));
        });
        return list;
    }
}

7.5 创建索引

7.6 后端

controller:

package com.wzh.controller;

import com.wzh.service.ProductService;
import com.wzh.vo.CommonResult;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;

/**
 * @ProjectName: jd-ES
 * @Package: com.wzh.controller
 * @ClassName: ProductController
 * @Author: 王振华
 * @Description:
 * @Date: 2022/8/17 10:49
 * @Version: 1.0
 */
@RestController
@RequestMapping("/product")
@CrossOrigin
public class ProductController {

    @Resource
    private ProductService productService;

    @GetMapping("/export/{keyword}")
    public CommonResult export(@PathVariable String keyword){
        return productService.export(keyword);
    }

    @GetMapping("/search/{keyword}/{currentPage}/{pageSize}")
    public CommonResult search(@PathVariable String keyword,@PathVariable Integer currentPage,@PathVariable Integer pageSize){
        return productService.search(keyword,currentPage,pageSize);
    }
}

service:

package com.wzh.service;

import com.wzh.vo.CommonResult;

/**
 * @ProjectName: jd-ES
 * @Package: com.wzh.service
 * @ClassName: ProductService
 * @Author: 王振华
 * @Description:
 * @Date: 2022/8/17 10:52
 * @Version: 1.0
 */
public interface ProductService {
    CommonResult export(String keyword);

    CommonResult search(String keyword,Integer currentPage,Integer pageSize);
}

实现类:

package com.wzh.service.impl;

import com.alibaba.fastjson.JSON;
import com.wzh.entity.Product;
import com.wzh.service.ProductService;
import com.wzh.util.HtmlparseUtil;
import com.wzh.vo.CommonResult;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.MatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.PathVariable;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @ProjectName: jd-ES
 * @Package: com.wzh.service.impl
 * @ClassName: ProductServiceImpl
 * @Author: 王振华
 * @Description:
 * @Date: 2022/8/17 10:53
 * @Version: 1.0
 */
@Service
public class ProductServiceImpl implements ProductService {
    @Autowired
    private RestHighLevelClient restHighLevelClient;
    @Override
    public CommonResult export(String keyword) {
        try {
            List<Product> products = HtmlparseUtil.parseJd(keyword);

            BulkRequest bulkRequest = new BulkRequest("qy151-index");
            /*for (Product product:products){
                IndexRequest indexRequest=new IndexRequest();
                indexRequest.source(JSON.toJSONString(product), XContentType.JSON);
                bulkRequest.add(indexRequest);
            }*/
            products.stream().forEach(item -> bulkRequest.add(new IndexRequest().source(JSON.toJSONString(item), XContentType.JSON)));
            //批量添加文档
            BulkResponse bulkResponse = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
            if(!bulkResponse.hasFailures()){
                return new CommonResult(2000,"批量添加成功",null);
            }else{
                return new CommonResult(5000,"批量添加失败",null);
            }
        }catch (Exception e){
            e.printStackTrace();
            return new CommonResult(5000,"批量添加失败",null);
        }
    }

    /**
     * 根据keyword查询
     * @param keyword
     * @return
     */
    @Override
    public CommonResult search(String keyword,Integer currentPage,Integer pageSize) {
        try {
            //创建查询请求对象
            SearchRequest searchRequest = new SearchRequest("qy151-index");
            //创建一个条件对象
            SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
            //匹配查询
            TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("title", keyword);
            //条件投入
            sourceBuilder.query(termQueryBuilder);

            //ES默认分页10
            sourceBuilder.from((currentPage-1)*pageSize);
            sourceBuilder.size(pageSize);

            //设置高亮
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            highlightBuilder.field("title");
            highlightBuilder.preTags("<font color='red'>");
            highlightBuilder.postTags("</font>");
            sourceBuilder.highlighter(highlightBuilder);

            //把条件对象放入搜索请求对象中
            searchRequest.source(sourceBuilder);
            //执行搜索功能
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

            SearchHits hits = searchResponse.getHits();

            long total = hits.getTotalHits().value;
            List<Map<String, Object>> result = new ArrayList<>();
            SearchHit[] hits1 = hits.getHits();
            for (SearchHit hit : hits1) {
                Map<String, Object> map = hit.getSourceAsMap();

                //获取高亮字段
                Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                HighlightField title = highlightFields.get("title");
                Text[] fragments = title.getFragments();
                StringBuilder sb = new StringBuilder();
                for (Text text : fragments) {
                    sb.append(text);
                }
                map.put("title", sb);
                result.add(map);
            }
            Map<String,Object> map = new HashMap<>();
            map.put("total",total);
            map.put("result",result);
            return new CommonResult(2000,"搜索成功",map);
        }catch (Exception e){
            e.printStackTrace();
            return new CommonResult(5000,"搜索失败",null);
        }
    }
}

commonResult:

package com.wzh.vo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @ProjectName: jd-ES
 * @Package: com.wzh.vo
 * @ClassName: CommentResult
 * @Author: 王振华
 * @Description:
 * @Date: 2022/8/17 10:45
 * @Version: 1.0
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CommonResult {
    private Integer code;
    private String msg;
    private Object result;
}

7.7 前端

<template>
    <div class="page">
        <div id="app" class=" mallist tmall- page-not-market ">
            <!-- 头部搜索 -->
            <div id="header" class=" header-list-app">
                <div class="headerLayout">
                    <div class="headerCon ">
                        <!-- Logo-->
                        <h1 id="mallLogo">
                            <img src="../assets/jd.png" alt="">
                        </h1>
                        <div class="header-extra">
                            <!--搜索-->
                            <div id="mallSearch" class="mall-search">
                                <form name="searchTop" class="mallSearch-form clearfix">
                                    <fieldset>
                                        <legend>天猫搜索</legend>
                                        <div class="mallSearch-input clearfix">
                                            <div class="s-combobox" id="s-combobox-685">
                                                <div class="s-combobox-input-wrap">
                                                    <input v-model="keyword"  type="text" autocomplete="off" id="mq"
                                                           class="s-combobox-input"  aria-haspopup="true">
                                                </div>
                                            </div>
                                            <button type="submit" @click.prevent="searchKey" id="searchbtn">搜索</button>
                                        </div>
                                    </fieldset>
                                </form>
                                <ul class="relKeyTop">
                                    <li><a>老闫说Java</a></li>
                                    <li><a>老闫说前端</a></li>
                                    <li><a>老闫说Linux</a></li>
                                    <li><a>老闫说大数据</a></li>
                                    <li><a>老闫聊理财</a></li>
                                </ul>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            <!-- 商品详情页面 -->
            <div id="content">
                <div class="main">
                    <!-- 品牌分类 -->
                    <form class="navAttrsForm">
                        <div class="attrs j_NavAttrs" style="display:block">
                            <div class="brandAttr j_nav_brand">
                                <div class="j_Brand attr">
                                    <div class="attrKey">
                                        品牌
                                    </div>
                                    <div class="attrValues">
                                        <ul class="av-collapse row-2">
                                            <li><a href="#"> 老闫说 </a></li>
                                            <li><a href="#"> Java </a></li>
                                        </ul>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </form>
                    <!-- 排序规则 -->
                    <div class="filter clearfix">
                        <a class="fSort fSort-cur">综合<i class="f-ico-arrow-d"></i></a>
                        <a class="fSort">人气<i class="f-ico-arrow-d"></i></a>
                        <a class="fSort">新品<i class="f-ico-arrow-d"></i></a>
                        <a class="fSort">销量<i class="f-ico-arrow-d"></i></a>
                        <a class="fSort">价格<i class="f-ico-triangle-mt"></i><i class="f-ico-triangle-mb"></i></a>
                    </div>
                    <!-- 商品详情 -->
                    <div class="view grid-nosku" >
                        <div class="product" v-for="item in results">
                            <div class="product-iWrap">
                                <!--商品封面-->
                                <div class="productImg-wrap">
                                    <a class="productImg">
                                        <img :src="item.imgUrl">
                                    </a>
                                </div>
                                <!--价格-->
                                <p class="productPrice">
                                    <em>{{item.price}}</em>
                                </p>
                                <!--标题-->
                                <p class="productTitle">
                                    <a v-html="item.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>
                    </div>
                    <el-pagination
                            @size-change="handleSizeChange"
                            @current-change="handleCurrentChange"
                            :current-page="currentPage"
                            :page-sizes="pageSizes"
                            :page-size="pageSize"
                            layout="total, sizes, prev, pager, next, jumper"
                            :total="total">
                    </el-pagination>
                </div>
            </div>
        </div>
    </div>
</template>

<script>
    export default {
        name: "jd",
        data(){
             return {
                  keyword: '', // 搜索的关键字
                  results:[], // 后端返回的结果
                  currentPage:1,
                  pageSizes:[10,20,30,40],
                  pageSize:10,
                  total:0,
             }
        },
        methods:{
            searchKey(){
                var keyword = this.keyword;
                this.$http.get('/product/search/'+keyword+"/"+this.currentPage+"/"+this.pageSize).then(response=>{
                    //console.log(response.data.result);
                    this.results=response.data.result.result;
                    this.total=response.data.result.total;
                })
            },
            handleSizeChange(val) {
                this.pageSize = val;
                this.searchKey();
            },
            handleCurrentChange(val) {
                this.currentPage=val;
                this.searchKey();
            }
        }

    }
</script>

<style>
    /*** uncss> filename: http://localhost:9090/css/global.css ***/body,button,fieldset,form,h1,input,legend,li,p,ul{margin:0;padding:0}body,button,input{font:12px/1.5 tahoma,arial,"\5b8b\4f53";-ms-overflow-style:scrollbar}button,h1,input{font-size:100%}em{font-style:normal}ul{list-style:none}a{text-decoration:none}a:hover{text-decoration:underline}legend{color:#000}fieldset,img{border:0}#content,#header{margin-left:auto;margin-right:auto}html{zoom:expression(function(ele){ ele.style.zoom = "1"; document.execCommand("BackgroundImageCache", false, true); }(this))}@font-face{font-family:mui-global-iconfont;src:url(//at.alicdn.com/t/font_1401963178_8135476.eot);src:url(//at.alicdn.com/t/font_1401963178_8135476.eot?#iefix) format('embedded-opentype'),url(//at.alicdn.com/t/font_1401963178_8135476.woff) format('woff'),url(//at.alicdn.com/t/font_1401963178_8135476.ttf) format('truetype'),url(//at.alicdn.com/t/font_1401963178_8135476.svg#iconfont) format('svg')}#mallPage{width:auto;min-width:990px;background-color:transparent}#content{width:990px;margin:auto}#mallLogo{float:left;z-index:9;padding-top:28px;width:280px;height:64px;line-height:64px;position:relative}.page-not-market #mallLogo{width:400px}.clearfix:after,.clearfix:before,.headerCon:after,.headerCon:before{display:table;content:"";overflow:hidden}#mallSearch legend{display:none}.clearfix:after,.headerCon:after{clear:both}.clearfix,.headerCon{zoom:1}#mallPage #header{margin-top:-30px;width:auto;margin-bottom:0;min-width:990px;background:#fff}#header{height:122px;margin-top:-26px!important;background:#fff;min-width:990px;width:auto!important;position:relative;z-index:1000}#mallSearch #mq,#mallSearch fieldset,.mallSearch-input{position:relative}.headerLayout{width:990px;padding-top:26px;margin:0 auto}.header-extra{overflow:hidden}#mallSearch{float:right;padding-top:25px;width:390px;overflow:hidden}.mallSearch-form{border:solid #FF0036;border-width:3px 0 3px 3px}.mallSearch-input{background:#fff;height:30px}#mallSearch #mq{color:#000;margin:0;z-index:2;width:289px;height:20px;line-height:20px;padding:5px 3px 5px 5px;outline:0;border:none;font-weight:900;background:url(data:image/gif;base64,R0lGODlhAQADAJEAAObm5t3d3ff39wAAACH5BAAAAAAALAAAAAABAAMAAAICDFQAOw==) repeat-x;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}#mallSearch button{position:absolute;right:0;top:0;width:90px;border:0;font-size:16px;letter-spacing:4px;cursor:pointer;color:#fff;background-color:#FF0036;height:30px;overflow:hidden;font-family:'\5FAE\8F6F\96C5\9ED1',arial,"\5b8b\4f53"}#mallSearch .s-combobox{height:30px}#mallSearch .s-combobox .s-combobox-input:focus{outline:0}button::-moz-focus-inner{border:0;padding:0;margin:0}.page-not-market #mallSearch{width:540px!important}.page-not-market #mq{width:439px!important;float: left;}
    /*** uncss> filename: http://localhost:9090/css/test.css ***/#mallSearch{float:none}.page-not-market #mallLogo{width:280px}.header-list-app #mallSearch{width:448px!important}.header-list-app #mq{width:347px!important}@media (min-width:1210px){#header .headerCon,#header .headerLayout,.main{width:1190px!important}.header-list-app #mallSearch{width:597px!important}.header-list-app #mq{width:496px!important}}@media (min-width:600px) and (max-width:800px) and (orientation:portrait){.pg .page{min-width:inherit!important}.pg #mallPage,.pg #mallPage #header{min-width:740px!important}.pg #header .headerCon,.pg #header .headerLayout,.pg .main{width:740px!important}.pg #mallPage #mallLogo{width:260px}.pg #header{min-width:inherit}.pg #mallSearch .mallSearch-input{padding-right:95px}.pg #mallSearch .s-combobox{width:100%!important}.pg #mallPage .header-list-app #mallSearch{width:auto!important}.pg #mallPage .header-list-app #mallSearch #mq{width:100%!important;padding:5px 0 5px 5px}}i{font-style:normal}.main,.page{position:relative}.page{overflow:hidden}@font-face{font-family:tm-list-font;src:url(//at.alicdn.com/t/font_1442456441_338337.eot);src:url(//at.alicdn.com/t/font_1442456441_338337.eot?#iefix) format('embedded-opentype'),url(//at.alicdn.com/t/font_1442456441_338337.woff) format('woff'),url(//at.alicdn.com/t/font_1442456441_338337.ttf) format('truetype'),url(//at.alicdn.com/t/font_1442456441_338337.svg#iconfont) format('svg')}::selection{background:rgba(0,0,0,.1)}*{-webkit-tap-highlight-color:rgba(0,0,0,.3)}b{font-weight:400}.page{background:#fff;min-width:990px}#content{margin:0!important;width:100%!important}.main{margin:auto;width:990px}.main img{-ms-interpolation-mode:bicubic}.fSort i{background:url(//img.alicdn.com/tfs/TB1XClLeAY2gK0jSZFgXXc5OFXa-165-206.png) 9999px 9999px no-repeat}#mallSearch .s-combobox{width:auto}::-ms-clear,::-ms-reveal{display:none}.attrKey{white-space:nowrap;text-overflow:ellipsis}.attrs{border-top:1px solid #E6E2E1}.attrs a{outline:0}.attr{background-color:#F7F5F5;border-color:#E6E2E1 #E6E2E1 #D1CCC7;border-style:solid solid dotted;border-width:0 1px 1px}.attr ul:after,.attr:after{display:block;clear:both;height:0;content:' '}.attrKey{float:left;padding:7px 0 0;width:10%;color:#B0A59F;text-indent:13px}.attrKey{display:block;height:16px;line-height:16px;overflow:hidden}.attrValues{position:relative;float:left;background-color:#FFF;width:90%;padding:4px 0 0;overflow:hidden}.attrValues ul{position:relative;margin-right:105px;margin-left:25px}.attrValues ul.av-collapse{overflow:hidden}.attrValues li{float:left;height:22px;line-height:22px}.attrValues li a{position:relative;color:#806F66;display:inline-block;padding:1px 20px 1px 4px;line-height:20px;height:20px;white-space:nowrap}.attrValues li a:hover{color:#ff0036;text-decoration:none}.brandAttr .attr{border:2px solid #D1CCC7;margin-top:-1px}.brandAttr .attrKey{padding-top:9px}.brandAttr .attrValues{padding-top:6px}.brandAttr .av-collapse{overflow:hidden;max-height:60px}.brandAttr li{margin:0 8px 8px 0}.brandAttr li a{text-overflow:ellipsis;overflow:hidden}.navAttrsForm{position:relative}.relKeyTop{padding:4px 0 0;margin-left:-13px;height:16px;overflow:hidden;width:100%}.relKeyTop li{display:inline-block;border-left:1px solid #ccc;line-height:1.1;padding:0 12px}.relKeyTop li a{color:#999}.relKeyTop li a:hover{color:#ff0036;text-decoration:none}.filter i{display:inline-block;overflow:hidden}.filter{margin:10px 0;padding:5px;position:relative;z-index:10;background:#faf9f9;color:#806f66}.filter i{position:absolute}.filter a{color:#806f66;cursor:pointer}.filter a:hover{color:#ff0036;text-decoration:none}.fSort{float:left;height:22px;line-height:20px;line-height:24px\9;border:1px solid #ccc;background-color:#fff;z-index:10}.fSort{position:relative}.fSort{display:inline-block;margin-left:-1px;overflow:hidden;padding:0 15px 0 5px}.fSort:hover,a.fSort-cur{color:#ff0036;background:#F1EDEC}.fSort i{top:6px;right:5px;width:7px;height:10px;line-height:10px}.fSort .f-ico-arrow-d{background-position:-22px -23px}.fSort-cur .f-ico-arrow-d,.fSort:hover .f-ico-arrow-d{background-position:-30px -23px}i.f-ico-triangle-mb,i.f-ico-triangle-mt{border:4px solid transparent;height:0;width:0}i.f-ico-triangle-mt{border-bottom:4px solid #806F66;top:2px}i.f-ico-triangle-mb{border-top:4px solid #806F66;border-width:3px\9;right:6px\9;top:12px}:root i.f-ico-triangle-mb{border-width:4px\9;right:5px\9}i.f-ico-triangle-mb,i.f-ico-triangle-mt{border:4px solid transparent;height:0;width:0}i.f-ico-triangle-mt{border-bottom:4px solid #806F66;top:2px}i.f-ico-triangle-mb{border-top:4px solid #806F66;border-width:3px\9;right:6px\9;top:12px}:root i.f-ico-triangle-mb{border-width:4px\9;right:5px\9}.view:after{clear:both;content:' '}.productImg,.productPrice em b{vertical-align:middle}.product{position:relative;float:left;padding:0;margin:0 0 20px;line-height:1.5;overflow:visible;z-index:1}.product:hover{overflow:visible;z-index:3;background:#fff}.product-iWrap{position:absolute;background-color:#fff;margin:0;padding:4px 4px 0;font-size:0;border:1px solid #f5f5f5;border-radius:3px}.product-iWrap *{font-size:12px}.product:hover .product-iWrap{height:auto;margin:-3px;border:4px solid #ff0036;border-radius:0;-webkit-transition:border-color .2s ease-in;-moz-transition:border-color .2s ease-in;-ms-transition:border-color .2s ease-in;-o-transition:border-color .2s ease-in;transition:border-color .2s ease-in}.productPrice,.productShop,.productStatus,.productTitle{display:block;overflow:hidden;margin-bottom:3px}.view:after{display:block}.view{margin-top:10px}.view:after{height:0}.productImg-wrap{display:table;table-layout:fixed;height:210px;width:100%;padding:0;margin:0 0 5px}.productImg-wrap a,.productImg-wrap img{max-width:100%;max-height:210px}.productImg{display:table-cell;width:100%;text-align:center}.productImg img{display:block;margin:0 auto}.productPrice{font-family:arial,verdana,sans-serif!important;color:#ff0036;font-size:14px;height:30px;line-height:30px;margin:0 0 5px;letter-spacing:normal;overflow:inherit!important;white-space:nowrap}.productPrice *{height:30px}.productPrice em{float:left;font-family:arial;font-weight:400;font-size:20px;color:#ff0036}.productPrice em b{margin-right:2px;font-weight:700;font-size:14px}.productTitle{display:block;color:#666;height:14px;line-height:12px;margin-bottom:3px;word-break:break-all;font-size:0;position:relative}.productTitle *{font-size:12px;font-family:\5FAE\8F6F\96C5\9ED1;line-height:14px}.productTitle a{color:#333}.productTitle a:hover{color:#ff0036!important}.productTitle a:visited{color:#551A8B!important}.product:hover .productTitle{height:14px}.productShop{position:relative;height:22px;line-height:20px;margin-bottom:5px;color:#999;white-space:nowrap;overflow:visible}.productStatus{position:relative;height:32px;border:none;border-top:1px solid #eee;margin-bottom:0;color:#999}.productStatus span{float:left;display:inline-block;border-right:1px solid #eee;width:39%;padding:10px 1px;margin-right:6px;line-height:12px;text-align:left;white-space:nowrap}.productStatus a,.productStatus em{margin-top:-8px;font-family:arial;font-size:12px;font-weight:700}.productStatus em{color:#b57c5b}.productStatus a{color:#38b}.productImg-wrap{position:relative}.product-iWrap{min-height:98%;width:210px}.view{padding-left:5px;padding-right:5px}.view{width:1023px}.view .product{width:220px;margin-right:33px}@media (min-width:1210px){.view{width:1210px;padding-left:5px;padding-right:5px}.view .product{width:220px;margin-right:20px}}@media (min-width:600px) and (max-width:800px) and (orientation:portrait){.view{width:775px;padding-left:5px;padding-right:5px}.view .product{width:220px;margin-right:35px}}.product{height:372px}.grid-nosku .product{height:333px}

</style>

 

这里没有ES配置文件,因为有默认值

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值