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

  • 今天的文章,咱们先来体验用代码创建请求对象的不便之处,再尝试ES官方给我们提供的解决之道:用JSON创建请求对象
  • 接下来,咱们从一个假设的任务开始

任务安排

  • 现在咱们要创建一个索引,此索引记录的是商品信息
  1. 有一个副本(属于setting部分)
  2. 共三个分片(属于setting部分)
  3. 共三个字段:商品名称name(keyword),商品描述description(text),价格price(integer)(属于mapping部分)
  4. 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>
  • 效果如下,符合预期

image-20220625110440090

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

image-20220625110708346

  • 可见基于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的形式传入,这种嵌套效果在编码中看起来还是有点绕的,阅读起来可能会有点不适应
  • 执行单元测试,如下图,未发生异常

image-20220625114226165

  • 用kibana查看新建的索引

image-20220625114553023

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

image-20220625114939286

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

image-20220625115042349

小结和感慨

  • 至此,可以得出结论:
  1. Java API Client的对ES的操作,能得到kibana+JSON相同的效果
  2. 然而,用java代码来实现JSON的嵌套对象的内容,代码的复杂程度上升,可读性下降(纯属个人感觉)
  • 另外,在开发期间,我们也常常先用kibana+JSON先做基本的测试和验证,然后再去编码
  • 因此,如果能在代码中直接使用kibana的JSON,以此取代复杂的builder pattern代码去创建各种增删改查的请求对象,那该多好啊
  • ES官方预判了我的预判,在Java API Client中支持使用JSON来构建请求对象

image-20220625153336739

能用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来创建的关键

image-20220625155614986

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

image-20220625160132898

创建工程

<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&
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以使用 Elasticsearch 的 Java API 来实现 es8.x script_score 定制搜索结果的分数。具体实现步骤如下: 1. 创建一个 SearchRequest 对象,设置索引名称和查询条件。 2. 创建一个 ScriptScoreFunctionBuilder 对象,设置脚本语言和脚本内容。 3. 将 ScriptScoreFunctionBuilder 对象添加到 FunctionScoreQueryBuilder 中。 4. 创建一个 SearchSourceBuilder 对象,设置查询条件和排序方式。 5. 将 FunctionScoreQueryBuilder 对象添加到 SearchSourceBuilder 中。 6. 执行查询并获取结果。 以下是示例代码: ``` SearchRequest searchRequest = new SearchRequest("index_name"); QueryBuilder queryBuilder = QueryBuilders.matchQuery("field_name", "search_text"); ScriptScoreFunctionBuilder scriptScoreFunctionBuilder = new ScriptScoreFunctionBuilder( new Script(ScriptType.INLINE, "painless", "doc['field_name'].value * factor", Collections.singletonMap("factor", 2.0))); FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(queryBuilder, scriptScoreFunctionBuilder); SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); searchSourceBuilder.query(functionScoreQueryBuilder); searchSourceBuilder.sort(SortBuilders.scoreSort()); SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT); ``` 其中,ScriptScoreFunctionBuilder 中的脚本语言为 painless,脚本内容为 doc['field_name'].value * factor,表示将文档中的某个字段的值乘以一个因子作为分数。FunctionScoreQueryBuilder 中的 queryBuilder 表示查询条件,scriptScoreFunctionBuilder 表示定制分数的方式。最后,将 FunctionScoreQueryBuilder 对象添加到 SearchSourceBuilder 中,并执行查询。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值