springboot集成elasticsearch

Posted suruozhong

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了springboot集成elasticsearch相关的知识,希望对你有一定的参考价值。

spring cloud,其实一样的,我单独加一个模块做搜索

添加pom

<!--elasticsearch-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
        </dependency>
        <dependency>
            <groupId>io.searchbox</groupId>
            <artifactId>jest</artifactId>
            <version>5.3.3</version>
        </dependency>
        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>x-pack-transport</artifactId>
            <version>5.5.0</version>
        </dependency>

完整的pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>zhong</artifactId>
        <groupId>com.zh</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>zhong-search</artifactId>
    <packaging>jar</packaging>
    <properties>
        <elasticsearch.version>5.6.10</elasticsearch.version>
    </properties>
    <dependencies>
        <!--公共模块-->
        <dependency>
            <groupId>com.zh</groupId>
            <artifactId>zhong-common-api</artifactId>
            <version>${zhong.version}</version>
        </dependency>
        <dependency>
            <groupId>com.zh</groupId>
            <artifactId>zhong-common-core</artifactId>
            <version>${zhong.version}</version>
        </dependency>
        <!--配置中心客户端-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <!--web 模块-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <!--排除tomcat依赖-->
                <exclusion>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                    <groupId>org.springframework.boot</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <!--undertow容器-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-undertow</artifactId>
        </dependency>
        <!--elasticsearch-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
        </dependency>
        <dependency>
            <groupId>io.searchbox</groupId>
            <artifactId>jest</artifactId>
            <version>5.3.3</version>
        </dependency>
        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>x-pack-transport</artifactId>
            <version>5.5.0</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

bootstrap.yml配置

spring:
   data:
      elasticsearch:
         cluster‐name: my-application
         cluster‐nodes: 192.168.1.107:9300

添加entity实体

package com.zh.search.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.*;

import java.time.LocalDateTime;
import java.util.Date;

/**
 * @Auther: suruozhong
 * @Date: 2019/11/14 17:52
 * @Description:
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@Document(indexName = "search-entity",type = "search", shards = 1, replicas = 0)
@Setting(settingPath = "/search-setting.json")  //自动创建索引
@Mapping(mappingPath = "/search-mapping.json")
public class Search {

    /**
     * @Id注解必须是springframework包下的
     */
    @Id
    private String id; //id是model的id拼接模块module
    private String title; //标题
    private String content;// 内容
    private String model; // 对象
    private String module; // 模块
    private String createTime;

}

添加索引的设置,索引创建,分词,索引映射

在resource下添加两个文件

search-mapping.json

{
  "search": {
    "_all": {
      "enabled": true
    },
    "properties": {
      "id": {
        "type": "text"
      },
      "title": {
        "type": "text",
        "analyzer": "ikSearchAnalyzer",
        "search_analyzer": "ikSearchAnalyzer",
        "fields": {
          "pinyin": {
            "type": "text",
            "analyzer": "pinyinSimpleIndexAnalyzer",
            "search_analyzer": "pinyinSimpleIndexAnalyzer"
          }
        }
      },
      "content": {
        "type": "text",
        "analyzer": "ikSearchAnalyzer",
        "search_analyzer": "ikSearchAnalyzer",
        "fields": {
          "pinyin": {
            "type": "text",
            "analyzer": "pinyinSimpleIndexAnalyzer",
            "search_analyzer": "pinyinSimpleIndexAnalyzer"
          }
        }
      },
      "model": {
        "type": "text"
      },
      "module": {
        "type": "text"
      },
      "createTime": {
        "type": "date",
        "format": "yyyy-MM-dd HH:mm:ss"
      }
    }
  }
}

 

search-setting.json

{
  "index": {
    "analysis": {
      "filter": {
        "edge_ngram_filter": {
          "type": "edge_ngram",
          "min_gram": 1,
          "max_gram": 50
        },
        "pinyin_simple_filter": {
          "type": "pinyin",
          "first_letter": "prefix",
          "padding_char": " ",
          "limit_first_letter_length": 50,
          "lowercase": true
        }
      },
      "char_filter": {
        "tsconvert": {
          "type": "stconvert",
          "convert_type": "t2s"
        }
      },
      "analyzer": {
        "ikSearchAnalyzer": {
          "type": "custom",
          "tokenizer": "ik_max_word",
          "char_filter": [
            "tsconvert"
          ]
        },
        "pinyinSimpleIndexAnalyzer": {
          "tokenizer": "keyword",
          "filter": [
            "pinyin_simple_filter",
            "edge_ngram_filter",
            "lowercase"
          ]
        }
      }
    }
  }
}

 

