SpringBoot integrated Elasticsearch8.x (7) | (new version of Java API Client using a complete example)

SpringBoot integrated Elasticsearch8.x (7) | (new version of Java API Client using a complete example)


Chapters
Link to Chapter 1: SpringBoot Integration with Elasticsearch7.x (1) | (Creation, Delete, Modify and Check Function Implementation)
Link to Chapter 2: Link to SpringBoot to Integrate Elasticsearch7.x (2) | (Complex Query)
Link to Chapter 3: SpringBoot Integration to Elasticsearch7.x (3) | (index aggregation query of aggregations)
Chapter 4 link: SpringBoot integrated Elasticsearch7.x (4) | (bucket aggregation query of aggregations)
Chapter 5 link: SpringBoot integrated Elasticsearch7.x (5) | match, match_phrase difference)
Chapter 6 link: SpringBoot integrated Elasticsearch8.x (6) | (new version Java API Client used)
Chapter 7 link: SpringBoot integrated Elasticsearch8.x (7) | (new version Java API Client uses a complete example )

foreword

After the Es7.15 version, es officially marked its advanced client RestHighLevelClient as deprecated. At the same time, a new java API client, Elasticsearch Java API Client, was launched, which will also become the officially recommended client in Elasticsearch 8.0 and later versions.

1. Project dependencies

There is no application of the springboot version with its own elasticsearch dependency. The built-in version should be 7.x, so the elasticsearch8.x dependency is introduced separately

	  <!--   springboot 依赖     -->
	   <parent>
	       <groupId>org.springframework.boot</groupId>
	       <artifactId>spring-boot-starter-parent</artifactId>
	       <version>2.5.7</version>
	       <relativePath/>
	   </parent>
	   <!--   elasticsearch依赖     -->
       <dependency>
           <groupId>co.elastic.clients</groupId>
           <artifactId>elasticsearch-java</artifactId>
           <version>8.1.0</version>
       </dependency>
       <dependency>
           <groupId>org.glassfish</groupId>
           <artifactId>jakarta.json</artifactId>
           <version>2.0.1</version>
       </dependency>

Two, springboot integration implementation

1. ElasticSearchConfig configuration implementation

Inject elasticsearch client

  @Configuration
public class ElasticSearchConfig {
    
    

    @Value("${es.host}")
    private String host;

    @Value("${es.port}")
    private int port;

    @Bean
    public ElasticsearchClient elasticsearchClient() {
    
    
        RestClient client = RestClient.builder(new HttpHost(host, port, "http")).build();
        ElasticsearchTransport transport = new RestClientTransport(client, new JacksonJsonpMapper());
        return new ElasticsearchClient(transport);
    }
}

2.elasticsearch tool class

The ElasticsearchHandle tool class mainly encapsulates the index of elasticsearch, and some methods of adding, deleting, modifying and checking corresponding to the data

@Slf4j
@Component
public class ElasticsearchHandle {
    
    

    @Autowired
    private ElasticsearchClient client;

    /**
     * 判断索引是否存在
     *
     * @param indexName
     * @return
     * @throws IOException
     */
    public boolean hasIndex(String indexName) throws IOException {
    
    
        BooleanResponse exists = client.indices().exists(d -> d.index(indexName));
        return exists.value();
    }

    /**
     * 删除索引
     *
     * @param indexName
     * @throws IOException
     */
    public boolean deleteIndex(String indexName) throws IOException {
    
    
        DeleteIndexResponse response = client.indices().delete(d -> d.index(indexName));
        return true;
    }

    /**
     * 创建索引
     *
     * @param indexName
     * @return
     * @throws IOException
     */
    public boolean createIndex(String indexName) {
    
    
        try {
    
    
            CreateIndexResponse indexResponse = client.indices().create(c -> c.index(indexName));
        } catch (IOException e) {
    
    
            log.error("索引创建失败:{}", e.getMessage());
            throw new ExploException(HttpCode.INDEX_CREATE_ERROR, "创建索引失败");
        }
        return true;
    }

