SpringBoot integró Elasticsearch8.x (7) | (nueva versión de Java API Client usando un ejemplo completo)

SpringBoot integró Elasticsearch8.x (7) | (nueva versión de Java API Client usando un ejemplo completo)


Capítulos
Enlaces al Capítulo 1: Integración de SpringBoot con Elasticsearch7.x (1) | (Realización de funciones CRUD)
Enlaces al Capítulo 2: Enlaces a SpringBoot para integrar Elasticsearch7.x (2) | (Consultas complejas)
Enlaces al Capítulo 3: Integración de SpringBoot con Elasticsearch7.x (3) | (consulta de agregación de índice de agregaciones)
Enlace del capítulo 4: SpringBoot integrado Elasticsearch7.x (4) | (consulta de agregación de depósito de agregaciones)
Enlace del capítulo 5: SpringBoot integrado Elasticsearch7.x (5) | coincidencia, match_phrase difference)
Enlace del capítulo 6: SpringBoot integrado Elasticsearch8.x (6) | (nueva versión Java API Client uso)
Capítulo 7 enlace: SpringBoot integrado Elasticsearch8.x (7) | (nueva versión Java API Client utiliza un ejemplo completo)

prefacio

Después de la versión Es7.15, es marcó oficialmente su cliente avanzado RestHighLevelClient como obsoleto. Al mismo tiempo, se lanzó un nuevo cliente API Java, Elasticsearch Java API Client, que también se convertirá en el cliente recomendado oficialmente en Elasticsearch8.0 y versiones posteriores.

1. Dependencias del proyecto

No existe una aplicación de la versión springboot con su propia dependencia de elasticsearch. La versión incorporada debe ser 7.x, por lo que la dependencia de elasticsearch8.x se introduce por separado.

	  <!--   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>

Dos, implementación de integración springboot

1. Implementación de la configuración de ElasticSearchConfig

Inyectar cliente elasticsearch

  @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. clase de herramienta de búsqueda elástica

La clase de herramienta ElasticsearchHandle encapsula principalmente el índice de elasticsearch y algunos métodos para agregar, eliminar, modificar y verificar los datos correspondientes.

@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. Definir una estructura de datos de almacenamiento ES

Definir una entidad para almacenar datos en ES

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

    private String id;

    private String docId;

    private String kgId;

    private String content;

    private String type;
}

4.clase de herramienta de consulta de búsqueda elástica

Dado que usamos principalmente escenarios de consulta, aquí hay algunas formas de mejorar la consulta

4.1 Datos de consulta que cumplen las condiciones

Introducción de parámetros
ndexName: el nombre de la
consulta de índice: el contenido de la consulta
top: el número de elementos de consulta

   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 Datos de consulta que cumplen las condiciones bajo ciertos grupos

Introducción de parámetros
ndexName: consulta de nombre de índice
: contenido de consulta
categoryId: datos de consulta en esas categorías
top: número de consulta

 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 Consultar los datos de un documento y resaltar palabras clave

Introducción de parámetros
ndexName:
ID de nombre de índice: ID de documento

  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.Herramientas para agregar y eliminar de elasticsearch

La adición y eliminación por lotes también es un tipo común

5.1 Datos de incremento de lotes

Introducción de parámetros
ndexName: para el nombre del índice
objs: insertar entidad

     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 Eliminar datos por lotes

Eliminar todos los datos del artículo
Introducción de parámetros
indexName: es el nombre del índice
docId: id del artículo

    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. Llame a la implementación de prueba

Implementar ejemplos de consultas de creación de índices y almacenamiento de datos

@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());
        }
    }
}

Resumir

Lo anterior es el contenido de la base de datos Elasticsearch integrada de SpringBoot. El proyecto ha sido probado y todas las clases principales están en él. Puedes referirte a

Supongo que te gusta

Origin blog.csdn.net/Oaklkm/article/details/130992152
Recomendado
Clasificación