package com.admin.model;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.admin.common.entity.BaseSJFZEntity;
import lombok.Data;
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;
/**
* Es 索引库实体
*
* @author wangwei
* @date 2024-04-25
*/
/***
* esmodel8、 esmodel9 是存储在es中的索引类型
*/
@Document(indexName = "esmodel9")
@Data
public class EsModel extends BaseSJFZEntity {
/*** 索引id*/
@Field(type = FieldType.Long)
private String id;
/**** es中对应的数据库业务单据id*/
@Field(analyzer = "ik_max_word")
private String ctId;
/**** es中业务数据 对应的业务数据库表名称*/
@Field(analyzer = "ik_max_word")
private String ctTableName;
/**** es中业务数据 对应的 业务简称*/
@Field(analyzer = "ik_max_word")
private String ctName;
/**** es中业务数据 对应的 业务内容详细信息*/
@Field(analyzer = "ik_max_word")
private String ctContent;
/* *//** 创建者 *//*
//@Field(analyzer = "ik_max_word")
private String createUser;
*//** 创建时间 *//*
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
//@Field(analyzer = "ik_max_word")
private Date createTime;
*//** 更新者 *//*
// @Field(analyzer = "ik_max_word")
private String updateUser;
*//** 更新时间 *//*
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
// @Field(analyzer = "ik_max_word")
private Date updateTime;*/
/** 备注 */
@Field(analyzer = "ik_max_word")
private String remark;
/** 备用1 */
@Field(analyzer = "ik_max_word")
private String by1;
/** 备用2 */
@Field(analyzer = "ik_max_word")
private String by2;
/** 备用3 */
@Field(analyzer = "ik_max_word")
private String by3;
/** 备用4 */
@Field(analyzer = "ik_max_word")
private String by4;
/** 备用5 */
@Field(analyzer = "ik_max_word")
private String by5;
}
2.
package com.admin.controller;
import com.admin.common.annotation.Log;
import com.admin.common.core.controller.BaseController;
import com.admin.common.core.domain.AjaxResult;
import com.admin.common.core.page.TableDataInfo;
import com.admin.common.enums.BusinessType;
import com.admin.model.EsModel;
import com.admin.service.EsService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* es -spi
*
* @author wangwei
* @date 2024-04-25
*/
@Api(tags = "ES-api")
@RestController
@RequestMapping("/es/api")
public class EsController extends BaseController {
@Autowired
private EsService esService;
/***
*从es中按指定字段值获取数据
* @param key
* @return
*/
@ApiOperation(value = "查询 从es中按指定字段值获取数据")
@GetMapping("/list")
public AjaxResult list(String key, Pageable pageable) {
AjaxResult ajaxResult = esService.searchES(key, pageable);
return ajaxResult;
}
/***
* 全文检索
*查询 从es中全文检索所包含的 业务数据信息
* eg:es中的数据字段有 name, contene,等,接口查询条件key 包含在所有字段中的业务数据信息
* @param key
* @return
*/
@ApiOperation(value = "查询 从es中全文检索所包含的 业务数据信息")
@GetMapping("/whole")
public TableDataInfo searchWhole(String key, Pageable pageable) {
List<EsModel> list = esService.searchWhole(key, pageable);
return getDataTable(list);
}
/****
* es中数据新增
*/
@ApiOperation(value = "es中数据新增" )
@Log(title = "es中数据新增" , businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody EsModel esModel) {
return toAjax(esService.insertEsModel(esModel));
}
/****
* es中数据修改
*/
@ApiOperation(value = "es中数据修改" )
@Log(title = "es中数据修改" , businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult put(@RequestBody EsModel esModel) {
return toAjax(esService.updateEsModel(esModel));
}
/**
* 删除es中的数据
*/
@ApiOperation(value = "删除es中的数据")
@Log(title = "删除es中的数据", businessType = BusinessType.DELETE)
@DeleteMapping("/{ctId}")
public AjaxResult remove(@PathVariable String ctId) {
return toAjax(esService.deleteById(ctId));
}
}
3.
package com.admin.service.impl;
import com.admin.common.core.domain.AjaxResult;
import com.admin.common.exception.CustomException;
import com.admin.common.utils.StringUtils;
import com.admin.constant.EsConstants;
import com.admin.model.EsModel;
import com.admin.repository.EsModelRepository;
import com.admin.service.EsService;
import com.admin.util.HighlightResultMapper;
import com.alibaba.fastjson.JSON;
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.common.xcontent.XContentType;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import static org.elasticsearch.index.query.QueryBuilders.matchQuery;
@Service
public class EsServiceImpl implements EsService {
@Autowired
private EsModelRepository esModelRepository;
@Autowired
private ElasticsearchRestTemplate elasticsearchRestTemplate;
@Autowired
private RestHighLevelClient client;
@Override
public AjaxResult searchES(String key, Pageable pageable) {
Page<EsModel> ctContent = esModelRepository.findEsModelByCtContent(key, pageable);
return AjaxResult.success(ctContent);
}
/***
* 全文检索
* @param key
* @return
*/
@Override
public List<EsModel> searchWhole(String key, Pageable pageable) {
//高亮字段设置
String ctName = "ctName";
String ctContent = "ctContent";
String remark = "remark";
String preTags = "<span style=\"color:#F56C6C\">";
String postTags = "</span>";
HighlightBuilder.Field ctNameField = new HighlightBuilder.Field(ctName).preTags(preTags).postTags(postTags);
HighlightBuilder.Field ctContentField = new HighlightBuilder.Field(ctContent).preTags(preTags).postTags(postTags);
HighlightBuilder.Field remarkField = new HighlightBuilder.Field(remark).preTags(preTags).postTags(postTags);
if (StringUtils.isEmpty(key)) {
//throw new CustomException("请输入搜索内容!");
List<EsModel> contentList = new ArrayList<>();
return contentList;
}
//只要数据中包含就能检索到数据 (类似于模糊查询,有分词器的效果)
//不同类别检索的进阶示例: https://www.cnblogs.com/jelly12345/p/14765477.html
SearchQuery searchQuery = new NativeSearchQueryBuilder()
.withPageable(pageable)//分页
.withQuery(QueryBuilders.multiMatchQuery(key, "ctName", "ctContent", "remark"))
.withHighlightFields(ctNameField, ctContentField, remarkField)//设置高亮
// .withSort(SortBuilders.fieldSort("date").order(SortOrder.DESC))//排序
.build();
Page<EsModel> esModels = null;
try {
esModels = elasticsearchRestTemplate.queryForPage(searchQuery, EsModel.class, new HighlightResultMapper());
}catch (Exception e){
throw new CustomException("数据检索失败,请联系管理员处理!");
}
List<EsModel> contentList = esModels.getContent();
return contentList;
}
/****
*数据插入es中
* @param esModel
* @return
*/
@Override
public int insertEsModel(EsModel esModel) {
try {
esModel.setCtId(UUID.randomUUID().toString());
EsModel save = esModelRepository.save(esModel);
} catch (Exception e) {
System.out.println("==================================错误信息是:"+e.getMessage()+"============================");
throw new CustomException("ES数据新增失败,请联系管理员处理!");
}
return 1;
}
/***
*es-update https://www.jb51.net/article/246798.htm
* @param esModel
* @return
*/
@Override
public int updateEsModel(EsModel esModel) {
UpdateRequest updateRequest = new UpdateRequest(EsConstants.ES_INDEX, EsConstants.ES_INDEX_TYPE, String.valueOf(esModel.getCtId()));
//更新 将对象转换为json
updateRequest.doc(JSON.toJSONString(esModel), XContentType.JSON);
//客户端发送请求,进行更新
UpdateResponse update = null;
try {
update = client.update(updateRequest, RequestOptions.DEFAULT);
} catch (IOException e) {
throw new CustomException("ES数据同步失败可能原因是es中的业务ctId在数据库中不存在,请查看es客户端查询验证,请联系管理员处理!");
}
if (!"OK".equals(update.status().toString())) {
throw new CustomException("ES数据修改失败,请联系管理员处理!");
}
return 1;
}
/***
* 删除es中的数据
* @param ctId
* @return
*/
@Override
public int deleteById(String ctId) {
try {
esModelRepository.deleteById(ctId);
}catch (Exception e){
throw new CustomException("ES数据删除失败,请联系管理员处理!");
}
return 1;
}
}
4.
package com.admin.repository;
import com.admin.model.EsModel;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Repository;
/**
* @author wangwei
* @date 2024-04-25 14:12:36
*/
@Repository
public interface EsModelRepository extends ElasticsearchRepository<EsModel, String> {
/*****
* 这儿查询的 findBy 根据idea 的补全查询(eg:ByCtContent 将查询es中的 字段名称为 ctContent 中的值)
* @param ket
* @param pageable
* @return
*/
Page<EsModel> findEsModelByCtContent(String ket, Pageable pageable);
/****
* 删除es操作
*/
public void deleteById(Long id);
}
5.检索结果高亮显示
package com.admin.util;
import com.alibaba.fastjson.JSON;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.data.domain.Pageable;
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 java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/***
* 检索高亮设置
* @author wangwei
* @date 2024-04-25 11:55
*/
public class HighlightResultMapper implements SearchResultMapper {
@Override
public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
long totalHits = response.getHits().getTotalHits();
List<T> list = new ArrayList<>();
SearchHits hits = response.getHits();
if (hits.getHits().length> 0) {
for (SearchHit searchHit : hits) {
Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();
T item = JSON.parseObject(searchHit.getSourceAsString(), clazz);
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
if (highlightFields.containsKey(field.getName())) {
try {
field.set(item, highlightFields.get(field.getName()).fragments()[0].toString());
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
list.add(item);
}
}
return new AggregatedPageImpl<>(list, pageable, totalHits);
}
@Override
public <T> T mapSearchHit(SearchHit searchHit, Class<T> type) {
return null;
}
}
6.vue
<template>
<div class="app-container">
<el-row :gutter="20">
<!--用户数据-->
<el-col :span="20" :xs="24">
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px" style="text-align: center">
<el-form-item label="检索内容" prop="key" >
<el-input
v-model="queryParams.key"
placeholder="请输入检索内容"
clearable
size="small"
style="width: 240px"
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-table v-loading="loading" :data="tableList" >
<el-table-column label="编号" align="center" key="ctId" prop="ctId" v-if="columns[0].visible" />
<el-table-column label="名称" align="center" key="ctName" prop="ctName" v-if="columns[1].visible" :show-overflow-tooltip="true" />
<el-table-column label="内容" align="center" key="ctContent" prop="ctContent" v-if="columns[2].visible" :show-overflow-tooltip="true" />
<el-table-column label="备注" align="center" key="remark" prop="remark" v-if="columns[2].visible" :show-overflow-tooltip="true" />
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.number"
:limit.sync="queryParams.size"
@pagination="getList"
/>
</el-col>
</el-row>
</div>
</template>
<script>
import { list, getWholeList, addEsModel, editEsModel, removeEsModel} from "@/api/search";
import { getToken } from "@/utils/auth";
import { treeselect } from "@/api/system/dept";
import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
export default {
name: "User",
components: { Treeselect },
data() {
return {
// 遮罩层
loading: true,
// 选中数组
ids: [],
// 非单个禁用
single: true,
// 非多个禁用
multiple: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 表格数据
tableList: [],
// 弹出层标题
title: "",
// 部门树选项
deptOptions: undefined,
// 是否显示弹出层
open: false,
// 部门名称
deptName: undefined,
// 默认密码
initPassword: undefined,
// 日期范围
dateRange: [],
// 状态数据字典
statusOptions: [],
// 性别状态字典
sexOptions: [],
// 岗位选项
postOptions: [],
// 角色选项
roleOptions: [],
// 表单参数
form: {},
defaultProps: {
children: "children",
label: "label"
},
// 查询参数
queryParams: {
number: 1,
size: 10,
},
// 列信息
columns: [
{ key: 0, label: `编号`, visible: true },
{ key: 1, label: `名称`, visible: true },
{ key: 2, label: `内容`, visible: true },
],
// 表单校验
rules: {
}
};
},
watch: {
},
created() {
this.getList();
},
methods: {
/** 查询用户列表 */
getList() {
this.loading = true;
console.log(this.queryParams)
getWholeList(this.queryParams).then(response => {
this.tableList = response.rows;
this.total = response.total;
this.loading = false;
});
},
// 取消按钮
cancel() {
this.open = false;
this.reset();
},
// 表单重置
reset() {
this.form = {
ctId: undefined,
ctName: undefined,
ctContent: undefined,
remark: undefined,
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
//this.queryParams.page = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.dateRange = [];
this.resetForm("queryForm");
this.handleQuery();
},
}
};
</script>
7.