set命令语法

SET key id [FIELD name value ...] [EX seconds] [NX|XX] (OBJECT geojson)|(POINT lat lon z)|(BOUNDS minlat minlon maxlat maxlon)|(HASH geohash)|(STRING value)

set命令就相当于redis中的hash命令的使用,也是一个keyid的组合,但是不同的是,Tile38的set命令还可以携带更多的其他属性,比如可以自定义FIELD字段,还可以设置EX有效期等等,那么我们需要给这个语法设计一套好用的java api,以便开发人员可以更好地使用Tile38。

语法分析

首先,根据上面提供的语法,我们可以分为三部分:

1.第一部分就是命令的启示关键字SET,我们把这个关键字单独作为一部分;

2.第二部分就是key id [FIELD name value ...] [EX seconds] [NX|XX],我们把这些都作为参数;

3.第三部分就是最后的目标数据对象:

(OBJECT geojson)|(POINT lat lon z)|(BOUNDS minlat minlon maxlat maxlon)|(HASH geohash)|(STRING value)

代码设计

1.我们把第一部分的命令关键字通过枚举的方式来管理:

enum Tile38Command implements ProtocolKeyword {
    SET;
    public final byte[] bytes;
    static final String UNDERSCORE = "_";
    static final String SPACE = " ";
    Tile38Command() {
      String name = StringUtils.replace(this.name(), UNDERSCORE, SPACE);
      this.bytes = name.getBytes(StandardCharsets.US_ASCII);
    }
    @Override
    public byte[] getBytes() {
      return this.bytes;
    }
}

因为redis客户端工具在发送命令前需要对所有命令进行编码,所以要求所有的命令都必须实现ProtocolKeyword接口。如果命令的起始关键字是两个或多个单词,那么我们会使用下划线连接,转换成bytes的时候我们可以使用空格把下划线替换。

2.我们把命令的第二部分抽象成一个具体的class,通过相关的字段来进行描述:

public class SetOpts {
  private String key;
  private String id;
  //字段值必须是双精度浮点型
  private Map<String, Double> fields;
  // 单位秒
  private int ex;
  // 创建方式:
  // NX 不存在的时候创建
  // XX 存在的时候更新
  private NxXx nxXx;
  private SetOpts(Builder builder) {
    this.key = builder.key;
    this.id = builder.id;
    this.fields = builder.fields;
    this.ex = builder.ex;
    this.nxXx = builder.nxXx;
  }
  
  // 把所有的参数按顺序放到列表中
  public List<String> commandLine() {
    List<String> result = new LinkedList<>();
    result.add(this.key);
    result.add(this.id);
    // 添加所有的FIELD
    if (MapUtils.isNotEmpty(this.fields)) {
      for (Map.Entry<String, Double> entry : this.fields.entrySet()) {
        result.add("FIELD");
        result.add(entry.getKey());
        result.add(entry.getValue().toString());
      }
    }
    // 添加`EX`
    if (this.ex >= 0) {
      result.add("EX");
      result.add(String.valueOf(this.ex));
    }
    // 添加NX或XX
    if (Objects.nonNull(this.nxXx)) {
      result.add(this.nxXx.name());
    }
    // 返回结果
    return result;
  }
  
  public enum NxXx {
    NX,
    XX
  }
  // 建造者模式
  public static class Builder {
    private String key;
    private String id;
    //字段值必须是双精度浮点型
    private Map<String, Double> fields;
    // 单位秒
    private int ex = -1;
    // 创建方式:
    // NX 不存在的时候创建
    // XX 存在的时候更新
    private NxXx nxXx;
    public Builder key(String key) {
      this.key = key;
      return this;
    }
    public Builder id(String id) {
      this.id = id;
      return this;
    }
    public Builder field(String field, double value) {
      if (Objects.isNull(this.fields)) {
        this.fields = new LinkedHashMap<>();
      }
      this.fields.put(field, value);
      return this;
    }
    public Builder ex(int seconds) {
      this.ex = seconds;
      return this;
    }
    public Builder nxXx(NxXx nxXx) {
      this.nxXx = nxXx;
      return this;
    }
    public SetOpts build() throws AwesomeException {
      if (StringUtils.isEmpty(this.key)) {
        throw new AwesomeException(500, "key is empty");
      }
      if (StringUtils.isEmpty(this.id)) {
        throw new AwesomeException(500, "id is empty");
      }
      // 创建SetOpts对象
      return new SetOpts(this);
    }
  }
}

我们上面通过建造者的设计模式,把所有的参数都转换成了SetOpts这个类当中,开发人员就可以通过SetOpts对象的构建来灵活地控制命令中的参数了。

