5.Elasticsearch学习之springboot整合

1.SpringBoot整合Elasticsearch

1.1创建工程,引入依赖

<dependency> 
    <groupId>org.springframework.boot</groupId> 
    <artifactId>spring-boot-starter-data-elasticsearch</artifactId> 
    <!--<version>2.1.5.RELEASE</version>--> 
    <version>2.4.5</version>
</dependency> 
  

网址:https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-elasticsearch/2.4.5

1.2.配置yml

spring: 
  data: 
		elasticsearch: 
      cluster-name: es6 
      cluster-nodes: 192.168.1.187:9300
      #集群的时候
      #cluster-nodes: 192.168.1.187:9300,192.168.1.188:9300,192.168.1.189:9300

1.3版本协调

目前springboot-data-elasticsearch中的es版本贴合为es-2.4.5,如此一来版本需要统一,把es进行降级。等springboot升级es版本后可以在对接最新版的7.4。

1.4 Netty issue fix

如果没有引起,暂时可不加

@Configuration 
public class ESConfig { 

    /**
    * 解决netty引起的issue 
    */ 

    @PostConstruct 
    void init() { 
    		System.setProperty("es.set.netty.runtime.available.processors", "false"); 
    } 

}

1.5elasticsearch6.4.3配置文件

elasticsearch.yml

cluster.name: es6 
node.name: node0 
path.data: /usr/local/elasticsearch-6.4.3/data 
path.logs: /usr/local/elasticsearch-6.4.3/logs 
network.host: 0.0.0.0

./elasticsearch

如果出现如下错误:

elasticsearch41

那么需要切换到root用户下去修改配置:

vim /etc/security/limits.conf
* soft nofile 65536
* hard nofile 131072
* soft nproc 2048
* hard nproc 4096
vim /etc/sysctl.conf
vm.max_map_count=262146
#刷新一下,然后重启elasticsearch
sysctl -p

注意:中文分词器也需要去配置一下,下载自己使用的elasticsearch的对应版本

ik中文分词器下载网址:https://github.com/medcl/elasticsearch-analysis-ik/releases

2.ElasticsearchTemplate 模版的使用

    @Autowired
    private ElasticsearchTemplate esTemplate;
package com.test;

import com.imooc.Application;
import com.imooc.es.pojo.Stu;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.SearchResultMapper;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;
import org.springframework.data.elasticsearch.core.query.*;
import org.springframework.test.context.junit4.SpringRunner;

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