添加dao

package com.zh.search.dao;

import com.zh.search.entity.Search;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;

/**
 * @Auther: suruozhong
 * @Date: 2019/11/14 16:41
 * @Description:
 */
public interface SearchRepository extends ElasticsearchRepository<Search,String> {
}

 

代码实例

package com.zh.search.service.impl;

import com.zh.common.core.kit.DateKit;
import com.zh.common.core.kit.RandomKit;
import com.zh.common.core.util.R;
import com.zh.search.dao.SearchRepository;
import com.zh.search.entity.Search;
import com.zh.search.service.SearchService;
import org.elasticsearch.action.admin.indices.analyze.AnalyzeAction;
import org.elasticsearch.action.admin.indices.analyze.AnalyzeRequest;
import org.elasticsearch.action.admin.indices.analyze.AnalyzeRequestBuilder;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder.Field;
import org.elasticsearch.action.admin.indices.analyze.AnalyzeResponse;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.DisMaxQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
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.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import javax.annotation.Resource;
import java.util.*;

/**
 * @Auther: suruozhong
 * @Date: 2019/11/14 13:59
 * @Description:
 */
@Service
public class SearchServiceImpl implements SearchService {

    @Autowired
    private SearchRepository searchRepository;
    @Resource
    private ElasticsearchTemplate elasticsearchTemplate;

    /**
     * 添加数据
     * @param title
     */
    @Override
    public void saveM(String title){
        Search model = new Search();
        model.setId(RandomKit.getDateNumber());
        model.setTitle(title);
        model.setContent(title);
        model.setCreateTime(DateKit.format(new Date(),"yyyy-MM-dd HH:mm:ss"));
        searchRepository.save(model);
    }

    /**
     * 获取所有数据
     * @return
     */
    @Override
    public R getAll(){
        return new R(searchRepository.findAll());
    }

    /**
     * 分页分词高亮关键词查询
     * @param keyword
     * @param current
     * @param size
     * @return
     */
    @Override
    public Page page(String keyword,Integer current,Integer size){
        // 页码
        Page<Search> page = null;
        try {

            // 构建查询
            NativeSearchQueryBuilder searchQuery = new NativeSearchQueryBuilder();

            // 索引查询
            searchQuery.withIndices("search-entity");
            BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
            QueryBuilder queryBuilder = QueryBuilders.matchQuery("title", keyword).boost(2f);
            boolQueryBuilder.must(queryBuilder);
            //时间范围查询
//            boolQueryBuilder.must(QueryBuilders.rangeQuery("createTime")
//                    .from(DateKit.format(DateKit.getDayBegin(),"yyyy-MM-dd HH:mm:ss"))
//                    .to(DateKit.format(DateKit.getDayBegin(),"yyyy-MM-dd HH:mm:ss")));
            searchQuery.withQuery(boolQueryBuilder);

            // 高亮设置
            List<String> highlightFields = new ArrayList<String>();
            highlightFields.add("title");
            Field[] fields = new Field[highlightFields.size()];
            for (int x = 0; x < highlightFields.size(); x++) {
                fields[x] = new HighlightBuilder.Field(highlightFields.get(x)).preTags("<high>").postTags("</high>");
            }
            searchQuery.withHighlightFields(fields);

            // 分页设置
            searchQuery.withPageable(PageRequest.of(current-1, size));

            page = elasticsearchTemplate.queryForPage(searchQuery.build(), Search.class, new SearchResultMapper() {

                @Override
                public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {

                    // 获取高亮搜索数据
                    List<Search> list = new ArrayList<Search>();
                    SearchHits hits = response.getHits();
                    for (SearchHit searchHit : hits) {
                        if (hits.getHits().length <= 0) {
                            return null;
                        }
                        Search model = new Search();
                        // 公共字段
                        model.setId(searchHit.getId());
                        model.setTitle(String.valueOf(searchHit.getSourceAsMap().get("title")));
                        model.setContent(String.valueOf(searchHit.getSourceAsMap().get("content")));
                        Object createTime = searchHit.getSourceAsMap().get("createTime");


                        //高亮字段
                        if (!StringUtils.isEmpty(searchHit.getHighlightFields().get("title"))) {
                            Text[] text = searchHit.getHighlightFields().get("title").getFragments();
                            model.setTitle(text[0].toString());
                        }
                        list.add(model);
                    }

                    if (list.size() > 0) {
                        AggregatedPage<T> result = new AggregatedPageImpl<T>((List<T>) list, pageable,
                                response.getHits().getTotalHits());

                        return result;
                    }
                    return null;
                }
            });

        } catch (Exception e) {
            e.printStackTrace();
        }
        return page;
    }

