Fatal problems with es's save All method, and replacing save All with updates
Tags: springboot, java
Published: 2023-08-23 07:55:34

Recently, we encountered a problem with es on our project, because we called the start-es encapsulated Api [saveAll] method to import es into the library. Through reading the source code and repeated testing, saveAll found that it works by updating the documents in the index by id, and overwriting all the data in the original document. However, this is completely contrary to our requirements. Due to business problems, we will store the same document in the database several times, and each time some fields will be null, which is unavoidable for business requirements. Since we used saveAll in the initial design, our data will always be incomplete. Found a way to replace saveAll. 
To explain this method briefly: 
  1. update the field when it does not exist in the corresponding document 
  2. If the field exists in the corresponding document and is also available in the current database, overwrite this field 
  3. When the document does not exist, add the operation if the above policies are met, then the data is definitely not lost


**Specific implementation is as follows**
```java
/**
 * ElasticsearchUtil
 * 智慧
 * @author lhy
 * @date 2023年08月23日 11:40
 */
@Slf4j
@Component
public class ElasticsearchUtil {

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    /**
     * es的批量【插入/更新】操作
     * @author lhy
     * @date 2023/8/23 11:27
     * @param esIndex es索引
     * @param paramList 批量插入的数据集，其中每一个【Map<S,O>】 都是一个文档数据,该Map应由具体的实体类进行映射填充。并且仅应存放不为null的数据
     *                  以此,上述期望将会达到目标。
     */
    public void insertWisdom(String esIndex, List<Map<String,Object>> paramList) {
        //批量请求
        BulkRequest bulkRequest = new BulkRequest();
        //遍历文档数据集
        for (Map<String, Object> param : paramList) {
            //指定索引及文档
            UpdateRequest request = new UpdateRequest(esIndex, param.get("id").toString());
            //如果文档存在，则更新文档内容为参数 param 中的内容；如果文档不存在，则插入一个新文档，内容为参数 param 中的内容。
            request.doc(param).docAsUpsert(true).upsertRequest();
            bulkRequest.add(request);
        }
        //插入用户信息
        try {
            BulkResponse bulk = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
            if (bulk.hasFailures()) {
                log.info("数据写入失败：{}",bulk.buildFailureMessage());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 实体类数据提取
     * @author lhy
     * @date 2023/8/23 13:42
     * @param obj 实体类
     * @param claxx class
     * @return java.util.Map<java.lang.String,java.lang.Object>
     */
    private static Map<String, Object> getPureData(Object obj,Class<?> claxx) {
        Map<String, Object> map = new HashMap<>();
        Field[] fields = claxx.getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            try {
                Object value = field.get(obj);
                //过滤掉值为null的数据
                if (Objects.nonNull(value)) {
                    map.put(field.getName(), value);
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return map;
    }

    public static Map<String,Object> getPureData(Object obj) {
        String value = JSON.toJSONString(obj);
        return JSON.parseObject(value, Map.class);
    }

}
```

**The core idea is in the design of getPureData, where we only put data whose entity attribute value is not null**

If you have good ideas and methods to share with me, the first time to use this platform, I hope it will not affect your experience