@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class ESTest {

    @Autowired
    private ElasticsearchTemplate esTemplate;

    /**
     * 不建议使用 ElasticsearchTemplate 对索引进行管理(创建索引,更新映射,删除索引)
     * 索引就像是数据库或者数据库中的表,我们平时是不会是通过java代码频繁的去创建修改删除数据库或者表的
     * 我们只会针对数据做CRUD的操作
     * 在es中也是同理,我们尽量使用 ElasticsearchTemplate 对文档数据做CRUD的操作
     * 1. 属性(FieldType)类型不灵活
     * 2. 主分片与副本分片数无法设置
     */

    @Test
    public void createIndexStu() {

        Stu stu = new Stu();
        stu.setStuId(1005L);
        stu.setName("iron man");
        stu.setAge(54);
        stu.setMoney(1999.8f);
        stu.setSign("I am iron man");
        stu.setDescription("I have a iron army");

        IndexQuery indexQuery = new IndexQueryBuilder().withObject(stu).build();
        esTemplate.index(indexQuery);
    }

    @Test
    public void deleteIndexStu() {
        esTemplate.deleteIndex(Stu.class);
    }

//    ------------------------- 我是分割线 --------------------------------

    @Test
    public void updateStuDoc() {

        Map<String, Object> sourceMap = new HashMap<>();
//        sourceMap.put("sign", "I am not super man");
        sourceMap.put("money", 99.8f);
//        sourceMap.put("age", 33);

        IndexRequest indexRequest = new IndexRequest();
        indexRequest.source(sourceMap);

        UpdateQuery updateQuery = new UpdateQueryBuilder()
                                        .withClass(Stu.class)
                                        .withId("1004")
                                        .withIndexRequest(indexRequest)
                                        .build();

//        update stu set sign='abc',age=33,money=88.6 where docId='1002'

        esTemplate.update(updateQuery);
    }

	//根据id查询记录
    @Test
    public void getStuDoc() {

        GetQuery query = new GetQuery();
        query.setId("1002");
        Stu stu = esTemplate.queryForObject(query, Stu.class);

        System.out.println(stu);
    }
//根据id删除对应的数据
    @Test
    public void deleteStuDoc() {
        esTemplate.delete(Stu.class, "1002");
    }


//    ------------------------- 我是分割线 --------------------------------

    @Test
    public void searchStuDoc() {
				//分页
        Pageable pageable = PageRequest.of(0, 2);

        SearchQuery query = new NativeSearchQueryBuilder()
                .withQuery(QueryBuilders.matchQuery("description", "save man"))
                .withPageable(pageable)
                .build();
        AggregatedPage<Stu> pagedStu = esTemplate.queryForPage(query, Stu.class);
        System.out.println("检索后的总分页数目为:" + pagedStu.getTotalPages());
        List<Stu> stuList = pagedStu.getContent();
        for (Stu s : stuList) {
            System.out.println(s);
        }

    }

    @Test
    public void highlightStuDoc() {

        String preTag = "<font color='red'>";
        String postTag = "</font>";

        Pageable pageable = PageRequest.of(0, 10);

        SortBuilder sortBuilder = new FieldSortBuilder("money")
                .order(SortOrder.DESC);
        SortBuilder sortBuilderAge = new FieldSortBuilder("age")
                .order(SortOrder.ASC);

        SearchQuery query = new NativeSearchQueryBuilder()
                .withQuery(QueryBuilders.matchQuery("description", "save man"))
                .withHighlightFields(new HighlightBuilder.Field("description")
                                    .preTags(preTag)
                                    .postTags(postTag))
                .withSort(sortBuilder)
                .withSort(sortBuilderAge)
                .withPageable(pageable)
                .build();
        AggregatedPage<Stu> pagedStu = esTemplate.queryForPage(query, Stu.class, new SearchResultMapper() {
            @Override
            public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {

                List<Stu> stuListHighlight = new ArrayList<>();

                SearchHits hits = response.getHits();
                for (SearchHit h : hits) {
                    HighlightField highlightField = h.getHighlightFields().get("description");
                    String description = highlightField.getFragments()[0].toString();

                    Object stuId = (Object)h.getSourceAsMap().get("stuId");
                    String name = (String)h.getSourceAsMap().get("name");
                    Integer age = (Integer)h.getSourceAsMap().get("age");
                    String sign = (String)h.getSourceAsMap().get("sign");
                    Object money = (Object)h.getSourceAsMap().get("money");

                    Stu stuHL = new Stu();
                    stuHL.setDescription(description);
                    stuHL.setStuId(Long.valueOf(stuId.toString()));
                    stuHL.setName(name);
                    stuHL.setAge(age);
                    stuHL.setSign(sign);
                    stuHL.setMoney(Float.valueOf(money.toString()));

                    stuListHighlight.add(stuHL);
                }

                if (stuListHighlight.size() > 0) {
                    return new AggregatedPageImpl<>((List<T>)stuListHighlight);
                }

                return null;
            }
        });
        System.out.println("检索后的总分页数目为:" + pagedStu.getTotalPages());
        List<Stu> stuList = pagedStu.getContent();
        for (Stu s : stuList) {
            System.out.println(s);
        }

    }
}

3.测试关键字查询

1.实体类

package com.meishi.business.model.dto;

