360 lines
15 KiB
Java
360 lines
15 KiB
Java
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<Long> 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<Content> dataList = contentService.getListByFront(param);
|
||
PageInfo<Content> 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<String, Object> getList(Integer pageNum, Integer pageSize, String type, String keyWord, String tagIds, String sourceIds) {
|
||
// 封装Map参数返回
|
||
Map<String, Object> 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<String> tagIdList = Arrays.asList(tagIds.split(","));
|
||
for(String tagId:tagIdList) {
|
||
boolQueryBuilder.must(QueryBuilders.termQuery("tagList.tid", tagId));
|
||
}
|
||
}
|
||
/*if(StringUtils.isNotBlank(sourceIds)){
|
||
List<String> 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<Content> 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;
|
||
}
|
||
|
||
}
|