- 今天的文章,咱们先来体验用代码创建请求对象的不便之处,再尝试ES官方给我们提供的解决之道:用JSON创建请求对象
- 接下来,咱们从一个假设的任务开始
任务安排
- 现在咱们要创建一个索引,此索引记录的是商品信息
- 有一个副本(属于setting部分)
- 共三个分片(属于setting部分)
- 共三个字段:商品名称name(keyword),商品描述description(text),价格price(integer)(属于mapping部分)
- name字段值长为256,超出此长度的字段将不会被索引,但是会存储
- 接下来,咱们在kibana上用JSON创建索引,再写代码创建相同索引,然后对比两种方式的复杂程度
kibana上创建索引
- 如果在kibana上用json来创建,请求内容如下,索引名是product001
<span style="color:#393939"><span style="background-color:#faf7ef"><code class="language-json">PUT product001
{
<span style="color:#ff0000">"settings"</span>: {
<span style="color:#ff0000">"number_of_shards"</span>: <span style="color:#880000">3</span>,
<span style="color:#ff0000">"number_of_replicas"</span>: <span style="color:#880000">1</span>
},
<span style="color:#ff0000">"mappings"</span>: {
<span style="color:#ff0000">"properties"</span>: {
<span style="color:#ff0000">"name"</span>: {
<span style="color:#ff0000">"type"</span>: <span style="color:#a31515">"keyword"</span>,
<span style="color:#ff0000">"ignore_above"</span>: <span style="color:#880000">256</span>
},
<span style="color:#ff0000">"description"</span>: {
<span style="color:#ff0000">"type"</span>: <span style="color:#a31515">"text"</span>
},
<span style="color:#ff0000">"price"</span>: {
<span style="color:#ff0000">"type"</span>: <span style="color:#a31515">"integer"</span>
}
}
}
}
</code></span></span>
- 效果如下,符合预期
- 通过eshead观察,也是符合预期
- 可见基于JSON的操作简单明了,接下来看看创建相通索引的代码是什么样子
基于代码创建
- 关于如何连接ES的代码并非本篇重点,而且前面的文章已有详细说明,就不多赘述了
- 首先创建一个API,可以接受外部传来的Setting和Mapping设定,然后用这些设定来创建索引
<span style="color:#393939"><span style="background-color:#faf7ef"><code class="language-java"> <span style="color:#2b91af">@Autowired</span>
<span style="color:#0000ff">private</span> ElasticsearchClient elasticsearchClient;
<span style="color:#2b91af">@Override</span>
<span style="color:#0000ff">public</span> <span style="color:#0000ff">void</span> <span style="color:#a31515">create</span>(String name,
Function<IndexSettings.Builder, ObjectBuilder<IndexSettings>> settingFn,
Function<TypeMapping.Builder, ObjectBuilder<TypeMapping>> mappingFn) <span style="color:#0000ff">throws</span> IOException {
elasticsearchClient
.indices()
.create(c -> c
.index(name)
.settings(settingFn)
.mappings(mappingFn)
);
}
</code></span></span>
- 然后就是如何准备Setting和Mapping参数,再调用create方法完成创建,为了让代码顺利执行,我将调用create方法的代码写在单元测试类中,这样后面只需要执行单元测试即可调用create方法
<span style="color:#393939"><span style="background-color:#faf7ef"><code class="language-java"><span style="color:#2b91af">@SpringBootTest</span>
<span style="color:#0000ff">class</span> <span style="color:#a31515">EsServiceImplTest</span> {
<span style="color:#2b91af">@Autowired</span>
EsService esService;
<span style="color:#2b91af">@Test</span>
<span style="color:#0000ff">void</span> <span style="color:#a31515">create</span>() <span style="color:#0000ff">throws</span> Exception {
<span style="color:#008000">// 索引名</span>
<span style="color:#a31515">String</span> <span style="color:#008000">indexName</span> <span style="color:#ab5656">=</span> <span style="color:#a31515">"product002"</span>;
<span style="color:#008000">// 构建setting时,builder用到的lambda</span>
Function<IndexSettings.Builder, ObjectBuilder<IndexSettings>> settingFn = sBuilder -> sBuilder
.index(iBuilder -> iBuilder
<span style="color:#008000">// 三个分片</span>
.numberOfShards(<span style="color:#a31515">"3"</span>)
<span style="color:#008000">// 一个副本</span>
.numberOfReplicas(<span style="color:#a31515">"1"</span>)
);
<span style="color:#008000">// 新的索引有三个字段,每个字段都有自己的property,这里依次创建</span>
<span style="color:#a31515">Property</span> <span style="color:#008000">keywordProperty</span> <span style="color:#ab5656">=</span> Property.of(pBuilder -> pBuilder.keyword(kBuilder -> kBuilder.ignoreAbove(<span style="color:#880000">256</span>)));
<span style="color:#a31515">Property</span> <span style="color:#008000">textProperty</span> <span style="color:#ab5656">=</span> Property.of(pBuilder -> pBuilder.text(tBuilder -> tBuilder));
<span style="color:#a31515">Property</span> <span style="color:#008000">integerProperty</span> <span style="color:#ab5656">=</span> Property.of(pBuilder -> pBuilder.integer(iBuilder -> iBuilder));
<span style="color:#008000">// // 构建mapping时,builder用到的lambda</span>
Function<TypeMapping.Builder, ObjectBuilder<TypeMapping>> mappingFn = mBuilder -> mBuilder
.properties(<span style="color:#a31515">"name"</span>, keywordProperty)
.properties(<span style="color:#a31515">"description"</span>, textProperty)
.properties(<span style="color:#a31515">"price"</span>, integerProperty);
<span style="color:#008000">// 创建索引,并且指定了setting和mapping</span>
esService.create(indexName, settingFn, mappingFn);
}
}
</code></span></span>
- 由于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的源码,集成了父类,也实现了接口,
<span style="color:#393939"><span style="background-color:#faf7ef"><code class="language-java"><span style="color:#0000ff">public</span> <span style="color:#0000ff">static</span> <span style="color:#0000ff">class</span> <span style="color:#a31515">Builder</span> <span style="color:#0000ff">extends</span> <span style="color:#a31515">WithJsonObjectBuilderBase</span><Builder>
<span style="color:#0000ff">implements</span>
<span style="color:#a31515">ObjectBuilder</span><CreateIndexRequest> {
</code></span></span>
- 用IDEA查看类图的功能,Builder的继承和实现关系一目了然,注意红色箭头指向的WithJson接口,它是Builder父类实现的接口,也是让CreateIndexResponse可以通过JSON来创建的关键
- 强大的IDEA,可以在上图直接展开WithJson接口的所有方法签名,如下图,一目了然,三个方法三种入参,证明了使用者可以用三种方式将JSON内容传给Builder,再由Builer根据传入的内容生成CreateIndexResponse实例
创建工程
- 在《java与es8实战之二:实战前的准备工作》中创建整了个系列共用的父工程elasticsearch-tutorials,今天新建的新工程名为object-from-json,也属于elasticsearch-tutorials的子工程,pom.xml如下
<span style="color:#393939"><span style="background-color:#faf7ef"><code class="language-xml"><span style="color:#2b91af"><?xml version="1.0" encoding="UTF-8"?></span>
<span style="color:#0000ff"><<span style="color:#0000ff">project</span> <span style="color:#ff0000">xmlns</span>=<span style="color:#a31515">"http://maven.apache.org/POM/4.0.0"</span> <span style="color:#ff0000">xmlns:xsi</span>=<span style="color:#a31515">"http://www.w3.org/2001/XMLSchema-instance"</span>
<span style="color:#ff0000">xsi:schemaLocation</span>=<span style="color:#a31515">"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"</span>></span>
<span style="color:#008000"><!-- 请改为自己项目的parent坐标 --></span>
<span style="color:#0000ff"><<span style="color:#0000ff">parent</span>></span>
<span style="color:#0000ff"><<span style="color:#0000ff">artifactId</span>></span>elasticsearch-tutorials<span style="color:#0000ff"></<span style="color:#0000ff">artifactId</span>></span>
<span style="color:#0000ff"><<span style="color:#0000ff">groupId</span>></span>com.bolingcavalry<span style="color:#0000ff"></<span style="color:#0000ff">groupId&