在pom.xml引入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-elasticsearch</artifactId> </dependency>
定义一个通用的实体类
所有业务中使用到的实体类都可以继承这个通用的实体类。
@Data
public class CommonEsModel implements Serializable {
@Id
private Long id;
@Field(type = FieldType.Text, analyzer = "ik_smart")
private String code;
@Field(type = FieldType.Text, analyzer = "ik_smart")
private String fondsCode; // 全宗编号
@Field(type = FieldType.Text, analyzer = "ik_smart")
private String title;
@Field(type = FieldType.Text, analyzer = "ik_smart")
private Long typeId; // 分类id
@Field(type = FieldType.Text, analyzer = "ik_smart")
private Integer type;
@Field(type = FieldType.Text, analyzer = "ik_smart")
private String fileCode; // 文件编号
@Field(type = FieldType.Text, analyzer = "ik_smart")
private String unitCode; // 档号
@Field(type = FieldType.Text, analyzer = "ik_smart")
private String custodyCom; // 保管部门
@Field(type = FieldType.Text, analyzer = "ik_smart")
private String manager; // 责任人
@Field(type = FieldType.Text, analyzer = "ik_smart")
private String secret; // 密级
@Field(type = FieldType.Text, analyzer = "ik_smart")
private Integer year; // 年度
@Field(type = FieldType.Text)
private String createBy;
@Field(type = FieldType.Date, pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
@Field(type = FieldType.Long)
private Long deptId;
@Field(type = FieldType.Long)
private Long tenantId;
/**
* ES中存储的用于模糊搜索、高亮显示的字段
*/
@Field(type = FieldType.Text, analyzer = "ik_max_word")
private String desc;
/**
* 文件内容
*/
@Field(type = FieldType.Text, analyzer = "ik_max_word")
private String content;
}
定义一个通用的Dao
public interface CommonEsDao<O extends CommonEsModel> extends ElasticsearchRepository<O, Long> {
}
定义一个通用的Service
@Service
public class CommonEsService<O extends CommonEsModel, D extends CommonEsDao<O>> {
private final D esDao;
private final ElasticsearchRestTemplate elasticsearchRestTemplate;
public CommonEsService(ElasticsearchRestTemplate elasticsearchRestTemplate, D esDao) {
this.elasticsearchRestTemplate = elasticsearchRestTemplate;
this.esDao = esDao;
}
/**
* 分页查询(带关键字和高亮)
*
* @param clz 映射的实体类
* @param keywords 搜索的关键字
* @param highLightField 高亮的字段
* @param orderBy 排序字段
*/
public Page<O> page(Integer current, Integer size, Class<O> clz, String keywords, String[] highLightField, String orderBy) {
BoolQueryBuilder defaultQueryBuilder = QueryBuilders.boolQuery();
if (StringUtils.isNotEmpty(keywords)) {
// 全文模糊搜索
for (String s : highLightField) {
defaultQueryBuilder.should(QueryBuilders.matchQuery(s, keywords));
}
}
// 分页条件
PageRequest pageRequest = PageRequest.of(current - 1, size);
// 高亮条件
HighlightBuilder highlightBuilder = getHighlightBuilder(highLightField);
highlightBuilder.requireFieldMatch(false);
// 排序条件
FieldSortBuilder sortBuilder = SortBuilders.fieldSort(orderBy).order(SortOrder.DESC);
//组装条件
NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(defaultQueryBuilder)
.withHighlightBuilder(highlightBuilder)
.withPageable(pageRequest)
.withSorts(sortBuilder)
.build();
// 查询
SearchHits<O> searchHits = elasticsearchRestTemplate.search(searchQuery, clz);
// 高亮字段映射
List<O> list = Lists.newArrayList();
for (SearchHit<O> searchHit : searchHits) {
O content = searchHit.getContent();
Map<String, List<String>> highlightFields = searchHit.getHighlightFields();
for (String highlightField : highlightFields.keySet()) {
// 设置高亮,这里仅为示例,实际项目中建议使用自定义的高亮字段通过反射或获取及设值
if (StringUtils.equals(highlightField, "desc")) {
content.setDesc(highlightFields.get(highlightField).get(0));
}
}
list.add(content);
}
// 组装分页对象
return new PageImpl<>(list, pageRequest, searchHits.getTotalHits());
}
/**
* 高级查询(带关键字和高亮)
*
* @param clz 映射的实体类
* @param termQuery 精准匹配的键值对,键为字段名称,值为要匹配的内容
* @param keywordsQuery 关键字匹配的键值对
* @param highLightField 高亮的字段
* @param orderBy 排序字段
*/
public Page<O> page(Integer current, Integer size, Class<O> clz, Map<String, Object> termQuery, Map<String, Object> keywordsQuery,
String[] highLightField, String orderBy, Set<Long> tenantIds) {
if (CollectionUtils.isEmpty(tenantIds)) {
return Page.empty();
}
BoolQueryBuilder defaultQueryBuilder = QueryBuilders.boolQuery();
// 精准匹配条件组装
if (CollectionUtils.isNotEmpty(termQuery)) {
for (String s : termQuery.keySet()) {
defaultQueryBuilder.should(QueryBuilders.termQuery(s, termQuery.get(s)));
}
}
defaultQueryBuilder.should(QueryBuilders.termsQuery("tenantId", tenantIds));
// 模糊搜索条件组装
if (CollectionUtils.isNotEmpty(keywordsQuery)) {
for (String s : keywordsQuery.keySet()) {
defaultQueryBuilder.should(QueryBuilders.matchQuery(s, keywordsQuery.get(s)));
}
}
// 分页条件
PageRequest pageRequest = PageRequest.of(current - 1, size);
// 高亮条件
HighlightBuilder highlightBuilder = getHighlightBuilder(highLightField);
highlightBuilder.requireFieldMatch(false);
// 排序条件
FieldSortBuilder sortBuilder = SortBuilders.fieldSort(orderBy).order(SortOrder.DESC);
//组装条件
NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(defaultQueryBuilder)
.withHighlightBuilder(highlightBuilder)
.withPageable(pageRequest)
.withSorts(sortBuilder)
.build();
// 查询
SearchHits<O> searchHits = elasticsearchRestTemplate.search(searchQuery, clz);
// 高亮字段映射
List<O> list = Lists.newArrayList();
for (SearchHit<O> searchHit : searchHits) {
O content = searchHit.getContent();
Map<String, List<String>> highlightFields = searchHit.getHighlightFields();
for (String highlightField : highlightFields.keySet()) {
for (String s : highLightField) {
// 设置高亮,这里仅为示例,实际项目中建议使用自定义的高亮字段通过反射或获取及设值
if (StringUtils.equals(highlightField, "desc")) {
content.setDesc(highlightFields.get(highlightField).get(0));
}
}
}
list.add(content);
}
// 组装分页对象
return new PageImpl<>(list, pageRequest, searchHits.getTotalHits());
}
/**
* 设置高亮字段
*/
private HighlightBuilder getHighlightBuilder(String... fields) {
// 高亮条件
HighlightBuilder highlightBuilder = new HighlightBuilder(); //生成高亮查询器
for (String field : fields) {
highlightBuilder.field(field);//高亮查询字段
}
highlightBuilder.requireFieldMatch(false); //如果要多个字段高亮,这项要为false
highlightBuilder.preTags("<span style=\"color:red\">"); //高亮设置
highlightBuilder.postTags("</span>");
//下面这两项,如果你要高亮如文字内容等有很多字的字段,必须配置,不然会导致高亮不全,文章内容缺失等
highlightBuilder.fragmentSize(800000); //最大高亮分片数
highlightBuilder.numOfFragments(0); //从第一个分片获取高亮片段
return highlightBuilder;
}
/**
* 通过id同ES中查询数据
*/
public O getById(Long id) {
Optional<O> byId = esDao.findById(id);
boolean present = byId.isPresent();
if (present) {
return byId.get();
}
return null;
}
/**
* 向ES保存数据
*/
public O save(O o) {
return esDao.save(o);
}
/**
* 保存/修改数据,如果id存在,则修改,否则保存
*/
public O updateOrSave(O o) {
return esDao.save(o);
}
/**
* 通过id从ES中删除数据
*/
public void delete(Long id) {
esDao.deleteById(id);
}
业务代码中调用
业务实体类
@Data
@Document(indexName = "yj_es_archives")
public class EsArchives extends CommonEsModel {
}
Service层引用
private final ElasticsearchRestTemplate elasticsearchRestTemplate;
private final EsArchivesDao dao;
private CommonEsService<EsArchives, EsArchivesDao> esService() {
return new CommonEsService<>(elasticsearchRestTemplate, dao);
}
private org.springframework.data.domain.Page<EsArchives> pageFromEs(Integer current, Integer size, ArchivesPriorSearch search) {
List<ArchivesFonds> fonds = fondsService.listCurrentUserFonds();
if (CollectionUtils.isEmpty(fonds)) {
return org.springframework.data.domain.Page.empty();
}
Set<Long> tenantIds = fonds.stream().map(ArchivesFonds::getId).collect(Collectors.toSet());
// 组装精准匹配的条件
Map<String, Object> termQuery = new HashMap<>();
if (StringUtils.isNotEmpty(search.getCustodyCom())) {
termQuery.put("custodyCom", search.getCustodyCom());
}
if (Objects.nonNull(search.getTypeId())) {
termQuery.put("typeId", search.getTypeId());
}
if (Objects.nonNull(search.getYear())) {
termQuery.put("year", search.getYear());
}
// 组装关键字匹配的条件
Map<String, Object> keywordsQuery = new HashMap<>();
if (StringUtils.isNotEmpty(search.getKeywords())) {
keywordsQuery.put("desc", search.getKeywords());
keywordsQuery.put("content", search.getKeywords());
}
if (StringUtils.isNotEmpty(search.getCode())) {
keywordsQuery.put("code", search.getCode());
}
if (StringUtils.isNotEmpty(search.getManager())) {
keywordsQuery.put("manager", search.getManager());
}
if (StringUtils.isNotEmpty(search.getTitle())) {
keywordsQuery.put("title", search.getTitle());
}
if (StringUtils.isNotEmpty(search.getSecret())) {
keywordsQuery.put("secret", search.getTitle());
}
if (StringUtils.isNotEmpty(search.getUnitCode())) {
keywordsQuery.put("unitCode", search.getUnitCode());
}
if (StringUtils.isNotEmpty(search.getFondsCode())) {
keywordsQuery.put("fondsCode", search.getFondsCode());
}
if (StringUtils.isNotEmpty(search.getFileCode())) {
keywordsQuery.put("fileCode", search.getFileCode());
}
String[] highLightField = new String[]{"code", "title", "desc", "content"};
return esService().page(current, size, EsArchives.class, termQuery, keywordsQuery, highLightField, "createTime", tenantIds);
}
这样就可以啦。
效果如下