import com.github.wxiaoqi.security.common.dto.BaseDTO;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldIndex;
import org.springframework.data.elasticsearch.annotations.FieldType;

import java.util.Date;

/**
 * @ClassName PltHelpCenterTitleDTO
 * @description:
 * @author: qsong
 * @create: 2021-03-10 10:45
 * @Version 1.0
 **/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@Document(indexName = "business", type = "doc", createIndex = false)
public class PltHelpCenterSearchDTO {
    private static final long serialVersionUID = 1L;
    /**
     * id
     */
    @Id
    @Field(store = true, type = FieldType.String, index = FieldIndex.not_analyzed)
    private String id;
    /**
     * 文档标题
     */
    @Field(store = true, type = FieldType.String, index = FieldIndex.analyzed,analyzer ="ik_max_word")
    private String title;
    /**
     * 上级目录id
     */
    @Field(store = true, type = FieldType.String, index = FieldIndex.not_analyzed)
    private String directoryId;
    /**
     * 上级目录名称
     */
    @Field(store = true, type = FieldType.String, index = FieldIndex.not_analyzed)
    private String directoryName;
    /**
     * 目录名称
     */
    @Field(store = true, type = FieldType.String, index = FieldIndex.not_analyzed)
    private String name;
    /**
     * 文档内容(富文本)
     */
    @Field(store = true, type = FieldType.String, index = FieldIndex.analyzed,analyzer ="ik_max_word")
    private String content;
    /**
     * 所属应用()
     */
    @Field(store = true, type = FieldType.String, index = FieldIndex.not_analyzed)
    private String application;
    /**
     * 排序
     */
    @Field(store = true, type = FieldType.Integer, index = FieldIndex.not_analyzed)
    private Integer sort;
    /**
     * 状态 0 关闭 1启动
     */
    @Field(store = true, type = FieldType.Integer, index = FieldIndex.not_analyzed)
    private Integer status;
    /**
     * 操作人ID
     */
    @Field(store = true, type = FieldType.String, index = FieldIndex.not_analyzed)
    private String operatorId;
    /**
     * 操作员姓名
     */
    @Field(store = true, type = FieldType.String, index = FieldIndex.not_analyzed)
    private String operatorName;
    /**
     * 操作人编号
     */
    @Field(store = true, type = FieldType.String, index = FieldIndex.not_analyzed)
    private String operatorCode;
    /**
     * 文本类型 0菜单 1文本   type as titleType
     */
    @Field(store = true, type = FieldType.Integer, index = FieldIndex.not_analyzed)
    private Integer titleType;
    /**
     * 创建时间-开始时间
     */
    @Field(store = true, type = FieldType.String, index = FieldIndex.not_analyzed)
    private  String startTime;
    /**
     * 创建时间-结束时间
     */
    @Field(store = true, type = FieldType.String, index = FieldIndex.not_analyzed)
    private  String endTime;
    /**
     * 顶级菜单目录iD
     */
    @Field(store = true, type = FieldType.String, index = FieldIndex.not_analyzed)
    private String topDirectoryId;

