关于注解式的分布式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(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。