java与es8实战之六:用JSON创建请求对象(比builder pattern更加直观简洁) ...

打印 上一主题 下一主题

主题 1051|帖子 1051|积分 3153

欢迎访问我的GitHub

这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos
本篇概览


  • 本文是《java与es8实战》系列的第六篇,经过前面的实战,咱们初步掌握了一些Java对ES的基本操作,通过发送请求对象(例如CreateIndexResponse)到ES服务端,达到操作ES的目的,但是细心的您可能发现了:请求对象可能很复杂,例如多层对象嵌套,那么用代码来创建这些请求对象也必然不会容易
  • 今天的文章,咱们先来体验用代码创建请求对象的不便之处,再尝试ES官方给我们提供的解决之道:用JSON创建请求对象
  • 接下来,咱们从一个假设的任务开始
任务安排


  • 现在咱们要创建一个索引,此索引记录的是商品信息

  • 有一个副本(属于setting部分)
  • 共三个分片(属于setting部分)
  • 共三个字段:商品名称name(keyword),商品描述description(text),价格price(integer)(属于mapping部分)
  • name字段值长为256,超出此长度的字段将不会被索引,但是会存储


  • 接下来,咱们在kibana上用JSON创建索引,再写代码创建相同索引,然后对比两种方式的复杂程度
kibana上创建索引


  • 如果在kibana上用json来创建,请求内容如下,索引名是product001
  1. PUT product001
  2. {
  3.   "settings": {
  4.     "number_of_shards": 3,
  5.     "number_of_replicas": 1
  6.   },
  7.   "mappings": {
  8.     "properties": {
  9.       "name": {
  10.         "type": "keyword",
  11.         "ignore_above": 256
  12.       },
  13.       "description": {
  14.         "type": "text"
  15.       },
  16.       "price": {
  17.         "type": "integer"
  18.       }
  19.     }
  20.   }
  21. }
复制代码

  • 效果如下,符合预期


  • 通过eshead观察,也是符合预期


  • 可见基于JSON的操作简单明了,接下来看看创建相通索引的代码是什么样子
基于代码创建


  • 关于如何连接ES的代码并非本篇重点,而且前面的文章已有详细说明,就不多赘述了
  • 首先创建一个API,可以接受外部传来的Setting和Mapping设定,然后用这些设定来创建索引
  1.     @Autowired
  2.     private ElasticsearchClient elasticsearchClient;
  3.     @Override
  4.     public void create(String name,
  5.                        Function<IndexSettings.Builder, ObjectBuilder<IndexSettings>> settingFn,
  6.                        Function<TypeMapping.Builder, ObjectBuilder<TypeMapping>> mappingFn) throws IOException {
  7.         elasticsearchClient
  8.                 .indices()
  9.                 .create(c -> c
  10.                         .index(name)
  11.                         .settings(settingFn)
  12.                         .mappings(mappingFn)
  13.                 );
  14.     }
复制代码

  • 然后就是如何准备Setting和Mapping参数,再调用create方法完成创建,为了让代码顺利执行,我将调用create方法的代码写在单元测试类中,这样后面只需要执行单元测试即可调用create方法
  1. @SpringBootTest
  2. class EsServiceImplTest {
  3.     @Autowired
  4.     EsService esService;
  5.     @Test
  6.     void create() throws Exception {
  7.         // 索引名
  8.         String indexName = "product002";
  9.         // 构建setting时,builder用到的lambda
  10.         Function<IndexSettings.Builder, ObjectBuilder<IndexSettings>> settingFn = sBuilder -> sBuilder
  11.                 .index(iBuilder -> iBuilder
  12.                         // 三个分片
  13.                         .numberOfShards("3")
  14.                         // 一个副本
  15.                         .numberOfReplicas("1")
  16.                 );
  17.         // 新的索引有三个字段,每个字段都有自己的property,这里依次创建
  18.         Property keywordProperty = Property.of(pBuilder -> pBuilder.keyword(kBuilder -> kBuilder.ignoreAbove(256)));
  19.         Property textProperty = Property.of(pBuilder -> pBuilder.text(tBuilder -> tBuilder));
  20.         Property integerProperty = Property.of(pBuilder -> pBuilder.integer(iBuilder -> iBuilder));
  21.         // // 构建mapping时,builder用到的lambda
  22.         Function<TypeMapping.Builder, ObjectBuilder<TypeMapping>> mappingFn = mBuilder -> mBuilder
  23.                 .properties("name", keywordProperty)
  24.                 .properties("description", textProperty)
  25.                 .properties("price", integerProperty);
  26.         // 创建索引,并且指定了setting和mapping
  27.         esService.create(indexName, settingFn, mappingFn);
  28.     }
  29. }
