基于注解式的分布式Elasticsearch的封装

基于注解式的分布式Elasticsearch的封装原生的RestLevelClient不好用,构建检索等很多重复操作。对bboss-elasticsearch进行了部分增强:通过注解配合实体类进行自动构建索引和自动刷入文档,复杂的业务检索需要自己在xml中写Dsl。用法与mybatis-plus如出一辙。依赖<dependency> <groupId>org.elasticsearch</gr…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全家桶1年46,售后保障稳定

原生的Rest Level Client不好用,构建检索等很多重复操作。

对bboss-elasticsearch进行了部分增强:通过注解配合实体类进行自动构建索引和自动刷入文档,复杂的业务检索需要自己在xml中写Dsl。用法与mybatis-plus如出一辙。

依赖

<dependency>
			<groupId>org.elasticsearch</groupId>
			<artifactId>elasticsearch</artifactId>
		</dependency>
		<dependency>
			<groupId>com.bbossgroups.plugins</groupId>
			<artifactId>bboss-elasticsearch-spring-boot-starter</artifactId>
			<version>5.9.5</version>
			<exclusions>
				<exclusion>
					<artifactId>slf4j-log4j12</artifactId>
					<groupId>org.slf4j</groupId>
				</exclusion>
			</exclusions>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<version>1.18.6</version>
			<scope>provided</scope>
		</dependency>

Jetbrains全家桶1年46,售后保障稳定

配置:


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<K, V> extends HashMap<K, V> {

    public static <K, V> LambdaHashMap<K, V> builder() {
        return new LambdaHashMap<>();
    }

    public LambdaHashMap<K, V> put(K key, Supplier<V> 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<T> {
    {   //初始化解析
        ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass();
        // 获取第一个类型参数的真实类型
        Class<T> clazz = (Class<T>) 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<T> 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<Object, Object> put = LambdaHashMap.builder()
                                                         .put("mappings", () -> LambdaHashMap.builder()
                                                                                             .put(indexType, () -> LambdaHashMap.builder()
                                                                                                                                .put("properties", () -> {
                                                                                                                                    Field[] fields = clazz.getDeclaredFields();
                                                                                                                                    LambdaHashMap<Object, Object> builder = LambdaHashMap.builder();
                                                                                                                                    for (Field field : fields) {
                                                                                                                                        builder.put(field.getName(), () -> toEsjson(field));
                                                                                                                                    }
                                                                                                                                    return builder;
                                                                                                                                })))
        ;
        this.mapping = JSON.toJSONString(put);
    }

    private LambdaHashMap<Object, Object> toEsjson(Field field) {
        //基本数据类型
        if (ClassUtil.isSimpleTypeOrArray(field.getType())) {
            //对字符串做大小限制、分词设置
            if (new ArrayList<Class>(Collections.singletonList(String.class)).contains(field.getType())) {
                LambdaHashMap<Object, Object> 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<Class>(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<Class>(Arrays.asList(double.class, Double.class, float.class, Float.class)).contains(field.getType())) {
                    return "double";
                } else if (new ArrayList<Class>(Arrays.asList(Date.class, java.sql.Date.class, LocalDate.class, LocalDateTime.class, LocalTime.class)).contains(field.getType())) {
                    return "date";
                } else if (new ArrayList<Class>(Arrays.asList(boolean.class, Boolean.class)).contains(field.getType())) {
                    return "boolean";
                }
                return "text";
            });
        } else {
            //设置对象类型
            LambdaHashMap<Object, Object> properties = LambdaHashMap.builder()
                                                                    .put("properties", () -> {
                                                                        Field[] fields = field.getType().getDeclaredFields();
                                                                        LambdaHashMap<Object, Object> 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.*;

/**
 * Elastic基础函数
 *
 * @author sunziwen
 * @version 1.0
 * @date 2019/12/13 9:56
 **/
@Slf4j
public class ElasticBaseService<T> extends AbstractElasticBase<T> {

    @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<T> ts, Boolean refresh) {
        return starter.getRestClient().addDocuments(indexName, indexType, ts, "refresh=" + refresh);
    }

    /**
     * 分页-添加文档集合
     *
     * @param ts      实体类集合
     * @param refresh 是否强制刷新
     */
    protected void addDocumentsOfPage(List<T> ts, Boolean refresh) {
        this.delIndex();
        this.createIndex();
        int start = 0;
        int rows = 100;
        Integer size;
        do {
            List<T> 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<T> 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<T> 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标签)

<properties>
    
</properties>

框架集成完毕,以下是使用示例:

定义数据模型:

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<String> 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<ElasticZsInfoDto> {

}

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

如有疑问sunziwen3366备注csdn。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/210154.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • python2 手动安装更新pip

    python2 手动安装更新pip

    2021年10月22日
  • soapclient php 扩展,PHP扩展—SOAP[通俗易懂]

    soapclient php 扩展,PHP扩展—SOAP[通俗易懂]一、概述及安装SOAP扩展可以用于编写SOAP服务器和客户端,支持SOAP1.1,SOAP1.2和WSDL1.1规格的子集。此扩展需要libxmlPHP扩展。这表示需要使用–enable-libxml,尽管这将隐式完成因为libxml是缺省开启的。要开启SOAP支持,配置PHP时要加上–enable-soap.二、相关函数is_soap_fault—…

  • 乌班图pycharm激活码2022年_在线激活2022.02.12

    (乌班图pycharm激活码2022年)最近有小伙伴私信我,问我这边有没有免费的intellijIdea的激活码,然后我将全栈君台教程分享给他了。激活成功之后他一直表示感谢,哈哈~IntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,下面是详细链接哦~https://javaforall.cn/100143.html4K…

  • Intel x86 Emulator Accelerator(HAXM installer)无法安装「建议收藏」

    Intel x86 Emulator Accelerator(HAXM installer)无法安装「建议收藏」在sdkmanager中Intelx86EmulatorAccelerator(HAXMinstaller)后面显示NOTcompatiblewithwindows这个时候可以尝试手动安装Intelx86EmulatorAccelerator(HAXMinstaller)1、在网上下载后,https://software.intel.com/en-us/artic…

  • MD编辑器设置字体、颜色、大小

    MD编辑器设置字体、颜色、大小颜色:浅红色文字:浅红色文字:深红色文字:深红色文字浅绿色文字:浅绿色文字深绿色文字:深绿色文字浅蓝色文字:浅蓝色文字深蓝色文字:深蓝色文字浅黄色文字:浅黄色文字深黄色文字:深黄色文字浅青色文字:浅青色文字深青色文字:深青色文字浅紫色文字:浅紫色文字深紫色文字:深紫色文字大小:size为1:size为1size为2:size为2size为3:size为3size为4:size为4size为10:size为10字体:我是黑体字我是宋体字我是微软雅黑字我是fanta

  • 运维面试官喜欢问的问题是_运维是什么意思

    运维面试官喜欢问的问题是_运维是什么意思你最大的缺点是什么?行为问题(behaviouralquestion),这类问题目的是看性格是否合适公司文化。主要考察:①你是否真心想做这个工作;②你性格与企业文化是否相符。所有答案都应该围绕这两点组织,即每个经历都应回归到你通过这个经历学到什么该职位所需关键技巧,这些经历为何让你想做这个工作,和该经历体现出你什么样的个人风格。你最大的缺点是什么:①避免避重就轻,谈一个算不得缺点的缺点,这…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号