    /**
     * 创建索引,不允许外部直接调用
     *
     * @param indexName
     * @param mapping
     * @throws IOException
     */
    private boolean createIndex(String indexName, Map<String, Property> mapping) throws IOException {
    
    
        CreateIndexResponse createIndexResponse = client.indices().create(c -> {
    
    
            c.index(indexName).mappings(mappings -> mappings.properties(mapping));
            return c;
        });
        return createIndexResponse.acknowledged();
    }

//    public Map<String, Property> buildMapping( Map<String, String> propertyKeys) {
    
    
//        Map<String, Property> documentMap = new HashMap<>();
//        for (Map.Entry<String, String> propertyKey : propertyKeys.entrySet()) {
    
    
//            String type = getIndxPropType(propertyKey.getValue());
//            String key = propertyKey.getKey();
//            log.info("属性:{}类型:{}", key, type);
//            if (type.equals("text")) {
    
    
//                documentMap.put(key, Property.of(property ->
//                                property.keyword(KeywordProperty.of(p ->
//                                                p.index(true)
//                                        )
//                                )
//                        )
//                );
//            } else if (type.equals("date")) {
    
    
//                documentMap.put(key, Property.of(property ->
//                                property.date(DateProperty.of(p ->
//                                                p.index(true).format("yyyy-MM-dd HH:mm:ss.SSS")
//                                        )
//                                )
//                        )
//                );
//            } else if (type.equals("long")) {
    
    
//                documentMap.put(key, Property.of(property ->
//                                property.long_(LongNumberProperty.of(p ->
//                                                p.index(true)
//                                        )
//                                )
//                        )
//                );
//            } else if (type.equals("integer")) {
    
    
//                documentMap.put(key, Property.of(property ->
//                                property.integer(
//                                        IntegerNumberProperty.of(p ->
//                                                p.index(false)
//                                        )
//                                )
//                        )
//                );
//            } else {
    
    
//                documentMap.put(key, Property.of(property ->
//                                property.object(
//                                        ObjectProperty.of(p ->
//                                                p.enabled(true)
//                                        )
//                                )
//                        )
//                );
//            }
//        }
//        return documentMap;
//    }

    /**
     * 重新创建索引,如果已存在先删除
     *
     * @param indexName
     * @param mapping
     */
    public void reCreateIndex(String indexName, Map<String, Property> mapping) {
    
    
        try {
    
    
            if (this.hasIndex(indexName)) {
    
    
                this.deleteIndex(indexName);
            }
        } catch (IOException e) {
    
    
            e.printStackTrace();
            throw new ExploException(HttpCode.INDEX_DELETE_ERROR, "删除索引失败");
        }

        try {
    
    
            this.createIndex(indexName, mapping);
        } catch (IOException e) {
    
    
            e.printStackTrace();
            throw new ExploException(HttpCode.INDEX_CREATE_ERROR, "重新创建索引失败");
        }
    }


    /**
     * 新增数据
     *
     * @param indexName
     * @throws IOException
     */
    public boolean insertDocument(String indexName, Object obj, String id) {
    
    
        try {
    
    
            IndexResponse indexResponse = client.index(i -> i
                    .index(indexName)
                    .id(id)
                    .document(obj));
            return true;
        } catch (IOException e) {
    
    
            log.error("数据插入ES异常:{}", e.getMessage());
            throw new ExploException(HttpCode.ES_INSERT_ERROR, "ES新增数据失败");
        }
    }

    /**
     * 查询数据
     *
     * @param indexName
     * @param id
     * @return
     */
    public GetResponse<DocumentPartESDto> searchDocument(String indexName, String id) {
    
    

        try {
    
    
            GetResponse<DocumentPartESDto> getResponse = client.get(g -> g
                            .index(indexName)
                            .id(id)
                    , DocumentPartESDto.class
            );
            return getResponse;
        } catch (IOException e) {
    
    
            log.error("查询ES异常:{}", e.getMessage());
            throw new ExploException(HttpCode.ES_SEARCH_ERROR, "查询ES数据失败");
        }
    }

    /**
     * 删除数据
     *
     * @param indexName
     * @param id
     * @return
     */
    public boolean deleteDocument(String indexName, String id) {
    
    
        try {
    
    
            DeleteResponse deleteResponse = client.delete(d -> d
                    .index(indexName)
                    .id(id)
            );
        } catch (IOException e) {
    
    
            log.error("删除Es数据异常:{}", e.getMessage());
            throw new ExploException(HttpCode.ES_DELETE_ERROR, "数据删除失败");
        }
        return true;
    }

}

3. Define an ES storage data structure

Define an entity for storing data in ES

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DocumentPartESDto {
    
    

    private String id;

    private String docId;

    private String kgId;

    private String content;

    private String type;
}

4.elasticsearch query tool class

Since we use mostly query scenarios, here are some ways to improve the query

4.1. Query data that meets the conditions