3.我们需要把第三部分当中的不同数据对象转换成不同的类型:

POINT数据类型

Point关键的字段就是经纬度,除此之外,还有一个额外的字段z,用来存储额外的业务参数,可为空。

public class Point extends Element implements Serializable {
  // 经度
  private double lng;
  // 维度
  private double lat;
  // 额外的数据
  private double z;
  public Point(double lng, double lat, double z) {
    this.lat = lat;
    this.lng = lng;
    this.z = z;
  }
  public Point(double lng, double lat) {
    this(lng, lat, Integer.MIN_VALUE);
  }
  @Override
  public List<String> commandArgs() {
    List<String> result = new LinkedList<>();
    result.add("POINT");
    result.add(String.valueOf(this.lng));
    result.add(String.valueOf(this.lat));
    if (this.z != Integer.MIN_VALUE) {
      result.add(String.valueOf(this.z));
    }
    return result;
  }
}

BOUNDS数据类型

BOUNDS就是矩形,它的关键字段就是左下角和右上角两个点位,我们使用coordinate1和coordinate2来表示左下角和右上角;

@AllArgsConstructor
public class Bounds extends Element {
  private double[] coordinate1;
  private double[] coordinate2;
  @Override
  public List<String> commandArgs() {
    List<String> result = new LinkedList<>();
    result.add("BOUNDS");
    result.add(String.valueOf(coordinate1[0]));
    result.add(String.valueOf(coordinate1[1]));
    result.add(String.valueOf(coordinate2[0]));
    result.add(String.valueOf(coordinate2[1]));
    return result;
  }
}

HASH和STRING数据类型

HASH和STRING其实就是一个单独的字符串,但是我们还是把它封装一下,以便开发人员使用;

@AllArgsConstructor
public class Geohash extends Element {
  private String hash;
  @Override
  public List<String> commandArgs() {
    List<String> result = new LinkedList<>();
    result.add("HASH");
    result.add(this.hash);
    return result;
  }
}
@AllArgsConstructor
public class RawString extends Element {
  private String raw;
  @Override
  public List<String> commandArgs() {
    List<String> result = new LinkedList<>();
    result.add("STRING");
    result.add(this.raw);
    return result;
  }
}

OBJECT数据类型

OBJECT其实就是GeoJSON数据,这一类数据比较复杂一点,一共有六种类型,想了解的小伙伴可以看这里geojson.org/

Point,
LineString,
Polygon,
MultiPoint,
MultiLineString,
MultiPolygon

为了开发人员能够更好的使用这六种类型,我们同样使用建造者模式来设计一下GeoJSON数据类型:

@Data
public class GeoJson {
  public static class Builder {
    public Point.Builder point() {
      return new Point.Builder();
    }
    public MultiPoint.Builder multPoint() {
      return new MultiPoint.Builder();
    }
    public LineString.Builder lineString() {
      return new LineString.Builder();
    }
    public MultiLineString.Builder multiLineString() {
      return new MultiLineString.Builder();
    }
    public Polygon.Builder polygon() {
      return new Polygon.Builder();
    }
    public MultiPolygon.Builder multiPolygon() {
      return new MultiPolygon.Builder();
    }
  }
}

我们现在一个大类里面创建多个方法,每一个方法都把对应类型的建造者给创造出来,这样的话,就相当于这个类当中有创建六种对象的方式,每个建造者都只负责建造对应的那个对象。

下面分别是六个建造者的代码,每个对象都基于最基本的BaseGeoJson来构造,BaseGeoJson中把公共的字段type和额外的meta字段抽出来,各个类型不同的点在于坐标点的数量和层次不同,所以根据各自类型的特点,代码设计如下:

// Point类型
  public static class Point extends BaseGeoJson {
    // 坐标点
    private double[] coordinates;
    Point(Builder builder) {
      super(builder);
      this.type = GeoJsonType.Point;
      this.coordinates = builder.coordinates;
    }
    @Override
    protected Object coordinates() {
      return this.coordinates;
    }
    public static class Builder extends BaseGeoJson.Builder {
      private double[] coordinates;
      public Builder coordinate(double lon, double lat) {
        coordinates = new double[]{lat, lon};
        return this;
      }
      public Point build() {
        return new Point(this);
      }
    }
  }