复制代码

  • 由于Java API Client中所有对象都统一使用builder pattern的方式创建,这导致代码量略多,例如setting部分,除了setting自身要用Lambda表达式,设置分片和副本的代码也要用Lambda的形式传入,这种嵌套效果在编码中看起来还是有点绕的,阅读起来可能会有点不适应
  • 执行单元测试,如下图,未发生异常


  • 用kibana查看新建的索引


  • 最后,将product001和product002的mapping放在一起对比,可见一模一样


  • 再用eshead对比分片和副本的效果,也是一模一样
小结和感慨


  • 至此,可以得出结论:

  • Java API Client的对ES的操作,能得到kibana+JSON相同的效果
  • 然而,用java代码来实现JSON的嵌套对象的内容,代码的复杂程度上升,可读性下降(纯属个人感觉)


  • 另外,在开发期间,我们也常常先用kibana+JSON先做基本的测试和验证,然后再去编码
  • 因此,如果能在代码中直接使用kibana的JSON,以此取代复杂的builder pattern代码去创建各种增删改查的请求对象,那该多好啊
  • ES官方预判了我的预判,在Java API Client中支持使用JSON来构建请求对象
能用JSON的根本原因


  • 动手实践之前,有个问题先思考一下
  • 刚才咱们写了那么多代码,才能创建出CreateIndexResponse对象(注意代码:elasticsearchClient.indices().create),怎么就能用JSON轻易的创建出来呢?有什么直接证据或者关键代码吗?
  • 来看看CreateIndexResponse的builder的源码,集成了父类,也实现了接口,
  1. public static class Builder extends WithJsonObjectBuilderBase<Builder>
  2.                         implements
  3.                                 ObjectBuilder<CreateIndexRequest> {
复制代码

  • 用IDEA查看类图的功能,Builder的继承和实现关系一目了然,注意红色箭头指向的WithJson接口,它是Builder父类实现的接口,也是让CreateIndexResponse可以通过JSON来创建的关键


  • 强大的IDEA,可以在上图直接展开WithJson接口的所有方法签名,如下图,一目了然,三个方法三种入参,证明了使用者可以用三种方式将JSON内容传给Builder,再由Builer根据传入的内容生成CreateIndexResponse实例

创建工程

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3.          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  4.    
  5.     <parent>
  6.         <artifactId>elasticsearch-tutorials</artifactId>
  7.         <groupId>com.bolingcavalry</groupId>
  8.         <version>1.0-SNAPSHOT</version>
  9.         <relativePath>../pom.xml</relativePath>
  10.     </parent>
  11.     <modelVersion>4.0.0</modelVersion>
  12.    
  13.     <artifactId>object-from-json</artifactId>
  14.     <packaging>jar</packaging>
  15.    
  16.     <name>object-from-json</name>
  17.     <url>https://github.com/zq2599</url>
  18.    
  19.     <dependencyManagement>
  20.         <dependencies>
  21.             <dependency>
  22.                 <groupId>org.springframework.boot</groupId>
  23.                 <artifactId>spring-boot-dependencies</artifactId>
  24.                 <version>${springboot.version}</version>
  25.                 <type>pom</type>
  26.                 <scope>import</scope>
  27.             </dependency>
  28.         </dependencies>
  29.     </dependencyManagement>
  30.     <dependencies>
  31.         <dependency>
  32.             <groupId>org.springframework.boot</groupId>
  33.             <artifactId>spring-boot-starter-actuator</artifactId>
  34.         </dependency>
  35.         
  36.         <dependency>
  37.             <groupId>org.springframework.boot</groupId>
  38.             <artifactId>spring-boot-configuration-processor</artifactId>
  39.             <optional>true</optional>
  40.         </dependency>
  41.         <dependency>
  42.             <groupId>org.projectlombok</groupId>
  43.             <artifactId>lombok</artifactId>
  44.         </dependency>
  45.         <dependency>
  46.             <groupId>org.springframework.boot</groupId>
  47.             <artifactId>spring-boot-starter-web</artifactId>
  48.         </dependency>
  49.         <dependency>
  50.             <groupId>org.springframework.boot</groupId>
  51.             <artifactId>spring-boot-starter-test</artifactId>
  52.             <scope>test</scope>
  53.             
  54.             <exclusions>
  55.                 <exclusion>
  56.                     <groupId>junit</groupId>
  57.                     <artifactId>junit</artifactId>
  58.                 </exclusion>
  59.             </exclusions>
  60.         </dependency>
  61.         
  62.         <dependency>
  63.             <groupId>org.junit.jupiter</groupId>
  64.             <artifactId>junit-jupiter-api</artifactId>
  65.             <scope>test</scope>
  66.         </dependency>
  67.         <dependency>
  68.             <groupId>org.junit.jupiter</groupId>
  69.             <artifactId>junit-jupiter-engine</artifactId>
  70.             <scope>test</scope>
  71.         </dependency>
  72.         
  73.         <dependency>
  74.             <groupId>co.elastic.clients</groupId>
  75.             <artifactId>elasticsearch-java</artifactId>
  76.         </dependency>
  77.         <dependency>
  78.             <groupId>com.fasterxml.jackson.core</groupId>
  79.             <artifactId>jackson-databind</artifactId>
  80.         </dependency>
  81.         
  82.         <dependency>
  83.             <groupId>jakarta.json</groupId>
  84.             <artifactId>jakarta.json-api</artifactId>
  85.         </dependency>
  86.         <dependency>
  87.             <groupId>org.springframework.boot</groupId>
  88.             <artifactId>spring-boot-starter-web</artifactId>
  89.         </dependency>
  90.     </dependencies>
  91.     <build>
  92.         <plugins>
  93.             
  94.             <plugin>
  95.                 <groupId>org.apache.maven.plugins</groupId>
  96.                 <artifactId>maven-surefire-plugin</artifactId>
  97.                 <version>3.0.0-M4</version>
  98.                 <configuration>
  99.                     <skipTests>false</skipTests>
  100.                 </configuration>
  101.             </plugin>
  102.             <plugin>
  103.                 <groupId>org.springframework.boot</groupId>
  104.                 <artifactId>spring-boot-maven-plugin</artifactId>
  105.                 <configuration>
  106.                     <excludes>
  107.                         <exclude>
  108.                             <groupId>org.projectlombok</groupId>
  109.                             <artifactId>lombok</artifactId>
  110.                         </exclude>
  111.                     </excludes>
  112.                 </configuration>
  113.             </plugin>
  114.         </plugins>
  115.         <resources>
  116.             <resource>
  117.                 <directory>src/main/resources</directory>
  118.                 <includes>
  119.                     <include>**/*.*</include>
  120.                 </includes>
  121.             </resource>
  122.         </resources>
  123.     </build>
  124. </project>
复制代码

  • 是个普通的SpringBoot应用,入口类FromJsonApplication.java如下,非常简单
  1. package com.bolingcavalry.fromjson;
  2. import org.springframework.boot.SpringApplication;
  3. import org.springframework.boot.autoconfigure.SpringBootApplication;
  4. @SpringBootApplication
  5. public class FromJsonApplication {
  6.     public static void main(String[] args) {
  7.         SpringApplication.run(FromJsonApplication.class, args);
  8.     }
  9. }
复制代码

  • 然后是连接ES的配置类ClientConfig.java,关于如何连接ES,在《java与es8实战之四》一文已经详细说明,不再赘述,直接使用配置类的elasticsearchClient方法创建的ElasticsearchClient对象即可操作ES
  1. @ConfigurationProperties(prefix = "elasticsearch") //配置的前缀
  2. @Configuration
  3. public class ClientConfig {
  4.     @Setter
  5.     private String hosts;
  6.     /**
  7.      * 解析配置的字符串,转为HttpHost对象数组
  8.      * @return
  9.      */
  10.     private HttpHost[] toHttpHost() {
  11.         if (!StringUtils.hasLength(hosts)) {
  12.             throw new RuntimeException("invalid elasticsearch configuration");
  13.         }
  14.         String[] hostArray = hosts.split(",");
  15.         HttpHost[] httpHosts = new HttpHost[hostArray.length];
  16.         HttpHost httpHost;
  17.         for (int i = 0; i < hostArray.length; i++) {
  18.             String[] strings = hostArray[i].split(":");
  19.             httpHost = new HttpHost(strings[0], Integer.parseInt(strings[1]), "http");
  20.             httpHosts[i] = httpHost;
  21.         }
  22.         return httpHosts;
  23.     }
  24.     @Bean
  25.     public ElasticsearchClient elasticsearchClient() {
  26.         HttpHost[] httpHosts = toHttpHost();
  27.         RestClient restClient = RestClient.builder(httpHosts).build();
  28.         RestClientTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
  29.         // And create the API client
  30.         return new ElasticsearchClient(transport);
  31.     }
  32. }