    /**
     * 分词查询
     * @param title
     * @return
     */
    @Override
    public List<Search> search(String title) {
        //使用中文拼音混合搜索,取分数最高的,具体评分规则可参照:
        //  https://blog.csdn.net/paditang/article/details/79098830
        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(structureQuery(title))
                .build();
        List<Search> list = searchRepository.search(searchQuery).getContent();
        return list;
    }

    /**
     * 高亮关键词查询
     * @param title
     * @return
     */
    @Override
    public List<Search> searchHigh(String title) {
        Client client = elasticsearchTemplate.getClient();
        QueryBuilder query = structureQuery(title);
        // 加入查询中
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.preTags("<high>");//设置前缀
        highlightBuilder.postTags("</high>");//设置后缀
        highlightBuilder.field("title");//设置高亮字段
//        highlightBuilder.field("title.pinyin");//设置拼音高亮字段
        SearchResponse response = client.prepareSearch("search-entity")
                .setQuery(query).highlighter(highlightBuilder).execute().actionGet();

        // 遍历结果, 获取高亮片段
        SearchHits searchHits = response.getHits();
        Search entity = null;
        List<Search> result = new ArrayList<>();
        for (SearchHit hit : searchHits) {
            Map<String, Object> entityMap = hit.getSourceAsMap();
            entity = new Search();
            if (!StringUtils.isEmpty(hit.getHighlightFields().get("title"))) {
                Text[] text = hit.getHighlightFields().get("title").getFragments();
                entity.setTitle(text[0].toString());
            }
//            if (!StringUtils.isEmpty(hit.getHighlightFields().get("title.pinyin"))) {
//                Text[] text = hit.getHighlightFields().get("title.pinyin").getFragments();
//                entity.setTitle(text[0].toString());
//            }
            result.add(entity);
        }
        return result;
    }

    /**
     * 组成搜索条件,中文、拼音混合搜索
     *
     * @param content the content
     * @return dis max query builder
     */
    public DisMaxQueryBuilder structureQuery(String content) {
        //使用dis_max直接取多个query中,分数最高的那一个query的分数即可
        DisMaxQueryBuilder disMaxQueryBuilder = QueryBuilders.disMaxQuery();
        //boost 设置权重,只搜索匹配name
        QueryBuilder ikNameQuery = QueryBuilders.matchQuery("title", content).boost(2f);
//        QueryBuilder pinyinNameQuery = QueryBuilders.matchQuery("title.pinyin", content);
        disMaxQueryBuilder.add(ikNameQuery);
//        disMaxQueryBuilder.add(pinyinNameQuery);
        return disMaxQueryBuilder;
    }

    /**
     * 获取分词结果
     * @param keyword
     * @param type  type取值,拼音-pinyin,IK-ik_max_word,.....
     * @return
     */
    @Override
    public List analyze(String keyword,String type){

        AnalyzeRequest analyzeRequest = new AnalyzeRequest("search-entity")
                .text(keyword)
                .analyzer(Optional.ofNullable(type).orElse("ik_max_word"));
        List<AnalyzeResponse.AnalyzeToken> tokens = elasticsearchTemplate.getClient().admin().indices()
                .analyze(analyzeRequest)
                .actionGet()
                .getTokens();
        return tokens;
    }

}

 

以上是关于springboot集成elasticsearch的主要内容,如果未能解决你的问题,请参考以下文章

精通系列SpringBoot集成ElasticSearch+项目实战

精通系列SpringBoot集成ElasticSearch+项目实战

精通系列SpringBoot集成ElasticSearch+项目实战

springboot集成ElasticSearch

springboot集成elk 三:springboot + Elasticsearch Rest-Client

springboot集成elasticsearch全文搜索高亮显示实践