Parameter introduction
ndexName: the name of the index
query: the content of the query
top: the number of query items

   public List<DocumentParagraph> search(String indexName, String query,int top) {
    
    
        List<DocumentParagraph> documentParagraphs = Lists.newArrayList();
        try {
    
    
            SearchResponse<DocumentParagraph> search = client.search(s -> s
                            .index(indexName)
                            .query(q -> q
                                    .match(t -> t
                                            .field("content")
                                            .query(query)
                                    ))
                            .from(0)
                            .size(top)
//                    .sort(f -> f.field(o -> o.field("docId").order(SortOrder.Desc)))
                    , DocumentParagraph.class
            );
            for (Hit<DocumentParagraph> hit : search.hits().hits()) {
    
    
                DocumentParagraph pd = hit.source();
                documentParagraphs.add(pd);
            }
        } catch (IOException e) {
    
    
            log.error("查询ES异常:{}", e.getMessage());
            throw new ExploException(HttpCode.ES_SEARCH_ERROR, "查询ES数据失败");
        }
        return documentParagraphs;
    }

4.2. Query data that meets the conditions under certain groups

Parameter introduction
ndexName: index name
query: query content
categoryId: query data under those categories
top: query number

 public List<DocumentParagraph> search(String indexName, String query, List<String> categoryId,int top) {
    
    
        List<DocumentParagraph> documentParagraphs = Lists.newArrayList();
        List<FieldValue> values = new ArrayList<>();
        for (String id : categoryId) {
    
    
            values.add(FieldValue.of(id));
        }
        Query categoryQuery = TermsQuery.of(t -> t.field("categoryId.keyword").terms(new TermsQueryField.Builder()
                .value(values).build()
        ))._toQuery();

        try {
    
    
            SearchResponse<DocumentParagraph> search = client.search(s -> s
                            .index(indexName)
                            .query(q -> q
                                    .bool(b -> b
                                            .must(categoryQuery
                                            )
                                            .should(sh -> sh
                                                    .match(t -> t
                                                            .field("content")
                                                            .query(query)
                                                    )
                                            )
                                    )
                            )  
                            .highlight(h -> h
                                    .fields("content", f -> f
                                            .preTags("<em>")
                                            .postTags("</em>")
                                    )
                            )
                            .from(0)
                            .size(top)
                    , DocumentParagraph.class
            );
            for (Hit<DocumentParagraph> hit : search.hits().hits()) {
    
    
                DocumentParagraph pd = hit.source();
                documentParagraphs.add(pd);
            }
        } catch (IOException e) {
    
    
            log.error("查询ES异常:{}", e.getMessage());
            throw new ExploException(HttpCode.ES_SEARCH_ERROR, "查询ES数据失败");
        }
        return documentParagraphs;
    }

/**
* 高亮数据提取
*/
    private DocumentParagraph highLight(Hit<DocumentParagraph> hit) {
    
    
        DocumentParagraph paragraph = hit.source();
        try {
    
    
            Map<String, List<String>> highlight = hit.highlight();
            List<String> list = highlight.get("content");
            String join = StringUtils.join(list, "");
            if (StringUtils.isNotBlank(join)) {
    
    
                paragraph.setContent(join);
                paragraph.setScore(hit.score());

            }
        } catch (Exception e) {
    
    
            log.error("获取ES高亮数据异常:{}", e.getMessage());
        }
        return paragraph;
    }
/**
*解析高亮数据
*/
 Map<String, List<String>> highlight = hit.highlight();
            List<String> list = highlight.get("content");
            String join = StringUtils.join(list, "");
            if (StringUtils.isNotBlank(join)) {
    
    
                paragraph.setContent(join);
            }

4.3. Query the data of a document and highlight keywords

Parameter introduction
ndexName: index name
id: document id

  public List<DocumentParagraph> searchDocumentByDocId(String indexName, String id) {
    
    
        List<DocumentParagraph> documentParagraphs = Lists.newArrayList();
        try {
    
    
            SearchResponse<DocumentParagraph> search = client.search(s -> s
                    .index(indexName)
                    .query(q -> q
                            .term(t -> t
                                    .field("docId")
                                    .value(id)
                            )
                    ), DocumentParagraph.class
            );
            for (Hit<DocumentParagraph> hit : search.hits().hits()) {
    
    
                DocumentParagraph pd = hit.source();
                documentParagraphs.add(pd);
            }
        } catch (IOException e) {
    
    
            log.error("查询ES异常:{}", e.getMessage());
            throw new ExploException(HttpCode.ES_SEARCH_ERROR, "查询ES数据失败");
        }
        return documentParagraphs;
    }

5.elasticsearch add and delete tools

Batch addition and deletion is also a common type

5.1. Batch increase data