复制代码

  • 最后是配置文件application.yml
  1. elasticsearch:
  2.   # 多个IP逗号隔开
  3.   hosts: 127.0.0.1:9200
复制代码

  • 现在工程已经建好,接下来开始实践如何通过JSON得到请求对象,通过刚才对WithJson接口的分析,JSON转请求对象共有三种方式

  • ImputStream
  • JSON字符串
  • Parse


  • 接下来逐个实践
第一种:InputStream作为入参


  • 最简单的方式莫过通过InputStream转换,InputStream是大家常用到的IO类,相信您已经胸有成竹了,流程如下图


  • 开始编码,首先创建一个接口EsService.java,里面有名为create的方法,这是创建索引用的,入参是索引名和包含有JSON内容的InputStream
  1. public interface EsService {
  2.     /**
  3.      * 以InputStream为入参创建索引
  4.      * @param name 索引名称
  5.      * @param inputStream 包含JSON内容的文件流对象
  6.      */
  7.     void create(String name, InputStream inputStream) throws IOException;
  8. }
复制代码

  • 接下来是重点:EsService接口的实现类EsServiceImpl.java,可见非常简单,只要调用builder的withJson方法,将InputStream作为入参传入即可
  1. @Service
  2. public class EsServiceImpl implements EsService {
  3.     @Autowired
  4.     private ElasticsearchClient elasticsearchClient;
  5.     @Override
  6.     public void create(String name, InputStream inputStream) throws IOException {
  7.         // 根据InputStrea创建请求对象
  8.         CreateIndexRequest request = CreateIndexRequest.of(builder -> builder
  9.                 .index(name)
  10.                 .withJson(inputStream));
  11.         elasticsearchClient.indices().create(request);
  12.     }
  13. }
