1. 程式人生 > 實用技巧 >springboot2.0 整合elasticsearch 實現分頁搜尋

springboot2.0 整合elasticsearch 實現分頁搜尋

最近的專案中有使用到el做站內分詞搜尋,簡單描述下業務邏輯:

首先專案是類似於部落格的一個系統,在釋出文章的時候有全公開,只針對某個部門公開 還有私密的;

分析 在使用者沒有登入的時候只能 搜尋到全部公開符合搜尋框條件的文章,登入之後 可以搜尋到自己釋出私密的和針對自己所在部門釋出的或者是全部公開的文章

我使用的是 es springdata包

pom依賴

 <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-elasticsearch</
artifactId> </dependency>

建立一個文件對應的實體類

package com.cmbchina.ccd.itpm.search.entity;

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.FieldType;

import java.util.Date; /** * @Author zly * @Date 2019/11/5 14:40 */ @Document(indexName = "asset_article", type = "article") public class Article { @Id private String id; @Field(index = true, type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_max_word") private String title; @Field(index
= true, type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_max_word") private String digest; @Field(index = true, type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_max_word") private String comment; private Date issueTime; private String cover; private String isPublic; private Integer pubStatus; private Integer visit; private String authorId; public String getAuthorId() { return authorId; } public void setAuthorId(String authorId) { this.authorId = authorId; } public Integer getVisit() { return visit; } public void setVisit(Integer visit) { this.visit = visit; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getDigest() { return digest; } public void setDigest(String digest) { this.digest = digest; } public String getComment() { return comment; } public void setComment(String comment) { this.comment = comment; } public Date getIssueTime() { return issueTime; } public void setIssueTime(Date issueTime) { this.issueTime = issueTime; } public String getCover() { return cover; } public void setCover(String cover) { this.cover = cover; } public String getIsPublic() { return isPublic; } public void setIsPublic(String isPublic) { this.isPublic = isPublic; } public Integer getPubStatus() { return pubStatus; } public void setPubStatus(Integer pubStatus) { this.pubStatus = pubStatus; } @Override public String toString() { return "Article{" + "id='" + id + '\'' + ", title='" + title + '\'' + ", digest='" + digest + '\'' + ", comment='" + comment + '\'' + ", issueTime=" + issueTime + ", cover='" + cover + '\'' + ", isPublic='" + isPublic + '\'' + ", pubStatus=" + pubStatus + ", visit=" + visit + ", authorId='" + authorId + '\'' + '}'; } }

編寫一個Dao層

package com.cmbchina.ccd.itpm.search.dao;

import com.cmbchina.ccd.itpm.search.entity.Article;
import org.elasticsearch.index.query.QueryBuilder;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.repository.ElasticsearchCrudRepository;

/**
 * @Author zly
 * @Date 2019/11/5 15:55
 */
public interface ArticleDao extends ElasticsearchCrudRepository<Article, String> {

    Page<Article> findByIsPublicLikeOrIsPublicIsAndPubStatusIsAndTitleOrCommentOrDigestLike(String departName,
                                                                                            String isPublic,
                                                                                            String pubStatus,
                                                                                            String title,
                                                                                            String comment,
                                                                                            String digest,
                                                                                            Pageable pageable);
    Page<Article> search(QueryBuilder query, Pageable pageable);
}

  ps:上面一個使用JPA傳統的命名方式查詢方法實現的簡單的查詢

後面主要記錄的是第二個方法使用

邏輯實現層程式碼:

package com.cmbchina.ccd.itpm.search.service;

import com.cmbchina.ccd.itpm.label.entity.User;
import com.cmbchina.ccd.itpm.label.utils.SwitchObjectUtil;
import com.cmbchina.ccd.itpm.search.dao.ArticleDao;
import com.cmbchina.ccd.itpm.search.dto.ArticleDto;
import com.cmbchina.ccd.itpm.search.entity.Article;
import com.cmbchina.ccd.itpm.search.entity.Label;
import com.cmbchina.ccd.itpm.search.mapper.LabelMapper;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.elasticsearch.action.search.SearchRequest;
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.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

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

/**
 * @Author zly
 * @Date 2019/11/5 16:43
 */
@Service
@SuppressWarnings("all")
public class ArticleService {
    private Logger logger = LoggerFactory.getLogger(ArticleService.class);
    @Autowired
    private ArticleDao articleDao;
    @Autowired
    private LabelMapper labelMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    @Value("${asset.uploadPath}")
    private String filePath;
    @Value("${asset.local}")
    private String local;
    private ObjectMapper mapper = new ObjectMapper();

    public Map findByTitleLike(String keywords, int page, int size) throws Exception {
        User user = SwitchObjectUtil.getUser(redisTemplate.opsForValue().get("user"));
        Map<String, Object> map = new HashMap<>();
        SearchSourceBuilder builder = new SearchSourceBuilder();
        SearchRequest searchRequest = new SearchRequest("asset_article");
        Pageable pageable = PageRequest.of(page - 1, size);
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        Page<Article> articlePage = null;
        List<ArticleDto> articleDtoList = null;
        try {
            if (user != null) {
                //SELECT * FROM asset_article WHERE       (isPublic LIKE "%應用開發一室%" or  isPublic="1")
                // AND pubStatus="1"
                // and (`comment` like "%測試%" or title like "%測試%" OR digest like "%測試%")
     //OR ( isPublic = "0" AND authorId="66666")
boolQuery .should(QueryBuilders.boolQuery().should(QueryBuilders.matchQuery("isPublic", user.getDeptName())) .should(QueryBuilders.termQuery("isPublic", "1"))) .must(QueryBuilders.termQuery("pubStatus", "1")) .must(QueryBuilders.boolQuery().should(QueryBuilders.matchQuery("comment", keywords)) .should(QueryBuilders.matchQuery("digest", keywords)) .should(QueryBuilders.matchQuery("title", keywords))) .should(QueryBuilders.boolQuery().must(QueryBuilders.termQuery("isPublic", "0")) .must(QueryBuilders.termQuery("authorId", user.getEmployeeId()))); } else { //SELECT * FROM asset_article WHERE (isPublic="1") // AND pubStatus="1" // and (`comment` like "%測試%" or title like "%測試%" OR digest like "%測試%") BoolQueryBuilder must = boolQuery.must(QueryBuilders.termQuery("isPublic", "1")) .must(QueryBuilders.termQuery("pubStatus", "1")) .must(QueryBuilders.boolQuery().should(QueryBuilders.matchQuery("comment", keywords)) .should(QueryBuilders.matchQuery("digest", keywords)) .should(QueryBuilders.matchQuery("title", keywords))); } articlePage = articleDao.search(boolQuery, pageable); articleDtoList = new ArrayList<>(); for (Article article : articlePage.getContent()) { ArticleDto articleDto = new ArticleDto(); articleDto.setId(article.getId()); articleDto.setComment(article.getComment()); articleDto.setCover(local + article.getCover()); articleDto.setDigest(article.getDigest()); articleDto.setIsPublic(article.getIsPublic()); articleDto.setIssueTime(article.getIssueTime()); articleDto.setTitle(article.getTitle()); articleDto.setPubStatus(article.getPubStatus()); articleDto.setVisit(article.getVisit()); List<Label> labelNameByArticle = labelMapper.getLabelNameByArticle(article.getId()); articleDto.setLabels(labelNameByArticle); articleDtoList.add(articleDto); } } catch (BeansException e) { map.put("code", 500); map.put("message", "獲取資料異常"); logger.error("全文搜尋資料異常"); return map; } map.put("code", 200); map.put("total", articlePage.getTotalElements()); map.put("message", "SUCCESS"); map.put("data", articleDtoList); return map; } public int deleteArticleById(String id) { try { articleDao.deleteById(id); return 1; } catch (Exception e) { logger.error("搜尋服務根據ID刪除文章失敗{}", e.getMessage()); return 0; } } }

說明一下

SELECT * FROM asset_article WHERE       (isPublic LIKE "%應用開發一室%" or  isPublic="1")
               AND pubStatus="1"
                 and (`comment` like "%測試%" or title like "%測試%" OR digest like "%測試%")
     OR ( isPublic = "0" AND authorId="66666")

es 整合java程式碼實現

 boolQuery
                        .should(QueryBuilders.boolQuery().should(QueryBuilders.matchQuery("isPublic", user.getDeptName()))
                                .should(QueryBuilders.termQuery("isPublic", "1")))
                        .must(QueryBuilders.termQuery("pubStatus", "1"))
                        .must(QueryBuilders.boolQuery().should(QueryBuilders.matchQuery("comment", keywords))
                                .should(QueryBuilders.matchQuery("digest", keywords))
                                .should(QueryBuilders.matchQuery("title", keywords)))
                        .should(QueryBuilders.boolQuery().must(QueryBuilders.termQuery("isPublic", "0"))
                                .must(QueryBuilders.termQuery("authorId", user.getEmployeeId())));

兩種邏輯都在註釋寫了sql語句作為對比

不久會更新說一下排序的效果