SpringBoot与es(elasticsearch)整合问题集锦

项目场景:

在springboot项目中,针对elasticsearch读写遇到的问题记录.


问题描述及解决方案

1.WRITE

1.springboot不同版本与其对应的es版本也不同,使用的部分API有所变化,详情见下方

1.更新es数据所使用的UpdateQuery构建方式不同
2.elasticsearchRestTemplate()传参不同

springboot-2.3.2.RELEASE版本(使用的版本)

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.2.RELEASE</version>
        <relativePath/>
    </parent>

构建方式如下:

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.BulkFailureException;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.UpdateQuery;

import java.lang.reflect.ParameterizedType;
import java.util.Set;

@Slf4j
public class EsService<T> {
    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    /**
     * 插入/更新
     * @param idName
     * @param object 数据
     */
    public void upsert(String idName, T object) {
        // 获取T.class
        Class<T> entityClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
        IndexCoordinates indexCoordinates = elasticsearchRestTemplate.getIndexCoordinatesFor(entityClass);
        try {
            if (object == null) {
                return;
            }
            JSONObject jsonObject = (JSONObject) JSON.toJSON(object);
            UpdateQuery updateQuery = UpdateQuery.builder(jsonObject.getString(idName)).withDocument(Document.parse(jsonObject.toString())).withDocAsUpsert(true).build();
            elasticsearchRestTemplate.update(updateQuery,indexCoordinates);
            
        } catch (BulkFailureException e) {
            //捕获更新/插入失败的异常,从而把失败数据的id取到
            if (e.getFailedDocuments() != null){
                Set<String> failedIds = e.getFailedDocuments().keySet();
                for (String failedId : failedIds) {
                    log.warn("更新/插入失败的数据id:" + failedId);
                }
            }
        }
    }
}

springboot-2.2.2.RELEASE版本

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.2.RELEASE</version>
        <relativePath/>
    </parent>

构建方式如下:

@Slf4j
public class EsService<T> {
    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    /**
     * 插入/更新
     * @param idName
     * @param object 数据
     */
    public void upsert(String idName, T object) {
        // 获取T.class
        Class<T> entityClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
        try {
            if (object == null) {
                return;
            }
            JSONObject jsonObject = (JSONObject) JSON.toJSON(object);
            UpdateRequest updateRequest = new UpdateRequest();
            updateRequest.retryOnConflict(1);// 冲突重试
            updateRequest.doc(jsonObject.toString(),XContentType.JSON);
            
            UpdateQuery updateQuery = new UpdateQueryBuilder()
								.withId(jsonObject.getString(idName))
								.withUpdateRequest(updateRequest)
								.withDoUpsert(true)
								.withClass(entityClass)
								.build();
            elasticsearchRestTemplate.update(updateQuery);
            
        } catch (BulkFailureException e) {
            //捕获更新/插入失败的异常,从而把失败数据的id取到
            if (e.getFailedDocuments() != null){
                // 这里会报空指针异常,因为这个版本底层对异常进行包装了
                Set<String> failedIds = e.getFailedDocuments().keySet();
                for (String failedId : failedIds) {
                    log.warn("更新/插入失败的数据id:" + failedId);
                }
            }
        }
    }
}

注:如果在2.2.2版本中使用2.3.2版本API构建方式会报空指针异常


2.es不同版本导致索引类型‘type’不同

es旧版本支持自定义索引类型,比如type=”Company”
es新版本之后索引类型均为“_doc”
因此需要在实体类上通过Document注解进行指明,否则索引类型会自动生成,为实体类类名小写格式如:Company->company,不指明会因为数据类型问题报错

@Data
@Document(indexName = "company",type = "_doc", useServerConfiguration = true, createIndex = false)
@Mapping
public class Company {
    @Field(type = FieldType.Object) // 这样才能使User里的@Field注解生效
    private User user;
}

3.在实体中指明es索引字段数据类型使其生效的问题-@Field注解