复制代码

  • 为了验证EsServiceImpl的create方法,先准备好json文件,文件名为product003.json,完整路径是:/Users/will/temp/202206/25/product003.json
  1. {
  2.   "settings": {
  3.     "number_of_shards": 3,
  4.     "number_of_replicas": 1
  5.   },
  6.   "mappings": {
  7.     "properties": {
  8.       "name": {
  9.         "type": "keyword",
  10.         "ignore_above": 256
  11.       },
  12.       "description": {
  13.         "type": "text"
  14.       },
  15.       "price": {
  16.         "type": "integer"
  17.       }
  18.     }
  19.   }
  20. }
复制代码

  • 最后写一个单元测试类,调用EsServiceImpl的create方法,将product003.json转成InputStream对象作为其入参,验证create方法的功能是否符合预期,如下所示,代码非常简单
  1.     @Test
  2.     void createByInputStream() throws Exception {
  3.         // 文件名
  4.         String filePath = "/Users/will/temp/202206/25/product003.json";
  5.         // 索引名
  6.         String indexName = "product003";
  7.         // 通过InputStream创建索引
  8.         esService.create(indexName, new FileInputStream(filePath));
  9.     }
复制代码

  • 运行单元测试代码,一切正常


  • 用kibana查看product003索引,如下所示,符合预期


  • 再用eshead查看副本和分片,和之前的两个索引一致
分析Reader类


  • 接下来尝试WithJson接口的第二个方法
  1. default T withJson(Reader input) {
  2.         JsonpMapper mapper = SimpleJsonpMapper.INSTANCE_REJECT_UNKNOWN_FIELDS;
  3.         return withJson(mapper.jsonProvider().createParser(input), mapper);
  4.     }
复制代码

  • 先来看看这个Reader的继承关系,本篇不会详细分析Reader代码,咱们重点关注它的两个比较重要的子类:StringReader和FileReader


  • 接下来先用FileReader作为withJson方法的入参,验证用文件来创建请求对象,再用StringReader作为withJson方法的入参,验证用字符串来创建请求对象