// MultiPoint类型
  public static class MultiPoint extends BaseGeoJson {
    private double[][] coordinates;
    MultiPoint(Builder builder) {
      super(builder);
      this.type = GeoJsonType.MultiPoint;
      this.coordinates = builder.convert2Array();
    }
    @Override
    protected Object coordinates() {
      return this.coordinates;
    }
    public static class Builder extends BaseGeoJson.Builder {
      private List<Coordinate> coordinates;
      public Builder coordinate(double lon, double lat) {
        if (CollectionUtils.isEmpty(this.coordinates)) {
          this.coordinates = new LinkedList<>();
        }
        this.coordinates.add(new Coordinate(lat, lon));
        return this;
      }
      protected double[][] convert2Array() {
        int length = this.coordinates.size();
        double[][] result = new double[length][];
        for (int i = 0; i < length; i  ) {
          result[i] = this.coordinates.get(i).convertToArray();
        }
        return result;
      }
      @Override
      public MultiPoint build() {
        return new MultiPoint(this);
      }
    }
  }
// LineString类型
  public static class LineString extends MultiPoint {
    private double[][] coordinates;
    LineString(Builder builder) {
      super(builder);
      this.type = GeoJsonType.LineString;
    }
    public static class Builder extends MultiPoint.Builder {
      @Override
      public LineString build() {
        return new LineString(this);
      }
    }
  }
// MultiLineString类型
  public static class MultiLineString extends BaseGeoJson {
    private double[][][] coordinates;
    MultiLineString(Builder builder) {
      super(builder);
      this.type = GeoJsonType.MultiLineString;
      this.coordinates = builder.convertToArray();
    }
    @Override
    protected Object coordinates() {
      return this.coordinates;
    }
    public static class Builder extends BaseGeoJson.Builder {
      private List<Line> lines = new LinkedList<>();
      public Line line() {
        return new Line(this);
      }
      void addLine(Line line) {
        lines.add(line);
      }
      double[][][] convertToArray() {
        int length = this.lines.size();
        double[][][] result = new double[length][][];
        for (int i = 0; i < length; i  ) {
          Line line = this.lines.get(i);
          result[i] = line.convert2Array();
        }
        return result;
      }
      @Override
      public BaseGeoJson build() {
        return new MultiLineString(this);
      }
    }
    static class Line {
      private List<Coordinate> coordinates;
      private Builder builder;
      Line(Builder builder) {
        this.builder = builder;
        this.builder.addLine(this);
      }
      private double[][] convert2Array() {
        int length = this.coordinates.size();
        double[][] result = new double[length][];
        for (int i = 0; i < length; i  ) {
          result[i] = this.coordinates.get(i).convertToArray();
        }
        return result;
      }
      public Line coordinate(double lon, double lat) {
        if (CollectionUtils.isEmpty(this.coordinates)) {
          this.coordinates = new LinkedList<>();
        }
        this.coordinates.add(new Coordinate(lat, lon));
        return this;
      }
      public Line nextLine() {
        return new Line(this.builder);
      }
      public Builder end() {
        return this.builder;
      }
    }
  }
// Polygon类型
  public static class Polygon extends MultiPoint {
    private double[][][] coordinates;
    Polygon(Builder builder) {
      super(builder);
      this.type = GeoJsonType.Polygon;
      this.coordinates = new double[][][]{builder.convert2Array()};
    }
    public static class Builder extends MultiPoint.Builder {
      @Override
      public Polygon build() {
        return new Polygon(this);
      }
    }
  }
// MultiPolygon类型
  public static class MultiPolygon extends BaseGeoJson {
    private double[][][][] coordinates;
    MultiPolygon(Builder builder) {
      super(builder);
      this.type = GeoJsonType.MultiPolygon;
      this.coordinates = new double[][][][]{builder.convert2Array()};
    }
    @Override
    protected Object coordinates() {
      return this.coordinates;
    }
    public static class Builder extends BaseGeoJson.Builder {
      private List<Polygon> polygons = new LinkedList<>();
      @Override
      public BaseGeoJson build() {
        return new MultiPolygon(this);
      }
      void addPolygon(Polygon polygon) {
        polygons.add(polygon);
      }
      private double[][][] convert2Array() {
        int length = this.polygons.size();
        double[][][] result = new double[length][][];
        for (int i = 0; i < length; i  ) {
          result[i] = this.polygons.get(i).convert2Array();
        }
        return result;
      }
    }
    static class Polygon {
      private List<Coordinate> coordinates;
      private Builder builder;
      Polygon(Builder builder) {
        this.builder = builder;
        this.builder.addPolygon(this);
      }
      private double[][] convert2Array() {
        int length = this.coordinates.size();
        double[][] result = new double[length][];
        for (int i = 0; i < length; i  ) {
          result[i] = this.coordinates.get(i).convertToArray();
        }
        return result;
      }
      public Polygon coordinate(double lon, double lat) {
        if (CollectionUtils.isEmpty(this.coordinates)) {
          this.coordinates = new LinkedList<>();
        }
        this.coordinates.add(new Coordinate(lat, lon));
        return this;
      }
      public Polygon nextLine() {
        return new Polygon(this.builder);
      }
      public Builder end() {
        return this.builder;
      }
    }
  }
