原生的Rest Level Client不好用,构建检索等很多重复操作。
对bboss-elasticsearch进行了部分增强:通过注解配合实体类进行自动构建索引和自动刷入文档,复杂的业务检索需要自己在xml中写Dsl。用法与mybatis-plus如出一辙。
依赖
org.elasticsearch
elasticsearch
com.bbossgroups.plugins
bboss-elasticsearch-spring-boot-starter
5.9.5
slf4j-log4j12
org.slf4j
org.projectlombok
lombok
1.18.6
provided
配置:
import com.rz.config.ElsConfig;
import org.frameworkset.elasticsearch.boot.ElasticSearchBoot;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* 启动时初始化bBoss
*
* @author sunziwen
* @version 1.0
* @date 2019/12/12 16:54
**/
@Component
@Order(value = 1)
public class StartElastic implements ApplicationRunner {
@Autowired
private ElsConfig config;
@Override
public void run(ApplicationArguments args) throws Exception {
Map properties = new HashMap();
properties.put("elasticsearch.rest.hostNames", config.getElsClusterNodes());
ElasticSearchBoot.boot(properties);
}
}
注解和枚举:
package com.rz.szwes.annotations;
import java.lang.annotation.*;
/**
* 标识实体对应的索引信息
*
* @author sunziwen
* 2019/12/13 10:14
* @version 1.0
**/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ESDsl {
/**
* xml的位置
*/
String value();
String indexName();
/**
* elasticsearch7.x版本已经删除该属性
*/
String indexType() default "";
}
package com.rz.szwes.annotations;
import java.lang.annotation.*;
/**
* 为字段指定映射类型
*
* @author sunziwen
* 2019/12/14 10:06
* @version 1.0
**/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ESMapping {
//映射类型
ESMappingType value();
//加权
int boost() default 1;
//分词标识analyzed、not_analyzed
String index() default "analyzed";
//分词器ik_max_word、standard
String analyzer() default "ik_max_word";
//String作为分组聚合字段的时候需要设置为true
boolean fildData() default false;
}
package com.rz.szwes.annotations;
/**
* Es映射类型枚举(定义了大部分,有缺失请使用者补全)当前版本基于elasticsearch 6.8
*
* @author sunziwen
* 2019/12/14 10:09
* @version 1.0
**/
public enum ESMappingType {
/**
* 全文搜索。
*/
text("text"),
/**
* keyword类型适用于索引结构化(排序、过滤、聚合),只能通过精确值搜索到。
*/
keyword("keyword"),
/////////////////////////////////////////////////////////////////////////////
/**
* -128~127 在满足需求的情况下,尽可能选择范围小的数据类型。
*/
_byte("byte"),
/**
* -32768~32767
*/
_short("short"),
/**
* -2^31~2^31-1
*/
_integer("integer"),
/**
* -2^63~2^63-1
*/
_long("long"),
/////////////////////////////////////////////////////////////////////////////
/**
* 64位双精度IEEE 754浮点类型
*/
_doule("doule"),
/**
* 32位单精度IEEE 754浮点类型
*/
_float("float"),
/**
* 16位半精度IEEE 754浮点类型
*/
half_float("half_float"),
/**
* 缩放类型的的浮点数
*/
scaled_float("scaled_float"),
/////////////////////////////////////////////////////////////////////////////
/**
* 时间类型
*/
date("date"),
_boolean("boolean"),
/**
* 范围类型
*/
range("range"),
/**
* 嵌套类型
*/
nested("nested"),
/**
* 地理坐标
*/
geo_point("geo_point"),
/**
* 地理地图
*/
geo_shape("geo_shape"),
/**
* 二进制类型
*/
binary("binary"),
/**
* ip 192.168.1.2
*/
ip("ip");
private String value;
ESMappingType(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
工具类:对HashMap进行了增强
package com.rz.szwes.util;
import java.util.HashMap;
import java.util.function.Supplier;
/**
* 原始HashMap不支持Lambda表达式,特此包装一个
*
* @author sunziwen
* @version 1.0
* @date 2019/12/13 11:09
**/
public class LambdaHashMap extends HashMap {
public static LambdaHashMap builder() {
return new LambdaHashMap<>();
}
public LambdaHashMap put(K key, Supplier supplier) {
super.put(key, supplier.get());
//流式
return this;
}
}
核心类两个:
package com.rz.szwes.core;
import cn.hutool.core.util.ClassUtil;
import com.alibaba.fastjson.JSON;
import com.frameworkset.orm.annotation.ESId;
import com.rz.szwes.annotations.ESDsl;
import com.rz.szwes.annotations.ESMapping;
import com.rz.szwes.util.LambdaHashMap;
import org.springframework.util.StringUtils;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
/**
* 抽象类解析泛型
*
* @author sunziwen
* 2019/12/14 16:04
* @version 1.0
**/
public abstract class AbstractElasticBase {
{ //初始化解析
ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass();
// 获取第一个类型参数的真实类型
Class clazz = (Class) pt.getActualTypeArguments()[0];
parseMapping(clazz);
}
/**
* 索引名称
*/
protected String indexName;
/**
* 索引类型
*/
protected String indexType;
/**
* es写dsl的文件路径
*/
protected String xmlPath;
/**
* 索引映射
*/
protected String mapping;
//将Class解析成映射JSONString
private void parseMapping(Class clazz) {
if (clazz.isAnnotationPresent(ESDsl.class)) {
ESDsl esDsl = clazz.getAnnotation(ESDsl.class);
this.xmlPath = esDsl.value();
this.indexName = esDsl.indexName();
//如果类型为空,则采用索引名作为其类型
this.indexType = StringUtils.isEmpty(esDsl.indexType()) ? esDsl.indexName() : esDsl.indexType();
} else {
throw new RuntimeException(clazz.getName() + "缺失注解[@ESDsl]");
}
//构建索引映射
LambdaHashMap
package com.rz.szwes.core;
import lombok.extern.slf4j.Slf4j;
import org.frameworkset.elasticsearch.boot.BBossESStarter;
import org.frameworkset.elasticsearch.client.ClientInterface;
import org.frameworkset.elasticsearch.client.ClientUtil;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.*;
/**
* Elastic基础函数
*
* @author sunziwen
* @version 1.0
* @date 2019/12/13 9:56
**/
@Slf4j
public class ElasticBaseService extends AbstractElasticBase {
@Autowired
private BBossESStarter starter;
/**
* Xml创建索引
*/
protected String createIndexByXml(String xmlName) {
ClientInterface restClient = starter.getConfigRestClient(xmlPath);
boolean existIndice = restClient.existIndice(this.indexName);
if (existIndice) {
restClient.dropIndice(indexName);
}
return restClient.createIndiceMapping(indexName, xmlName);
}
/**
* 自动创建索引
*/
protected String createIndex() {
ClientInterface restClient = starter.getRestClient();
boolean existIndice = restClient.existIndice(this.indexName);
if (existIndice) {
restClient.dropIndice(indexName);
}
log.debug("创建索引:" + this.mapping);
return restClient.executeHttp(indexName, this.mapping, ClientUtil.HTTP_PUT);
}
/**
* 删除索引
*/
protected String delIndex() {
return starter.getRestClient().dropIndice(this.indexName);
}
/**
* 添加文档
*
* @param t 实体类
* @param refresh 是否强制刷新
*/
protected String addDocument(T t, Boolean refresh) {
return starter.getRestClient().addDocument(indexName, indexType, t, "refresh=" + refresh);
}
/**
* 添加文档
*
* @param ts 实体类集合
* @param refresh 是否强制刷新
*/
protected String addDocuments(List ts, Boolean refresh) {
return starter.getRestClient().addDocuments(indexName, indexType, ts, "refresh=" + refresh);
}
/**
* 分页-添加文档集合
*
* @param ts 实体类集合
* @param refresh 是否强制刷新
*/
protected void addDocumentsOfPage(List ts, Boolean refresh) {
this.delIndex();
this.createIndex();
int start = 0;
int rows = 100;
Integer size;
do {
List list = pageDate(start, rows);
if (list.size() > 0) {
//批量同步信息
starter.getRestClient().addDocuments(indexName, indexType, ts, "refresh=" + refresh);
}
size = list.size();
start += size;
} while (size > 0);
}
/**
* 使用分页添加文档必须重写该类
*
* @param start 起始
* @param rows 项数
* @return
*/
protected List pageDate(int start, int rows) {
return null;
}
/**
* 删除文档
*
* @param id id
* @param refresh 是否强制刷新
* @return
*/
protected String delDocument(String id, Boolean refresh) {
return starter.getRestClient().deleteDocument(indexName, indexType, id, "refresh=" + refresh);
}
/**
* 删除文档
*
* @param ids id集合
* @param refresh 是否强制刷新
* @return
*/
protected String delDocuments(String[] ids, Boolean refresh) {
return starter.getRestClient().deleteDocumentsWithrefreshOption(indexName, indexType, "refresh=" + refresh, ids);
}
/**
* id获取文档
*
* @param id
* @return
*/
protected T getDocument(String id, Class clazz) {
return starter.getRestClient().getDocument(indexName, indexType, id, clazz);
}
/**
* id更新文档
*
* @param t 实体
* @param refresh 是否强制刷新
* @return
*/
protected String updateDocument(String id, T t, Boolean refresh) {
return starter.getRestClient().updateDocument(indexName, indexType, id, t, "refresh=" + refresh);
}
}
写复杂Dsl的xml:(如何写Dsl请参考bBoss-elasticsearch文档,用法类似mybatis标签)
框架集成完毕,以下是使用示例:
定义数据模型:
package com.rz.dto;
import com.frameworkset.orm.annotation.ESId;
import com.rz.szwes.annotations.ESDsl;
import com.rz.szwes.annotations.ESMapping;
import com.rz.szwes.annotations.ESMappingType;
import lombok.Data;
import java.util.List;
/**
* 对应elasticsearch服务器的数据模型
*
* @author sunziwen
* @version 1.0
* @date 2019/12/16 11:08
**/
@ESDsl(value = "elasticsearch/zsInfo.xml", indexName = "zsInfo")
@Data
public class ElasticZsInfoDto {
@ESMapping(ESMappingType._byte)
private int least_hit;
private int is_must_zz;
private int zs_level;
private int cur_zs_ct;
private int least_score_yy;
private int least_score_yw;
private int area_id;
private String coll_name;
private String coll_code;
private long coll_pro_id;
private int is_must_wl;
private int cur_year;
private int is_two;
private long logo;
@ESId
private int id;
private String area;
private int college_id;
private String is_must_yy;
private int is_double;
private int least_score_zz;
private int least_score_wl;
private String grade;
private int is_nine;
private String pro_name;
private int least_score_sx;
private int relevanceSort;
private int pre_avg;
private String is_must_dl;
private String profession_code;
private int least_score_sw;
private String is_must_ls;
private int grade_zk;
private int least_score_wy;
private int is_must_hx;
private int profession_id;
private String is_grad;
private String is_must_yw;
private int is_must_sw;
private int least_score_ls;
private int least_score_dl;
private String zs_memo;
private String is_must_sx;
private String introduce;
private int is_must_wy;
private int grade_bk;
private String pre_name;
private int least_score_hx;
private String coll_domain;
private int pre_wch;
private List courses;
}
定义服务
package com.rz.service;
import com.rz.dto.ElasticZsInfoDto;
import com.rz.szwes.core.ElasticBaseService;
/**
* 招生索引操作服务
*
* @author sunziwen
* @version 1.0
* @date 2019/12/16 11:02
**/
public class ElasticZsInfoService extends ElasticBaseService {
}
完毕。已经可以进行索引和文档的crud操作了,至于复杂的检索操作就需要在xml中定义了。这里只介绍了我增强的功能,大部分功能都在bBoss中定义好了,读者可以去看bBoss文档(笔者认为的他的唯一缺陷是不能通过实体配合注解实现自动索引,还要每次手动指定xml位置,手动写mapping是很痛苦的事情,特此进行了增强)。
如有疑问sunziwen3366备注csdn。