第二种:FileReader作为入参


  • 首先,给EsService接口新增一个方法
  1.     /**
  2.      * 以Reader为入参创建索引
  3.      * @param name 索引名称
  4.      * @param reader 包含JSON内容的文件流对象
  5.      */
  6.     void create(String name, Reader reader) throws IOException;
复制代码

  • 接下来是重点:EsService接口的实现类EsServiceImpl.java,可见非常简单,只要调用builder的withJson方法,将Reader作为入参传入即可
  1.     @Override
  2.     public void create(String name, Reader reader) throws IOException {
  3.         // 根据Reader创建请求对象
  4.         CreateIndexRequest request = CreateIndexRequest.of(builder -> builder
  5.                 .index(name)
  6.                 .withJson(reader));
  7.         elasticsearchClient.indices().create(request);
  8.     }
复制代码

  • json文件继续使用刚才创建的product003.json文件
  • 单元测试代码中也增加一个方法,用于验证刚才写的create方法
  1.     @Test
  2.     void createByReader() throws Exception {
  3.         // 文件名
  4.         String filePath = "/Users/will/temp/202206/25/product003.json";
  5.         // 索引名
  6.         String indexName = "product004";
  7.         // 通过InputStream创建索引
  8.         esService.create(indexName, new FileReader(filePath));
  9.     }