    /**
     * 乐观锁
     */
    @Field(store = true, type = FieldType.Integer, index = FieldIndex.not_analyzed)
    private Integer revision;
    /**
     * 创建人ID
     */
    @Field(store = true, type = FieldType.String, index = FieldIndex.not_analyzed)
    private String crtUserId;
    /**
     * 创建人
     */
    @Field(store = true, type = FieldType.String, index = FieldIndex.not_analyzed)
    private String crtUserName;
    /**
     * 创建时间
     */
    @Field(store = true, type = FieldType.Date, index = FieldIndex.not_analyzed)
    private Date crtTime;
    /**
     * 更新人ID
     */
    @Field(store = true, type = FieldType.String, index = FieldIndex.not_analyzed)
    private String updUserId;
    /**
     * 更新人
     */
    @Field(store = true, type = FieldType.String, index = FieldIndex.not_analyzed)
    private String updUserName;
    /**
     * 更新时间
     */
    @Field(store = true, type = FieldType.Date, index = FieldIndex.not_analyzed)
    private Date updTime;
    /**
     * 部门ID
     */
    @Field(store = true, type = FieldType.String, index = FieldIndex.not_analyzed)
    private String departId;
    /**
     * 租户ID
     */
    @Field(store = true, type = FieldType.String, index = FieldIndex.not_analyzed)
    private String tenantId;
    /**
     * 逻辑删除(0正常、1已删除)
     */
    @Field(store = true, type = FieldType.Integer, index = FieldIndex.not_analyzed)
    private Integer deleted;
    /**
     * 备注
     */
    @Field(store = true, type = FieldType.String, index = FieldIndex.not_analyzed)
    private String note;
    /**
     * 业务员工号
     */
    @Field(store = true, type = FieldType.String, index = FieldIndex.not_analyzed)
    private String crtUserNo;
    /**
     * 部门名称
     */
    @Field(store = true, type = FieldType.String, index = FieldIndex.not_analyzed)
    private String departName;

    /**
     * 视频类别
     */
    @Field(store = true, type = FieldType.String, index = FieldIndex.not_analyzed)
    private String category;
    /**
     * url
     *
     */
    @Field(store = true, type = FieldType.String, index = FieldIndex.not_analyzed)
    private String url;
    /**
     * 视频封面
     *
     */
    @Field(store = true, type = FieldType.String, index = FieldIndex.not_analyzed)
    private String cover;
    /**
     * 帮助类型 0 是文档 1是视频
     *
     */
    @Field(store = true, type = FieldType.Integer, index = FieldIndex.not_analyzed)
    private Integer helpType;

}

2.分页工具类

package com.meishi.business.utils;

import java.util.List;

/**
 * @ClassName PagedGridResult
 * @description: 用来返回分页Grid的数据格式
 * @author: qsong
 * @create: 2021-04-27 17:30
 * @Version 1.0
 **/
public class PagedGridResult {
    private int page;			// 当前页数
    private int total;			// 总页数
    private long records;		// 总记录数
    private List<?> rows;		// 每行显示的内容

    public int getPage() {
        return page;
    }
    public void setPage(int page) {
        this.page = page;
    }
    public int getTotal() {
        return total;
    }
    public void setTotal(int total) {
        this.total = total;
    }
    public long getRecords() {
        return records;
    }
    public void setRecords(long records) {
        this.records = records;
    }
    public List<?> getRows() {
        return rows;
    }
    public void setRows(List<?> rows) {
        this.rows = rows;
    }
}

3.接口请求,因为//elasticsearch是从0开始,所以分页需要减去1

    @Autowired
    private IPltHelpCenterTitleService pltHelpCenterTitleService;
    @ApiOperation("关键字查询")
    @GetMapping("/searchKeywords")
    public Object searchKeywords(@RequestParam("keywords")String keywords,@RequestParam("page")Integer page,@RequestParam("limit") Integer limit){
        if (StringUtils.isEmpty(keywords)){
            return new ActionResult<>(StatusCode.SUCCESS,"");
        }
        //elasticsearch是从0开始
        if (page == 1) {
            page = 0;
        } else {
            page = page - 1;
        }
        PagedGridResult gridResult=pltHelpCenterTitleService.searhItems(keywords.trim(), page, limit);

        return new ActionResult<>(StatusCode.SUCCESS,gridResult);
    }

4.服务接口类

 PagedGridResult searhItems(String keywords, Integer page, Integer limit);

5.服务实现类

package com.meishi.business.service.impl;