Parameter introduction
ndexName: for the index name
objs: insert entity

     public boolean batchInsertDocument(String indexName, List<DocumentParagraph> objs) {
    
    
        try {
    
    
            List<BulkOperation> bulkOperationArrayList = new ArrayList<>();
            for (DocumentParagraph obj : objs) {
    
    
                bulkOperationArrayList.add(BulkOperation.of(o -> o.index(i -> i.document(obj))));
            }

            BulkResponse bulkResponse = client.bulk(b -> b.index(indexName).operations(bulkOperationArrayList));
            return true;
        } catch (IOException e) {
    
    
            log.error("数据插入ES异常:{}", e.getMessage());
            throw new ExploException(HttpCode.ES_INSERT_ERROR, "ES新增数据失败");
        }
    }

5.2. Batch delete data

Delete all data under the article
Parameter introduction
indexName: is the index name
docId: article id

    public Boolean deleteDocument(String indexName, String docId) {
    
    
        try {
    
    
            client.deleteByQuery(d -> d
                    .index(indexName)
                    .query(q -> q
                            .term(t -> t
                                    .field("docId")
                                    .value(docId)
                            )
                    )
            );
        } catch (IOException e) {
    
    
            log.error("查询ES异常:{}", e.getMessage());
        }
        return true;
    }

5. Call the test implementation

Implement index creation and data storage query examples

@Api(tags = {
    
    "ES操作"})
@RestController
@RequestMapping("/es")
public class TestEsCurdCtrl {
    
    

    @Autowired
    public ElasticsearchHandle elasticsearchHandle;


    @ApiOperation(value = "添加es文件", notes = "添加es文件")
    @GetMapping(value = "/add")
    public ResponseHandle deleteDocument(@RequestParam(value = "id") String id) {
    
    
        DocumentPartESDto doc = DocumentPartESDto.builder()
                .id(id)
                .docId(id)
                .content("这是文本内容" + id)
                .type("doc")
                .build();
        elasticsearchHandle.insertDocument("doc", doc, id);
        return ResponseHandle.SUCCESS("成功");
    }

    @ApiOperation(value = "查询es文件", notes = "查询es文件")
    @GetMapping(value = "/search")
    public ResponseHandle searchDocument(@RequestParam(value = "id") String id) {
    
    
        GetResponse<DocumentPartESDto> doc = elasticsearchHandle.searchDocument("doc", id);
        DocumentPartESDto docDto = doc.source();
        return ResponseHandle.SUCCESS(docDto);
    }

    @ApiOperation(value = "创建索引", notes = "创建索引")
    @GetMapping(value = "/index")
    public ResponseHandle createIndex(@RequestParam(value = "indexName") String indexName) {
    
    
        elasticsearchHandle.createIndex(indexName);
        return ResponseHandle.SUCCESS("成功");
    }

   /**
     * 创建mapping索引
     *
     * @param indexName
     */
         public static String esmappingV2 = "{\"properties\":{\"docId\":{\"type\":\"keyword\"},\"docName\":{\"type\":\"text\",\"analyzer\":\"ik_max_word\",\"search_analyzer\":\"ik_smart\",\"copy_to\":\"all\"},\"categoryId\":{\"type\":\"keyword\"},\"libVersionList\":{\"type\":\"keyword\"},\"content\":{\"type\":\"text\",\"analyzer\":\"ik_max_word\",\"search_analyzer\":\"ik_smart\",\"copy_to\":\"all\"},\"title\":{\"type\":\"text\",\"analyzer\":\"ik_max_word\",\"search_analyzer\":\"ik_smart\",\"copy_to\":\"all\"},\"all\":{\"type\":\"text\",\"analyzer\":\"ik_max_word\",\"search_analyzer\":\"ik_smart\"}}}";

    public void createIndexAndMapping(String indexName)  {
    
    
        try {
    
    
            JsonpMapper mapper = client._transport().jsonpMapper();
            JsonParser parser = mapper.jsonProvider().createParser(new StringReader(esmappingV2));
            client.indices().create(c -> c.
                    index(indexName)
                    .mappings(TypeMapping._DESERIALIZER.deserialize(parser, mapper))
            );
        } catch (Exception e) {
    
    
            if (StringUtils.contains(e.getMessage(), "resource_already_exists_exception")) {
    
    
                log.warn("索引存在不创建");
                return;
            }
            log.error("es新增mapping索引异常:{}", e.getMessage());
            throw new SkynetException(HttpCode.ES_INDEX_ERROR.code(), HttpCode.ES_INDEX_ERROR.message());
        }
    }
}

Summarize

The above is the content of the SpringBoot integrated Elasticsearch database. The project has been tested, and all the main classes are on it. You can refer to

Guess you like

Origin blog.csdn.net/Oaklkm/article/details/130992152