复制代码

  • 接下来是执行单元测试方法,在kibana和eshead上验证product004索引和之前新建的几个索引是否一致,这里就不多占用篇幅了,结论是一模一样
  • 其实吧,用InputStream或者Reader作为参数,内部实现是一回事,来看看FileReader构造方法的源码吧,里面是InputStream
  1. public class FileReader extends InputStreamReader {
  2.     public FileReader(String fileName) throws FileNotFoundException {
  3.         super(new FileInputStream(fileName));
  4.     }
复制代码
第三种:字符串作为入参


  • 接下来要验证的是用字符串来创建请求对象,这个比较实用,用字符串创建请求对象,给我们的应用开发提供了很大的自由度,废话少说,开始写代码
  • 首先还是给EsService接口新增一个方法,入参是索引名称和JSON字符串
  1.     /**
  2.      * 以字符串为入参创建索引
  3.      * @param name 索引名称
  4.      * @param jsonContent 包含JSON内容的字符串
  5.      */
  6.     void create(String name, String jsonContent) throws IOException;
复制代码

  • 接下来是重点:EsService接口的实现类EsServiceImpl.java,可见非常简单,用字符串创建StringReader对象,然后只要调用builder的withJson方法,将StringReader对象作为入参传入即可
  1.     @Override
  2.     public void create(String name, String jsonContent) throws IOException {
  3.         // 根据Reader创建请求对象
  4.         CreateIndexRequest request = CreateIndexRequest.of(builder -> builder
  5.                 .index(name)
  6.                 .withJson(new StringReader(jsonContent)));
  7.         elasticsearchClient.indices().create(request);
  8.     }
复制代码

  • 为了验证上面的create方法,在单元测试类中新增一个方法来验证
  1.     @Test
  2.     void createByString() throws Exception {
  3.         // 文件名
  4.         String jsonContent = "{\n" +
  5.                 "  "settings": {\n" +
  6.                 "    "number_of_shards": 3,\n" +
  7.                 "    "number_of_replicas": 1\n" +
  8.                 "  },\n" +
  9.                 "  "mappings": {\n" +
  10.                 "    "properties": {\n" +
  11.                 "      "name": {\n" +
  12.                 "        "type": "keyword",\n" +
  13.                 "        "ignore_above": 256\n" +
  14.                 "      },\n" +
  15.                 "      "description": {\n" +
  16.                 "        "type": "text"\n" +
  17.                 "      },\n" +
  18.                 "      "price": {\n" +
  19.                 "        "type": "integer"\n" +
  20.                 "      }\n" +
  21.                 "    }\n" +
  22.                 "  }\n" +
  23.                 "}\n";
  24.         // 索引名
  25.         String indexName = "product005";
  26.         // 通过InputStream创建索引
  27.         esService.create(indexName, jsonContent);
  28.     }
复制代码

  • 接下来是执行单元测试方法,在kibana和eshead上验证product004索引和之前新建的几个索引是否一致,这里就不多占用篇幅了,结论是一模一样
第四种:JsonParser和JsonpMapper作为入参


  • 基于JSON创建ES请求对象的最后一种方法如下,入参是JsonParser和JsonpMapper
  1. T withJson(JsonParser parser, JsonpMapper mapper)
复制代码

  • 前面三种方法,咱们都写了代码去验证,不过最后这种就不写代码验证了,原因很简单:没必要,咱们先来看看WithJson接口的源码
  1. public interface WithJson {    default T withJson(InputStream input) {        JsonpMapper mapper = SimpleJsonpMapper.INSTANCE_REJECT_UNKNOWN_FIELDS;        return withJson(mapper.jsonProvider().createParser(input), mapper);    }    default T withJson(Reader input) {
  2.         JsonpMapper mapper = SimpleJsonpMapper.INSTANCE_REJECT_UNKNOWN_FIELDS;
  3.         return withJson(mapper.jsonProvider().createParser(input), mapper);
  4.     }    T withJson(JsonParser parser, JsonpMapper mapper);}
复制代码

  • 可见,前面使用过的withJson(InputStream input)withJson(Reader input),其实都是在调用withJson(JsonParser parser, JsonpMapper mapper),所以,在实际使用中,掌握withJson(InputStream input)withJson(Reader input)就已经够用了,如果一定要使用withJson(JsonParser parser, JsonpMapper mapper),就参考上面的代码去构造JsonParser即可
代码和JSON内容混用


  • 有时候用代码和JSON混合使用来创建请求对象,既能用JSON省去大量代码工作,又能用代码保持该有的灵活性,如下所示,查询用JSON字符串,聚合参数用builder的API生成
  1. Reader queryJson = new StringReader(
  2.     "{" +
  3.     "  "query": {" +
  4.     "    "range": {" +
  5.     "      "@timestamp": {" +
  6.     "        "gt": "now-1w"" +
  7.     "      }" +
  8.     "    }" +
  9.     "  }" +
  10.     "}");
  11. SearchRequest aggRequest = SearchRequest.of(b -> b
  12.     .withJson(queryJson)
  13.     .aggregations("max-cpu", a1 -> a1
  14.         .dateHistogram(h -> h
  15.             .field("@timestamp")
  16.             .calendarInterval(CalendarInterval.Hour)
  17.         )
  18.         .aggregations("max", a2 -> a2
  19.             .max(m -> m.field("host.cpu.usage"))
  20.         )
  21.     )
  22.     .size(0)
  23. );
  24. Map<String, Aggregate> aggs = client
  25.     .search(aggRequest, Void.class)
  26.     .aggregations();
复制代码

  • 另外,不光是请求对象,与请求对象有关的实例也能用JSON生成,回顾本文最开始的那段代码中,构造CreateIndexResponse对象时还要创建Property对象,实际上这个Property是可以通过JSON生成的,参考代码如下
  1. String json = "{ " +
  2.             "        "type": "text"," +
  3.             "        "fields": {" +
  4.             "          "some_field": { " +
  5.             "            "type": "keyword"," +
  6.             "            "normalizer": "lowercase"" +
  7.             "          }" +
  8.             "        }" +
  9.             "      }";
  10.         Property p = Property.of(b -> b
  11.             .withJson(new StringReader(json))
  12.         );
复制代码

  • 至此,基于JSON构造ES请求对象的实战就完成了,今后在kibana上验证通过的JSON请求体,可以直接放在代码中用于使用,这将有效的降低代码量,也提升了整体可读性
源码下载

名称链接备注项目主页https://github.com/zq2599/blog_demos该项目在GitHub上的主页git仓库地址(https)https://github.com/zq2599/blog_demos.git该项目源码的仓库地址,https协议git仓库地址(ssh)git@github.com:zq2599/blog_demos.git该项目源码的仓库地址,ssh协议

  • 这个git项目中有多个文件夹,本次实战的源码在elasticsearch-tutorials文件夹下,如下图红框

  • elasticsearch-tutorials是个父工程,里面有多个module,本篇实战的module是object-from-json,如下图红框

欢迎关注博客园:程序员欣宸

学习路上,你不孤单,欣宸原创一路相伴...

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

钜形不锈钢水箱

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表