场景描述:实体字段amount(举例)为BigDecimal类型,es本应该自动映射为double类型或float类型,但插入es的第一条数据amount值为整数导致es映射为long型,之后再插入带有小数点的数据也能插入成功,但进行sum统计时小数点后边的小数es会对其进行忽略,不进行计算。

问题解决:

之前也标注了@Field注解但并没有生效,查阅多方资料说是spring-data-elasticsearch根本没有使用该标签。按网上的解决方法是使用mappingPath(指明属性类型的配置文件路径)标签,不指明mappingPath才会根据@Field注解指明的类型
最终解决步骤如下:

(1).在实体类上标注@Mapping注解
(2)如果该实体包含其他实体,需要在包含的实体上标注@Field(type=FieldType.Object)

@Data
@Document(indexName = "company",type = "_doc", useServerConfiguration = true, createIndex = false)
@Mapping
public class Company {
    @Field(type = FieldType.Object) // 这样才能使User里的@Field注解生效
    private User user;
}

(3)在包含的实体中的字段上也标注上@Field注解

@Data
public class User {
    @Field(type = FieldType.Double)
    private BigDecimal amount;
}

(4)在插入数据之前执行putMapping操作

elasticsearchRestTemplate.putMapping(entityClass);

4.由于数据字段个数达到上限,默认为1000个,导致数据插入失败,报错如下:
在这里插入图片描述

问题解决:

在建索引时指定字段个数上限

PUT /索引名/_settings
{
"index.mapping.total_fields.limit": 2000
}

5.字段映射问题

在es建索引时不用指明数据具体字段名及数据类型,直接put /索引名建个索引就可以,es会自动映射出该索引的属性及数据类型,但出现了一些属性的映射有问题,比如某些字段为字符串类型,而第一条数据存的却是整数值,因此在es将该字段映射为long类型,导致再插入带有字母的数据时报错

问题解决:

将数据插入之前将该字段转化为String类型,这样在es会映射为keyword类型


6.批量插入es捕获到失败数据id报空指针异常

插入es失败的数据,ElasticsearchException会把失败数据的id写入到FailedDocument(Map)的key上(获取写入失败id见上方代码),由于springboot版本问题可能会对ElasticsearchException进行封装后再抛出,以至于直接取捕获到异常里的FailedDocument报空指针(封装后的异常可能没有该Map集合)

问题解决:

更换springboot版本:2.2.2更换为2.3.2


2.READ

1.判断条件tremsQuery

tremsQuery类似于sql in,所以传参可以是list集合
QueryBuilders.termsQuery(字段,集合),需要该字段的type为keyword


记得点赞收藏奥,后续开发遇到问题会实时更新,关注不迷路~

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Spring Boot与Elasticsearch整合的方式有两种,分别是使用Java REST Client和使用spring-data-elasticsearch。在这两种方式中,本文使用了spring-data-elasticsearch来进行整合。 要使用spring-data-elasticsearch进行整合,首先需要下载并安装elasticsearch图像化插件(elasticsearch-head-master)[2]。该插件可以提供一个图形化界面,方便管理和操作elasticsearch。 接下来,需要在Spring Boot项目的pom.xml文件中添加相应的依赖,以便引入spring-data-elasticsearch的功能。可以在Maven Central Repository中找到相应的依赖。 在完成依赖的添加后,需要在Spring Boot项目的配置文件中配置elasticsearch的连接信息,包括主机名、端口号等。 接着,可以创建一个ElasticsearchRepository接口的实现类,在该类中可以定义一些简单的查询方法,如根据关键字查询等。 最后,可以在业务逻辑中使用该实现类来进行elasticsearch的相关操作,如保存文档、更新文档、删除文档等。 通过以上步骤,就可以实现Spring Boot与Elasticsearch整合,从而在Spring Boot项目中方便地使用elasticsearch进行数据存储和查询操作。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [SpringBoot 整合 ElasticSearch](https://blog.csdn.net/weixin_46522803/article/details/124709967)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [【十九】springboot整合ElasticSearch实战(万字篇)](https://blog.csdn.net/weixin_56995925/article/details/123873580)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [undefined](undefined)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天青色等烟雨...

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值