import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.meishi.business.mapper.PltHelpCenterTitleMapper;
import com.meishi.business.model.dto.PltHelpCenterSearchDTO;
import com.meishi.business.model.entity.PltHelpCenterTitle;
import com.meishi.business.service.IPltHelpCenterTitleService;
import com.meishi.business.utils.PagedGridResult;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.highlight.HighlightBuilder;
import org.elasticsearch.search.highlight.HighlightField;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.SearchResultMapper;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author qsong
 * @since 2021-03-10
 */
@Service
public class PltHelpCenterTitleServiceImpl extends ServiceImpl<PltHelpCenterTitleMapper, PltHelpCenterTitle> implements IPltHelpCenterTitleService {

    @Resource
    private ElasticsearchTemplate esTemplate;

    @Override
    public PagedGridResult searhItems(String keywords, Integer page, Integer limit) {
        //全文检索的字段名称
        StringBuffer[] keyFields =new StringBuffer[]{new StringBuffer("title"),new StringBuffer("content")};
        Pageable pageable = new PageRequest(page, limit);
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        //过滤
        //使用布尔组合查询
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //must 相当于and should相当与or must_not 取反
        HighlightBuilder.Field[] fields=new HighlightBuilder.Field[keyFields.length];
        for (int i = 0; i < keyFields.length; i++) {
            boolQueryBuilder.should(QueryBuilders.matchQuery(keyFields[i].toString(),keywords));
            HighlightBuilder.Field field = new HighlightBuilder.Field(keyFields[i].toString()).preTags("<font color='red'>").postTags("</font>");
            fields[i]=field;
        }
        //g关键字高亮
        nativeSearchQueryBuilder.withHighlightFields(fields);
        nativeSearchQueryBuilder.withQuery(boolQueryBuilder);
        nativeSearchQueryBuilder.withPageable(pageable);
        NativeSearchQuery nativeSearchQuery = nativeSearchQueryBuilder.build();
        AggregatedPage<PltHelpCenterSearchDTO> pagedItems = esTemplate.queryForPage(nativeSearchQuery, PltHelpCenterSearchDTO.class, new SearchResultMapper() {
            @Override
            public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> aClass, Pageable pageable) {
                List<Map<String, Object>> list = new ArrayList<>();
                //得到匹配的所有选项
                SearchHits hits = response.getHits();
                for (SearchHit hit : hits) {
                    if (hits.getHits().length <= 0) {
                        return null;
                    }
                    Map<String, Object> source = hit.getSource();
                    for (StringBuffer keyField : keyFields) {
                        String keyFieldValue = (String) source.get(keyField.toString());
                        HighlightField highlightField = hit.getHighlightFields().get(keyField.toString());
                        if (ObjectUtils.isEmpty(highlightField)) {
                            source.put(keyField.toString(), keyFieldValue);
                        } else {
                            String hcontent = highlightField.fragments()[0].toString();
                            source.put(keyField.toString(), hcontent);
                        }
                    }
                    list.add(source);
                }
                return new AggregatedPageImpl<>((List<T>) list, pageable, response.getHits().totalHits());
            }
        });
        PagedGridResult gridResult = new PagedGridResult();
        gridResult.setRows(pagedItems.getContent());
        gridResult.setPage(page + 1);
        gridResult.setTotal(pagedItems.getTotalPages());
        gridResult.setRecords(pagedItems.getTotalElements());
        return gridResult;
    }
}

6.编写ElasticsearchRepository接口

package com.meishi.business.repository;

import com.meishi.business.model.dto.PltHelpCenterSearchDTO;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;

/**
 * @ClassName EsHelpCenterRepository
 * @description: es帮助中心接口
 * @author: qsong
 * @create: 2021-05-06 11:15
 * @Version 1.0
 **/
public interface EsHelpCenterRepository extends ElasticsearchRepository<PltHelpCenterSearchDTO,String> {

}

继承ElasticsearchRepository接口,可以自定义查询方式或者可以实时更新或者删除ES中的数据

常用的方法:

//新增/修改
save()
  
//删除
 delete()
  
 //查询
  search()
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值