// 基类BaseGeoJson
  public abstract static class BaseGeoJson extends Element {
    // 公共字段type
    protected GeoJsonType type;
    // 公共字段metadata
    private Map<String, String> metadata;
    BaseGeoJson(Builder builder) {
      this.metadata = builder.metadata;
    }
    protected abstract Object coordinates();
    // 转换成命令参数
    @Override
    public List<String> commandArgs() {
      List<String> result = new LinkedList<>();
      result.add("OBJECT");
      result.add(toJson());
      return result;
    }
    // 提供统一的转json方法
    protected String toJson() {
      Map<String, Object> map = new LinkedHashMap<>();
      map.put("type", this.type);
      map.put("coordinates", coordinates());
      if (!CollectionUtils.isEmpty(this.metadata)) {
        for (Map.Entry<String, String> entry : this.metadata.entrySet()) {
          map.put(entry.getKey(), entry.getValue());
        }
      }
      return JsonUtil.obj2String(map);
    }
    abstract static class Builder {
      private Map<String, String> metadata;
      public Builder meta(String key, String value) {
        if (MapUtils.isEmpty(this.metadata)) {
          this.metadata = new LinkedHashMap<>();
        }
        this.metadata.put(key, value);
        return this;
      }
      public abstract BaseGeoJson build();
    }
    static class Coordinate {
      private double lat;
      private double lon;
      Coordinate(double lat, double lon) {
        this.lat = lat;
        this.lon = lon;
      }
      public double[] convertToArray() {
        return new double[]{this.lat, this.lon};
      }
    }
    // GeoJSON所有的数据类型
    enum GeoJsonType {
      Point,
      LineString,
      Polygon,
      MultiPoint,
      MultiLineString,
      MultiPolygon
    }
  }

最后,再补充一个基类Element:

public abstract class Element implements Serializable {
  public abstract List<String> commandArgs();
}

如何使用

我们针对所有的数据类型全部转换成具体的代码设计,下面我们看看如何使用:

private String setElement(SetOpts setOpts, Element element) {
    List<String> args1 = setOpts.commandLine();
    List<String> commandArgs = element.commandArgs();
    return execute(Tile38Command.SET, args1, commandArgs);
}
/**
   * 设置点位
   *
   * @param setOpts
   * @param point
   * @return
   */
  public String setPoint(SetOpts setOpts, Point point) {
    return setElement(setOpts, point);
  }
  /**
   * 设置对象
   *
   * @param setOpts
   * @param geoJson
   * @return
   */
  public String setObject(SetOpts setOpts, GeoJson.BaseGeoJson geoJson) {
    return setElement(setOpts, geoJson);
  }
  /**
   * 设置矩形边界
   *
   * @param setOpts
   * @param bounds
   * @return
   */
  public String setBounds(SetOpts setOpts, Bounds bounds) {
    return setElement(setOpts, bounds);
  }
  /**
   * 设置geohash
   *
   * @param setOpts
   * @param geohash
   * @return
   */
  public String setGeohash(SetOpts setOpts, Geohash geohash) {
    return setElement(setOpts, geohash);
  }
  /**
   * 设置String
   *
   * @param setOpts
   * @param string
   * @return
   */
  public String setString(SetOpts setOpts, RawString string) {
    return setElement(setOpts, string);
  }

所有的开发人员只需要按照上面的方法来使用就可以很方便地执行Tile38的命令了,至此,我们所有关于SET命令的设计都已经讲解完毕。

以上就是解析Springboot集成Tile38客户端之Set命令实现示例的详细内容,更多关于Springboot集成Tile客户端Set命令的资料请关注Devmax其它相关文章!

