栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

关于注解式的分布式Elasticsearch的封装案例

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

关于注解式的分布式Elasticsearch的封装案例

原生的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;
 

@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.*;
 

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@documented
public @interface ESDsl {
 
 String value();
 
 String indexName();
 
 
 String indexType() default "";
}
package com.rz.szwes.annotations;
 
 
import java.lang.annotation.*;
 

@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;
 

public enum ESMappingType {
 
 text("text"),
 
 keyword("keyword"),
 
 /
 
 
 _byte("byte"),
 
 _short("short"),
 
 _integer("integer"),
 
 _long("long"),
 
 /
 
 _doule("doule"),
 
 _float("float"),
 
 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("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;
 

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;
 

public abstract class AbstractElasticbase {
 { //初始化解析
 ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass();
 // 获取第一个类型参数的真实类型
 Class clazz = (Class) pt.getActualTypeArguments()[0];
 parseMapping(clazz);
 }
 
 
 protected String indexName;
 
 protected String indexType;
 
 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 put = LambdaHashMap.builder()
 .put("mappings", () -> LambdaHashMap.builder()
     .put(indexType, () -> LambdaHashMap.builder()
  .put("properties", () -> {
   Field[] fields = clazz.getDeclaredFields();
   LambdaHashMap builder = LambdaHashMap.builder();
   for (Field field : fields) {
   builder.put(field.getName(), () -> toEsjson(field));
   }
   return builder;
  })))
 ;
 this.mapping = JSON.toJSonString(put);
 }
 
 private LambdaHashMap toEsjson(Field field) {
 //基本数据类型
 if (ClassUtil.isSimpleTypeOrArray(field.getType())) {
  //对字符串做大小限制、分词设置
  if (new ArrayList(Collections.singletonList(String.class)).contains(field.getType())) {
  LambdaHashMap put = LambdaHashMap.builder()
  .put("type", () -> "text")
  .put("fields", () -> LambdaHashMap.builder()
      .put("keyword", () -> LambdaHashMap.builder()
   .put("type", () -> "keyword")
   .put("ignore_above", () -> 256)));
  if (field.isAnnotationPresent(ESMapping.class)) {
   ESMapping esMapping = field.getAnnotation(ESMapping.class);
   //设置聚合分组
   if (esMapping.fildData()) {
   put.put("fildData", () -> true);
   }
   //设置加权
   if (esMapping.boost() != 1) {
   put.put("boost", esMapping::boost);
   }
   //设置是否进行分词
   if (!"analyzed".equals(esMapping.index())) {
   put.put("analyzed", esMapping::analyzer);
   }
   //分词器
   put.put("analyzer", esMapping::analyzer);
  }
  return put;
  }
  //设置默认类型
  return LambdaHashMap.builder().put("type", () -> {
  if (field.isAnnotationPresent(ESMapping.class)) {
   ESMapping esMapping = field.getAnnotation(ESMapping.class);
   return esMapping.value().getValue();
  }
  if (new ArrayList(Arrays.asList(byte.class, Byte.class, short.class, Short.class, int.class, Integer.class, long.class, Long.class)).contains(field.getType())) {
   return "long";
  } else if (new ArrayList(Arrays.asList(double.class, Double.class, float.class, Float.class)).contains(field.getType())) {
   return "double";
  } else if (new ArrayList(Arrays.asList(Date.class, java.sql.Date.class, LocalDate.class, LocalDateTime.class, LocalTime.class)).contains(field.getType())) {
   return "date";
  } else if (new ArrayList(Arrays.asList(boolean.class, Boolean.class)).contains(field.getType())) {
   return "boolean";
  }
  return "text";
  });
 } else {
  //设置对象类型
  LambdaHashMap properties = LambdaHashMap.builder()
  .put("properties", () -> {
  Field[] fields = field.getType().getDeclaredFields();
  LambdaHashMap builder = LambdaHashMap.builder();
  for (Field field01 : fields) {
   builder.put(field01.getName(), toEsjson(field01));
  }
  return builder;
  });
  if (field.isAnnotationPresent(ESMapping.class)) {
  ESMapping esMapping = field.getAnnotation(ESMapping.class);
  properties.put("type", esMapping.value().getValue());
  }
  return properties;
 }
 } 
}
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.*;
 

@Slf4j
public class ElasticbaseService extends AbstractElasticbase {
 
 @Autowired
 private BBossESStarter starter;
 
 
 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);
 }
 
 
 protected String adddocument(T t, Boolean refresh) {
 return starter.getRestClient().adddocument(indexName, indexType, t, "refresh=" + refresh);
 }
 
 
 protected String adddocuments(List ts, Boolean refresh) {
 return starter.getRestClient().adddocuments(indexName, indexType, ts, "refresh=" + 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);
 }
 
 
 protected List pageDate(int start, int rows) {
 return null;
 }
 
 
 protected String deldocument(String id, Boolean refresh) {
 return starter.getRestClient().deletedocument(indexName, indexType, id, "refresh=" + refresh);
 }
 
 
 protected String deldocuments(String[] ids, Boolean refresh) {
 return starter.getRestClient().deletedocumentsWithrefreshOption(indexName, indexType, "refresh=" + refresh, ids);
 }
 
 
 protected T getdocument(String id, Class clazz) {
 return starter.getRestClient().getdocument(indexName, indexType, id, clazz);
 }
 
 
 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;
 

@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; 
 

public class ElasticZsInfoService extends ElasticbaseService { 
}

完毕。

已经可以进行索引和文档的crud操作了,至于复杂的检索操作就需要在xml中定义了。这里只介绍了我增强的功能,大部分功能都在bBoss中定义好了,读者可以去看bBoss文档(笔者认为的他的唯一缺陷是不能通过实体配合注解实现自动索引,还要每次手动指定xml位置,手动写mapping是很痛苦的事情,特此进行了增强)。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持考高分网。如有错误或未考虑完全的地方,望不吝赐教。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/129272.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号