SpringBoot は Elasticsearch8.x (7) を統合しました | (完全な例を使用した Java API クライアントの新しいバージョン)

SpringBoot は Elasticsearch8.x (7) を統合しました | (完全な例を使用した Java API クライアントの新しいバージョン)


各章
へのリンク 第 1 章へのリンク: SpringBoot と Elasticsearch7.x の統合 (1) | (関数実装の作成、削除、変更およびチェック)
第 2 章へのリンク: Elasticsearch7.x を統合するための SpringBoot へのリンク (2) | (複雑なクエリ)
へのリンク第 3 章: SpringBoot と Elasticsearch7.x の統合 (3) | (集約のインデックス集約クエリ)
第 4 章のリンク: SpringBoot と Elasticsearch7.x の統合 (4) | (集約のバケット集約クエリ)
第 5 章のリンク: SpringBoot と Elasticsearch7.x の統合 ( 5) | match、match_phrase の違い)
第 6 章のリンク: SpringBoot に統合された Elasticsearch8.x (6) | (新しいバージョンの Java API クライアントを使用)
第 7 章のリンク: SpringBoot に統合された Elasticsearch8.x (7) | (新しいバージョンの Java API クライアントは完全な例)

序文

Es7.15 バージョンの後、es はその高度なクライアント RestHighLevelClient を非推奨として正式にマークしました。同時に、新しい Java API クライアントである Elasticsearch Java API Client がリリースされました。これは、Elasticsearch 8.0 以降のバージョンで正式に推奨されるクライアントになります。

1. プロジェクトの依存関係

独自の elasticsearch 依存関係を持つ springboot バージョンのアプリケーションはありません。組み込みバージョンは 7.x である必要があるため、elasticsearch8.x 依存関係は別途導入されます。

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

2、スプリングブート統合の実装

1. ElasticSearchConfig 構成の実装

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.elasticsearchツールクラス

ElasticsearchHandle ツール クラスは、主に elasticsearch のインデックスと、データに対応する追加、削除、変更、確認のメソッドをカプセル化します。

@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. ES ストレージのデータ構造を定義する

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クエリツールクラス

主にクエリ シナリオを使用するため、クエリを改善する方法をいくつか紹介します。

4.1. 条件を満たすデータをクエリする

パラメータの紹介
ndexName: インデックスの名前
query: クエリの内容
top: クエリの項目数

   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. 特定のグループで条件を満たすデータをクエリする

パラメータの概要
ndexName: インデックス名
query: クエリの内容
categoryId: それらのカテゴリの下にあるクエリ データ
top: クエリ番号

 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. ドキュメントのデータをクエリしてキーワードを強調表示する

パラメータの導入
ndexName: インデックス名
id: ドキュメント 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 追加および削除ツール

一括追加・削除もよくあるタイプ

5.1. 一括増加データ

パラメータの導入
ndexName: インデックス名の場合
objs: エンティティを挿入

     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. データの一括削除

記事の下のすべてのデータを削除します。
パラメータの概要
IndexName: インデックス名です。
docId: 記事 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. テスト実装を呼び出す

インデックス作成とデータストレージクエリの実装例

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

要約する

上記は SpringBoot 統合 Elasticsearch データベースの内容です。プロジェクトはテストされており、すべての主要クラスがその上にあります。参照できます

おすすめ

転載: blog.csdn.net/Oaklkm/article/details/130992152