package com.nbclass.activity.service.impl; import java.io.IOException; import java.util.*; import javax.annotation.Resource; import org.apache.commons.lang3.StringUtils; import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest; import org.elasticsearch.action.admin.indices.get.GetIndexRequest; import org.elasticsearch.action.delete.DeleteRequest; import org.elasticsearch.action.delete.DeleteResponse; import org.elasticsearch.action.get.GetRequest; import org.elasticsearch.action.get.GetResponse; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.index.IndexResponse; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.support.master.AcknowledgedResponse; import org.elasticsearch.action.update.UpdateRequest; import org.elasticsearch.action.update.UpdateResponse; import org.elasticsearch.client.RequestOptions; import org.elasticsearch.client.RestHighLevelClient; import org.elasticsearch.client.indices.CreateIndexRequest; import org.elasticsearch.client.indices.CreateIndexResponse; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.query.BoolQueryBuilder; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.index.reindex.BulkByScrollResponse; import org.elasticsearch.index.reindex.DeleteByQueryRequest; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.SearchHits; import org.elasticsearch.search.builder.SearchSourceBuilder; import org.elasticsearch.search.sort.SortOrder; import org.springframework.stereotype.Service; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageInfo; import com.nbclass.activity.model.Content; import com.nbclass.activity.model.ContentTags; import com.nbclass.activity.service.ContentService; import com.nbclass.activity.service.ElasticSearchService; import com.nbclass.exception.ParameterException; import com.nbclass.exception.ServiceException; import lombok.extern.slf4j.Slf4j; /** * 案例内容ES操作 * @author Leon * @datetime 2020年6月1日 下午3:02:00 */ @Slf4j @Service public class ElasticSearchServiceImpl implements ElasticSearchService { @Resource private RestHighLevelClient restHighLevelClient; @Resource private ContentService contentService; private static final String es_index_content = "es-cases-content"; /** * 检查索引是否存在 * @throws IOException */ private boolean existIndex() throws IOException{ GetIndexRequest request = new GetIndexRequest().indices(es_index_content); @SuppressWarnings("deprecation") boolean exists = restHighLevelClient.indices().exists(request, RequestOptions.DEFAULT); return exists; } // 创建索引 public void createIndex(){ try { // 检查索引是否存在 if(existIndex())return; CreateIndexRequest indexRequest = new CreateIndexRequest(es_index_content); CreateIndexResponse indexResponse = restHighLevelClient.indices().create(indexRequest, RequestOptions.DEFAULT); log.info("创建索引, 返回值=>{}, 确认状态=>{}", indexResponse.index(), indexResponse.isAcknowledged()); }catch (Exception e) { log.info("创建索引异常", e); } } // 删除索引 public void deleteIndex(){ try { DeleteIndexRequest indexRequest = new DeleteIndexRequest(es_index_content); AcknowledgedResponse delete = restHighLevelClient.indices().delete(indexRequest, RequestOptions.DEFAULT); log.info("删除索引, 返回值=>{}, 确认状态=>{}", delete.isFragment(), delete.isAcknowledged()); }catch (Exception e) { log.info("删除索引异常", e); } } // 创建或者更新文档 @Override public void merge(Content entity) { try { if(entity!=null && entity.getTagList()!=null && entity.getTagList().size()>0) { String tags = ""; for(ContentTags tag : entity.getTagList()) { if(StringUtils.isBlank(tag.getTagName()))continue; tags += (StringUtils.isBlank(tags)?"":",")+tag.getTagName(); } entity.setTags(tags); // 将标签组到字符串里,便于搜索。 } createIndex(); // 判断索引是否存在,不存在创建索引 // 判断document是否存在,存在修改,不存在新增 GetRequest getRequest = new GetRequest(es_index_content, String.valueOf(entity.getId())); boolean exists = restHighLevelClient.exists(getRequest, RequestOptions.DEFAULT); if(!exists) { // 创建文档索引 IndexRequest indexRequest = new IndexRequest(es_index_content); indexRequest.id(String.valueOf(entity.getId())).source(JSON.toJSONString(entity), XContentType.JSON); @SuppressWarnings("unused") IndexResponse indexResponse = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT); log.info("添加文档,新建ES索引成功,id = " + entity.getId()); }else { // 修改文档索引 UpdateRequest updateRequest = new UpdateRequest(es_index_content, String.valueOf(entity.getId())); updateRequest.doc(JSON.toJSONString(entity), XContentType.JSON); @SuppressWarnings("unused") UpdateResponse updateResponse = restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT); log.info("修改文档,重建ES索引成功,id = " + entity.getId()); } } catch (IOException e) { log.error("添加文档,新建ES索引失败,id = " + entity.getId(), e); throw new ServiceException("案例更新ES索引失败"); } } @Override public void deleteDocByIds(List ids) { if(ids == null || ids.isEmpty()){ return; } try { for (Long id : ids) { // DeleteRequest DeleteRequest deleteRequest = new DeleteRequest(es_index_content, String.valueOf(id)); @SuppressWarnings("unused") DeleteResponse deleteResponse = restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT); } } catch (IOException e) { log.error("删除案例ES索引失败", e); throw new ServiceException("删除案例ES索引失败"); } } // matchQuery:会将搜索词分词,再与目标查询字段进行匹配,若分词中的任意一个词与目标字段匹配上,则可查询到。 // termQuery:不会对搜索词进行分词处理,而是作为一个整体与目标字段进行匹配,若完全匹配,则可查询到。 @Override public void deleteDocByQuery(Content entity) { /*if(entity.getType()!=null) { throw new ParameterException("案例类型不能为空"); }*/ try { DeleteByQueryRequest request = new DeleteByQueryRequest(es_index_content); request.setConflicts("proceed"); // 发生冲突即略过 //request.setQuery(QueryBuilders.termQuery("type", entity.getType())); request.setQuery(QueryBuilders.matchAllQuery()); BulkByScrollResponse bulkResponse = restHighLevelClient.deleteByQuery(request, RequestOptions.DEFAULT); TimeValue timeTaken = bulkResponse.getTook(); boolean timedOut = bulkResponse.isTimedOut(); long totalDocs = bulkResponse.getTotal(); long updatedDocs = bulkResponse.getUpdated(); long deletedDocs = bulkResponse.getDeleted(); long batches = bulkResponse.getBatches(); long noops = bulkResponse.getNoops(); long versionConflicts = bulkResponse.getVersionConflicts(); log.info("根据案例类型删除文档,花费时间:" + timeTaken + ",是否超时:" + timedOut + ",总文档数:" + totalDocs + ",更新数:" + updatedDocs + ",删除数:" + deletedDocs + ",批量次数:" + batches + ",跳过数:" + noops + ",冲突数:" + versionConflicts); } catch (IOException e) { log.error("根据案例类型删除文档异常", e); } } @Override public boolean existDoc(Long id){ boolean exists = false; GetRequest getRequest = new GetRequest(es_index_content, String.valueOf(id)); try { exists = restHighLevelClient.exists(getRequest, RequestOptions.DEFAULT); } catch (IOException e) { e.printStackTrace(); } return exists; } // 获取文档 @Override public Content findById(Long id) { // GetRequest GetRequest getRequest = new GetRequest(es_index_content, String.valueOf(id)); // 查询ES GetResponse getResponse; Content content = null; try { getResponse = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT); content = JSON.parseObject(getResponse.getSourceAsString(), Content.class); if(content == null) { content = contentService.findById(id); } if(content!=null && content.getId()!=null && content.getId()>0) { content.setTags(null); content.setImages(null); } } catch (IOException e) { log.error("从ES查询数据失败", e); throw new ServiceException("从ES查询数据失败"); } return content; } /** * 从数据库查询 * @return */ private JSONObject getListByMySQL(Integer pageNum, Integer pageSize, String type, String keyWord, String tagIds, String sourceIds) { JSONObject result = new JSONObject(); Content param = new Content(); param.setType(type); param.setTitle(keyWord); param.setTags(tagIds); param.setFromids(sourceIds); PageHelper.startPage(pageNum, pageSize); List dataList = contentService.getListByFront(param); PageInfo pages = new PageInfo<>(dataList); if(dataList!=null && dataList.size()>0) { for (Content entity : dataList) { // 清空一些前端用不上的字段 entity.setTags(null); entity.setImages(null); entity.setImgList(null); } } result.put("total", pages.getTotal()); // 总记录数 result.put("rows", dataList); // 数据体 int totalPage = (int)(pages.getTotal() % pageSize==0 ? pages.getTotal()/pageSize : pages.getTotal()/pageSize+1); result.put("totalPage", totalPage); // 总页数 return result; } @Override public Map getList(Integer pageNum, Integer pageSize, String type, String keyWord, String tagIds, String sourceIds) { // 封装Map参数返回 Map result = new HashMap<>(); try { // 选择标签或来源过滤时,从数据库查询记录 /*if(StringUtils.isNotBlank(tagIds) || StringUtils.isNotBlank(sourceIds)) { return getListByMySQL(pageNum, pageSize, type, keyWord, tagIds, sourceIds); }*/ SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); // 分页采用简单的from + size分页,适用数据量小的,了解更多分页方式可自行查阅资料 searchSourceBuilder.from((pageNum - 1) * pageSize); // 从0开始 searchSourceBuilder.size(pageSize); // 查询条件,只有查询关键字不为空才带查询条件 if (StringUtils.isNoneBlank(keyWord)) { // QueryBuilder queryBuilder = QueryBuilders.multiMatchQuery(keyword, "name", "desc"); // searchSourceBuilder.query(queryBuilder); } BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); boolQueryBuilder.must(QueryBuilders.termQuery("visibility", 1)); /*if(StringUtils.isNotBlank(type)) { // 案例类型 boolQueryBuilder.must(QueryBuilders.termQuery("type", type)); if (StringUtils.isNoneBlank(keyWord)){ BoolQueryBuilder keywordQuery = QueryBuilders.boolQuery(); keywordQuery.should(QueryBuilders.matchQuery("title", keyWord)); keywordQuery.should(QueryBuilders.matchQuery("desct", keyWord)); keywordQuery.should(QueryBuilders.matchQuery("fromName", keyWord)); keywordQuery.should(QueryBuilders.matchQuery("tags", keyWord)); keywordQuery.should(QueryBuilders.matchQuery("content_text", keyWord)); boolQueryBuilder.must(keywordQuery); } }else { if (StringUtils.isNoneBlank(keyWord)){ boolQueryBuilder.should(QueryBuilders.matchQuery("title", keyWord)); //.boost(3) // 给name字段更高的权重 boolQueryBuilder.should(QueryBuilders.matchQuery("desct", keyWord)); // description 默认权重 1 boolQueryBuilder.should(QueryBuilders.matchQuery("fromName", keyWord)); boolQueryBuilder.should(QueryBuilders.matchQuery("tags", keyWord)); boolQueryBuilder.should(QueryBuilders.matchQuery("content_text", keyWord)); boolQueryBuilder.minimumShouldMatch(1); // 至少一个should条件满足 } }*/ if (StringUtils.isNoneBlank(keyWord)){ BoolQueryBuilder keywordQuery = QueryBuilders.boolQuery(); keywordQuery.should(QueryBuilders.matchQuery("title", keyWord)); keywordQuery.should(QueryBuilders.matchQuery("desct", keyWord)); //keywordQuery.should(QueryBuilders.matchQuery("fromName", keyWord)); keywordQuery.should(QueryBuilders.matchQuery("tags", keyWord)); keywordQuery.should(QueryBuilders.matchQuery("content_text", keyWord)); boolQueryBuilder.must(keywordQuery); } //选择标签或来源过滤 if(StringUtils.isNotBlank(tagIds)){ List tagIdList = Arrays.asList(tagIds.split(",")); for(String tagId:tagIdList) { boolQueryBuilder.must(QueryBuilders.termQuery("tagList.tid", tagId)); } } /*if(StringUtils.isNotBlank(sourceIds)){ List sourIdList = Arrays.asList(sourceIds.split(",")); for(String sourId:sourIdList) { boolQueryBuilder.must(QueryBuilders.termQuery("fromid", sourId)); } }*/ searchSourceBuilder.query(boolQueryBuilder); // 排序,根据ID倒叙 searchSourceBuilder.sort("id", SortOrder.DESC); // SearchRequest SearchRequest searchRequest = new SearchRequest(); searchRequest.source(searchSourceBuilder); // 查询ES SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT); SearchHits hits = searchResponse.getHits(); // 获取总数 Long total = hits.getTotalHits().value; // 遍历封装列表对象 List dataList = new ArrayList<>(); SearchHit[] searchHits = hits.getHits(); for (SearchHit searchHit : searchHits) { Content entity = JSON.parseObject(searchHit.getSourceAsString(), Content.class); // 清空一些前端用不上的字段 entity.setTags(null); entity.setImages(null); entity.setImgList(null); dataList.add(entity); } result.put("total", total); // 总记录数 result.put("rows", dataList); // 数据体 int totalPage = (int)(total % pageSize==0 ? total/pageSize : total/pageSize+1); result.put("totalPage", totalPage); // 总页数 }catch (Exception e) { log.error("从es查询数据列表异常", e); } return result; } }