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语句作为对比
不久会更新说一下排序的效果
努力提高自己的技术,不忘初心