关于注解式的分布式Elasticsearch的封装案例
原生的RestLevelClient不好用,构建检索等很多重复操作。
对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
配置:
importcom.rz.config.ElsConfig;
importorg.frameworkset.elasticsearch.boot.ElasticSearchBoot;
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.boot.ApplicationArguments;
importorg.springframework.boot.ApplicationRunner;
importorg.springframework.core.annotation.Order;
importorg.springframework.stereotype.Component;
importjava.util.HashMap;
importjava.util.Map;
/**
*启动时初始化bBoss
*
*@authorsunziwen
*@version1.0
*@date2019/12/1216:54
**/
@Component
@Order(value=1)
publicclassStartElasticimplementsApplicationRunner{
@Autowired
privateElsConfigconfig;
@Override
publicvoidrun(ApplicationArgumentsargs)throwsException{
Mapproperties=newHashMap();
properties.put("elasticsearch.rest.hostNames",config.getElsClusterNodes());
ElasticSearchBoot.boot(properties);
}
}
注解和枚举:
packagecom.rz.szwes.annotations;
importjava.lang.annotation.*;
/**
*标识实体对应的索引信息
*
*@authorsunziwen
*2019/12/1310:14
*@version1.0
**/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public@interfaceESDsl{
/**
*xml的位置
*/
Stringvalue();
StringindexName();
/**
*elasticsearch7.x版本已经删除该属性
*/
StringindexType()default"";
}
packagecom.rz.szwes.annotations;
importjava.lang.annotation.*;
/**
*为字段指定映射类型
*
*@authorsunziwen
*2019/12/1410:06
*@version1.0
**/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public@interfaceESMapping{
//映射类型
ESMappingTypevalue();
//加权
intboost()default1;
//分词标识analyzed、not_analyzed
Stringindex()default"analyzed";
//分词器ik_max_word、standard
Stringanalyzer()default"ik_max_word";
//String作为分组聚合字段的时候需要设置为true
booleanfildData()defaultfalse;
}
packagecom.rz.szwes.annotations;
/**
*Es映射类型枚举(定义了大部分,有缺失请使用者补全)当前版本基于elasticsearch6.8
*
*@authorsunziwen
*2019/12/1410:09
*@version1.0
**/
publicenumESMappingType{
/**
*全文搜索。
*/
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位双精度IEEE754浮点类型
*/
_doule("doule"),
/**
*32位单精度IEEE754浮点类型
*/
_float("float"),
/**
*16位半精度IEEE754浮点类型
*/
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"),
/**
*ip192.168.1.2
*/
ip("ip");
privateStringvalue;
ESMappingType(Stringvalue){
this.value=value;
}
publicStringgetValue(){
returnvalue;
}
}
工具类:对HashMap进行了增强
packagecom.rz.szwes.util; importjava.util.HashMap; importjava.util.function.Supplier; /** *原始HashMap不支持Lambda表达式,特此包装一个 * *@authorsunziwen *@version1.0 *@date2019/12/1311:09 **/ publicclassLambdaHashMapextendsHashMap { publicstatic LambdaHashMap builder(){ returnnewLambdaHashMap<>(); } publicLambdaHashMap put(Kkey,Supplier supplier){ super.put(key,supplier.get()); //流式 returnthis; } }
核心类两个:
packagecom.rz.szwes.core; importcn.hutool.core.util.ClassUtil; importcom.alibaba.fastjson.JSON; importcom.frameworkset.orm.annotation.ESId; importcom.rz.szwes.annotations.ESDsl; importcom.rz.szwes.annotations.ESMapping; importcom.rz.szwes.util.LambdaHashMap; importorg.springframework.util.StringUtils; importjava.lang.reflect.Field; importjava.lang.reflect.ParameterizedType; importjava.time.LocalDate; importjava.time.LocalDateTime; importjava.time.LocalTime; importjava.util.ArrayList; importjava.util.Arrays; importjava.util.Collections; importjava.util.Date; /** *抽象类解析泛型 * *@authorsunziwen *2019/12/1416:04 *@version1.0 **/ publicabstractclassAbstractElasticBase{ {//初始化解析 ParameterizedTypept=(ParameterizedType)this.getClass().getGenericSuperclass(); //获取第一个类型参数的真实类型 Class clazz=(Class )pt.getActualTypeArguments()[0]; parseMapping(clazz); } /** *索引名称 */ protectedStringindexName; /** *索引类型 */ protectedStringindexType; /** *es写dsl的文件路径 */ protectedStringxmlPath; /** *索引映射 */ protectedStringmapping; //将Class解析成映射JSONString privatevoidparseMapping(Class clazz){ if(clazz.isAnnotationPresent(ESDsl.class)){ ESDslesDsl=clazz.getAnnotation(ESDsl.class); this.xmlPath=esDsl.value(); this.indexName=esDsl.indexName(); //如果类型为空,则采用索引名作为其类型 this.indexType=StringUtils.isEmpty(esDsl.indexType())?esDsl.indexName():esDsl.indexType(); }else{ thrownewRuntimeException(clazz.getName()+"缺失注解[@ESDsl]"); } //构建索引映射 LambdaHashMap
packagecom.rz.szwes.core; importlombok.extern.slf4j.Slf4j; importorg.frameworkset.elasticsearch.boot.BBossESStarter; importorg.frameworkset.elasticsearch.client.ClientInterface; importorg.frameworkset.elasticsearch.client.ClientUtil; importorg.springframework.beans.factory.annotation.Autowired; importjava.util.*; /** *Elastic基础函数 * *@authorsunziwen *@version1.0 *@date2019/12/139:56 **/ @Slf4j publicclassElasticBaseServiceextendsAbstractElasticBase { @Autowired privateBBossESStarterstarter; /** *Xml创建索引 */ protectedStringcreateIndexByXml(StringxmlName){ ClientInterfacerestClient=starter.getConfigRestClient(xmlPath); booleanexistIndice=restClient.existIndice(this.indexName); if(existIndice){ restClient.dropIndice(indexName); } returnrestClient.createIndiceMapping(indexName,xmlName); } /** *自动创建索引 */ protectedStringcreateIndex(){ ClientInterfacerestClient=starter.getRestClient(); booleanexistIndice=restClient.existIndice(this.indexName); if(existIndice){ restClient.dropIndice(indexName); } log.debug("创建索引:"+this.mapping); returnrestClient.executeHttp(indexName,this.mapping,ClientUtil.HTTP_PUT); } /** *删除索引 */ protectedStringdelIndex(){ returnstarter.getRestClient().dropIndice(this.indexName); } /** *添加文档 * *@paramt实体类 *@paramrefresh是否强制刷新 */ protectedStringaddDocument(Tt,Booleanrefresh){ returnstarter.getRestClient().addDocument(indexName,indexType,t,"refresh="+refresh); } /** *添加文档 * *@paramts实体类集合 *@paramrefresh是否强制刷新 */ protectedStringaddDocuments(List ts,Booleanrefresh){ returnstarter.getRestClient().addDocuments(indexName,indexType,ts,"refresh="+refresh); } /** *分页-添加文档集合 * *@paramts实体类集合 *@paramrefresh是否强制刷新 */ protectedvoidaddDocumentsOfPage(List ts,Booleanrefresh){ this.delIndex(); this.createIndex(); intstart=0; introws=100; Integersize; 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); } /** *使用分页添加文档必须重写该类 * *@paramstart起始 *@paramrows项数 *@return */ protectedList pageDate(intstart,introws){ returnnull; } /** *删除文档 * *@paramidid *@paramrefresh是否强制刷新 *@return */ protectedStringdelDocument(Stringid,Booleanrefresh){ returnstarter.getRestClient().deleteDocument(indexName,indexType,id,"refresh="+refresh); } /** *删除文档 * *@paramidsid集合 *@paramrefresh是否强制刷新 *@return */ protectedStringdelDocuments(String[]ids,Booleanrefresh){ returnstarter.getRestClient().deleteDocumentsWithrefreshOption(indexName,indexType,"refresh="+refresh,ids); } /** *id获取文档 * *@paramid *@return */ protectedTgetDocument(Stringid,Class clazz){ returnstarter.getRestClient().getDocument(indexName,indexType,id,clazz); } /** *id更新文档 * *@paramt实体 *@paramrefresh是否强制刷新 *@return */ protectedStringupdateDocument(Stringid,Tt,Booleanrefresh){ returnstarter.getRestClient().updateDocument(indexName,indexType,id,t,"refresh="+refresh); } }
写复杂Dsl的xml:(如何写Dsl请参考bBoss-elasticsearch文档,用法类似mybatis标签)
框架集成完毕,以下是使用示例:
定义数据模型:
packagecom.rz.dto;
importcom.frameworkset.orm.annotation.ESId;
importcom.rz.szwes.annotations.ESDsl;
importcom.rz.szwes.annotations.ESMapping;
importcom.rz.szwes.annotations.ESMappingType;
importlombok.Data;
importjava.util.List;
/**
*对应elasticsearch服务器的数据模型
*
*@authorsunziwen
*@version1.0
*@date2019/12/1611:08
**/
@ESDsl(value="elasticsearch/zsInfo.xml",indexName="zsInfo")
@Data
publicclassElasticZsInfoDto{
@ESMapping(ESMappingType._byte)
privateintleast_hit;
privateintis_must_zz;
privateintzs_level;
privateintcur_zs_ct;
privateintleast_score_yy;
privateintleast_score_yw;
privateintarea_id;
privateStringcoll_name;
privateStringcoll_code;
privatelongcoll_pro_id;
privateintis_must_wl;
privateintcur_year;
privateintis_two;
privatelonglogo;
@ESId
privateintid;
privateStringarea;
privateintcollege_id;
privateStringis_must_yy;
privateintis_double;
privateintleast_score_zz;
privateintleast_score_wl;
privateStringgrade;
privateintis_nine;
privateStringpro_name;
privateintleast_score_sx;
privateintrelevanceSort;
privateintpre_avg;
privateStringis_must_dl;
privateStringprofession_code;
privateintleast_score_sw;
privateStringis_must_ls;
privateintgrade_zk;
privateintleast_score_wy;
privateintis_must_hx;
privateintprofession_id;
privateStringis_grad;
privateStringis_must_yw;
privateintis_must_sw;
privateintleast_score_ls;
privateintleast_score_dl;
privateStringzs_memo;
privateStringis_must_sx;
privateStringintroduce;
privateintis_must_wy;
privateintgrade_bk;
privateStringpre_name;
privateintleast_score_hx;
privateStringcoll_domain;
privateintpre_wch;
privateListcourses;
}
定义服务
packagecom.rz.service; importcom.rz.dto.ElasticZsInfoDto; importcom.rz.szwes.core.ElasticBaseService; /** *招生索引操作服务 * *@authorsunziwen *@version1.0 *@date2019/12/1611:02 **/ publicclassElasticZsInfoServiceextendsElasticBaseService{ }
完毕。
已经可以进行索引和文档的crud操作了,至于复杂的检索操作就需要在xml中定义了。这里只介绍了我增强的功能,大部分功能都在bBoss中定义好了,读者可以去看bBoss文档(笔者认为的他的唯一缺陷是不能通过实体配合注解实现自动索引,还要每次手动指定xml位置,手动写mapping是很痛苦的事情,特此进行了增强)。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持毛票票。如有错误或未考虑完全的地方,望不吝赐教。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。