解析Springboot集成Tile38客户端之Set命令实现示例的更多相关文章

  1. SpringBoot本地磁盘映射问题

    这篇文章主要介绍了SpringBoot本地磁盘映射问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  2. java SpringBoot 分布式事务的解决方案(JTA+Atomic+多数据源)

    这篇文章主要介绍了java SpringBoot 分布式事务的解决方案(JTA+Atomic+多数据源),文章围绕主题展开详细的内容介绍,具有一定的参考价值,感兴趣的小伙伴可以参考一下

  3. SpringBoot整合Javamail实现邮件发送的详细过程

    日常开发过程中,我们经常需要使用到邮件发送任务,比方说验证码的发送、日常信息的通知等,下面这篇文章主要给大家介绍了关于SpringBoot整合Javamail实现邮件发送的详细过程,需要的朋友可以参考下

  4. SpringBoot详细讲解视图整合引擎thymeleaf

    这篇文章主要分享了Spring Boot整合使用Thymeleaf,Thymeleaf是新一代的Java模板引擎,类似于Velocity、FreeMarker等传统引擎,关于其更多相关内容,需要的小伙伴可以参考一下

  5. Springboot集成mybatis实现多数据源配置详解流程

    在日常开发中,若遇到多个数据源的需求,怎么办呢?通过springboot集成mybatis实现多数据源配置,简单尝试一下,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  6. PHP获取真实客户端的真实IP

    本文主要介绍了PHP获取真实客户端的真实IP(REMOTE_ADDR,HTTP_CLIENT_IP,HTTP_X_FORWARDED_FOR)的方法,具有很好的参考价值。下面跟着小编一起来看下吧

  7. SpringBoot使用Minio进行文件存储的实现

    本文主要介绍了SpringBoot使用Minio进行文件存储的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  8. 解析SpringBoot中使用LoadTimeWeaving技术实现AOP功能

    这篇文章主要介绍了SpringBoot中使用LoadTimeWeaving技术实现AOP功能,AOP面向切面编程,通过为目标类织入切面的方式,实现对目标类功能的增强,本文给大家介绍的非常详细,需要的朋友可以参考下

  9. 详解springboot测试类注解

    这篇文章主要介绍了springboot测试类注解,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  10. SpringBoot实现过滤器和拦截器的方法

    大家应该都晓得实现过滤器需要实现 javax.servlet.Filter 接口,而拦截器会在处理指定请求之前和之后进行相关操作,配置拦截器需要两步,本文通过实例代码给大家介绍SpringBoot 过滤器和拦截器的相关知识,感兴趣的朋友一起看看吧

随机推荐

  1. 基于EJB技术的商务预订系统的开发

    用EJB结构开发的应用程序是可伸缩的、事务型的、多用户安全的。总的来说,EJB是一个组件事务监控的标准服务器端的组件模型。基于EJB技术的系统结构模型EJB结构是一个服务端组件结构,是一个层次性结构,其结构模型如图1所示。图2:商务预订系统的构架EntityBean是为了现实世界的对象建造的模型,这些对象通常是数据库的一些持久记录。

  2. Java利用POI实现导入导出Excel表格

    这篇文章主要为大家详细介绍了Java利用POI实现导入导出Excel表格,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  3. Mybatis分页插件PageHelper手写实现示例

    这篇文章主要为大家介绍了Mybatis分页插件PageHelper手写实现示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  4. (jsp/html)网页上嵌入播放器(常用播放器代码整理)

    网页上嵌入播放器,只要在HTML上添加以上代码就OK了,下面整理了一些常用的播放器代码,总有一款适合你,感兴趣的朋友可以参考下哈,希望对你有所帮助

  5. Java 阻塞队列BlockingQueue详解

    本文详细介绍了BlockingQueue家庭中的所有成员,包括他们各自的功能以及常见使用场景,通过实例代码介绍了Java 阻塞队列BlockingQueue的相关知识,需要的朋友可以参考下

  6. Java异常Exception详细讲解

    异常就是不正常,比如当我们身体出现了异常我们会根据身体情况选择喝开水、吃药、看病、等 异常处理方法。 java异常处理机制是我们java语言使用异常处理机制为程序提供了错误处理的能力,程序出现的错误,程序可以安全的退出,以保证程序正常的运行等

  7. Java Bean 作用域及它的几种类型介绍

    这篇文章主要介绍了Java Bean作用域及它的几种类型介绍,Spring框架作为一个管理Bean的IoC容器,那么Bean自然是Spring中的重要资源了,那Bean的作用域又是什么,接下来我们一起进入文章详细学习吧

  8. 面试突击之跨域问题的解决方案详解

    跨域问题本质是浏览器的一种保护机制,它的初衷是为了保证用户的安全,防止恶意网站窃取数据。那怎么解决这个问题呢?接下来我们一起来看

  9. Mybatis-Plus接口BaseMapper与Services使用详解

    这篇文章主要为大家介绍了Mybatis-Plus接口BaseMapper与Services使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  10. mybatis-plus雪花算法增强idworker的实现

    今天聊聊在mybatis-plus中引入分布式ID生成框架idworker,进一步增强实现生成分布式唯一ID,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

返回
顶部