source

ElasticSearch 7.0 버전을 Spring Boot과 통합하는 방법은 무엇입니까?

ittop 2023. 9. 4. 20:38
반응형

ElasticSearch 7.0 버전을 Spring Boot과 통합하는 방법은 무엇입니까?

이미 메이븐 저장소에서 사용할 수 있는 최신 버전의 Elastic Search 라이브러리를 사용하려고 합니다.

<dependency>
    <groupId>org.elasticsearch</groupId>
    <artifactId>elasticsearch</artifactId>
    <version>7.0.0</version>
</dependency>

하지만 6.5를 가져오는 Spring Boot과 함께 7번째 버전을 어떻게 사용할 수 있는지 잘 모르겠습니다.나의 메이븐 의존성:

  <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
  </dependency>

갱신하다

Spring Boot 2.3은 Spring-Data-Elastic Search 4를 통합하여 Elastic Search 7.x를 즉시 지원합니다.곧 출시될 예정이지만 이미 사용해 볼 수 있습니다.

plugins {
  id 'org.springframework.boot' version '2.3.0.RC1'
  id 'io.spring.dependency-management' version '1.0.9.RELEASE'
}

저는 긍정적으로 테스트했고 제 테스트 시나리오는 모두 통과했기 때문에 이 방법을 꼭 추천합니다.어떤 이유로 2.3으로 업그레이드할 수 없다는 답변은 아래와 같이 하겠습니다.

이전 해결 방법(이전 버전의 원래 답변)

"Spring Data Elastic Search 4.x가 언제 출시될지 모르기 때문에 현재의 Spring Data Elastic Search 4.x와 안정적인 Spring Boot 2.1.7을 통합하는 방법을 게시합니다.Spring Repositorys 및 최신 Elastic Search로 작업하려는 경우 임시 해결책으로 사용할 수 있습니다.

종속성에 최신 탄력적 검색 클라이언트 적용(내 경우: build.gradle)

dependencies {
    //Force spring-data to use the newest elastic-search client
    //this should removed as soon as spring-data-elasticsearch:4.0.0 is released!
    implementation('org.springframework.data:spring-data-elasticsearch:4.0.0.BUILD-SNAPSHOT') {
        exclude group: 'org.elasticsearch'
        exclude group: 'org.elasticsearch.plugin'
        exclude group: 'org.elasticsearch.client'
    }

    implementation('org.elasticsearch:elasticsearch:7.3.0') { force = true }
    implementation('org.elasticsearch.client:elasticsearch-rest-high-level-client:7.3.0') { force = true }
    implementation('org.elasticsearch.client:elasticsearch-rest-client:7.3.0') { force = true }
}

Elastic Search 자동 구성 및 상태 점검 구성 요소가 호환되지 않으므로 비활성화합니다(나중에 자체 상태 점검을 구현할 수도 있음).

@SpringBootApplication(exclude = {ElasticsearchAutoConfiguration.class, ElasticSearchRestHealthIndicatorAutoConfiguration.class})
@EnableElasticsearchRepositories
public class SpringBootApp {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootApp.class, args);
    }

}

자동 구성을 비활성화했기 때문에 다음을 초기화해야 합니다.ElasticsearchRestTemplate우리자신.우리는 또한 고객을 제공하기 위해 그것을 해야 합니다.MappingElasticsearchConverter클래스 비호환성을 방지합니다.

/**
 * Manual configuration to support the newest ElasticSearch that is currently not supported by {@link org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration}.
 *
 * @author aleksanderlech
 */
@Configuration
@EnableConfigurationProperties(ElasticsearchProperties.class)
public class ElasticSearchConfiguration {

    @Primary
    @Bean
    public ElasticsearchRestTemplate elasticsearchTemplate(ElasticsearchProperties configuration) {
        var nodes =  Stream.of(configuration.getClusterNodes().split(",")).map(HttpHost::create).toArray(HttpHost[]::new);
        var client = new RestHighLevelClient(RestClient.builder(nodes));
        var converter = new CustomElasticSearchConverter(new SimpleElasticsearchMappingContext(), createConversionService());
        return new ElasticsearchRestTemplate(client, converter, new DefaultResultMapper(converter));
    }

    private DefaultConversionService createConversionService() {
        var conversionService = new DefaultConversionService();
        conversionService.addConverter(new StringToLocalDateConverter());
        return conversionService;
    }
}

사용자 지정 탄성 검색 변환기:

/**
 * Custom version of {@link MappingElasticsearchConverter} to support newest Spring Data Elasticsearch integration that supports ElasticSearch 7. Remove when Spring Data Elasticsearch 4.x is released.
 */
class CustomElasticSearchConverter extends MappingElasticsearchConverter {

    private CustomConversions conversions = new ElasticsearchCustomConversions(Collections.emptyList());

    CustomElasticSearchConverter(MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> mappingContext) {
        super(mappingContext);
        setConversions(conversions);
    }

    CustomElasticSearchConverter(MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> mappingContext, GenericConversionService conversionService) {
        super(mappingContext, conversionService);
        setConversions(conversions);
    }

    @Override
    protected <R> R readValue(@Nullable Object source, ElasticsearchPersistentProperty property,
                              TypeInformation<R> targetType) {

        if (source == null) {
            return null;
        }

        if (source instanceof List) {
            return readCollectionValue((List) source, property, targetType);
        }

        return super.readValue(source, property, targetType);
    }

    private Object readSimpleValue(@Nullable Object value, TypeInformation<?> targetType) {

        Class<?> target = targetType.getType();

        if (value == null || target == null || ClassUtils.isAssignableValue(target, value)) {
            return value;
        }

        if (conversions.hasCustomReadTarget(value.getClass(), target)) {
            return getConversionService().convert(value, target);
        }

        if (Enum.class.isAssignableFrom(target)) {
            return Enum.valueOf((Class<Enum>) target, value.toString());
        }

        return getConversionService().convert(value, target);
    }


    private <R> R readCollectionValue(@Nullable List<?> source, ElasticsearchPersistentProperty property,
                                      TypeInformation<R> targetType) {

        if (source == null) {
            return null;
        }

        Collection<Object> target = createCollectionForValue(targetType, source.size());

        for (Object value : source) {

            if (isSimpleType(value)) {
                target.add(
                        readSimpleValue(value, targetType.getComponentType() != null ? targetType.getComponentType() : targetType));
            } else {

                if (value instanceof List) {
                    target.add(readValue(value, property, property.getTypeInformation().getActualType()));
                } else {
                    target.add(readEntity(computeGenericValueTypeForRead(property, value), (Map) value));
                }
            }
        }

        return (R) target;
    }

    private Collection<Object> createCollectionForValue(TypeInformation<?> collectionTypeInformation, int size) {

        Class<?> collectionType = collectionTypeInformation.isCollectionLike()//
                ? collectionTypeInformation.getType() //
                : List.class;

        TypeInformation<?> componentType = collectionTypeInformation.getComponentType() != null //
                ? collectionTypeInformation.getComponentType() //
                : ClassTypeInformation.OBJECT;

        return collectionTypeInformation.getType().isArray() //
                ? new ArrayList<>(size) //
                : CollectionFactory.createCollection(collectionType, componentType.getType(), size);
    }

    private ElasticsearchPersistentEntity<?> computeGenericValueTypeForRead(ElasticsearchPersistentProperty property,
                                                                            Object value) {

        return ClassTypeInformation.OBJECT.equals(property.getTypeInformation().getActualType())
                ? getMappingContext().getRequiredPersistentEntity(value.getClass())
                : getMappingContext().getRequiredPersistentEntity(property.getTypeInformation().getActualType());
    }

    private boolean isSimpleType(Object value) {
        return isSimpleType(value.getClass());
    }

    private boolean isSimpleType(Class<?> type) {
        return conversions.isSimpleType(type);
    }

}

Spring Boot 2.1.2 Kotlin을 사용하는 사람이 있다면 다음 코드가 도움이 될 수 있습니다.방금 @Alexander Lech 답변에서 번역했는데, 약간의 변경 사항이 있습니다.

Alexanders 답변의 첫 번째 변경 사항:

@SpringBootApplication(exclude = [ElasticsearchAutoConfiguration::class, 
ElasticsearchDataAutoConfiguration::class])

제외해야 했습니다.ElasticsearchDataAutoConfiguration효과가 있을 거예요

둘째: 우리는 Kotlin을 사용하고 사용자 지정 변환기는 많은 코드이기 때문에, Kotlin으로 번역하는 것이 누군가에게 도움이 될 것입니다.

class CustomElasticSearchConverter(mappingContext: MappingContext<out ElasticsearchPersistentEntity<*>, ElasticsearchPersistentProperty>, customConversionService: GenericConversionService?) : MappingElasticsearchConverter(mappingContext, customConversionService) {

    private val conversionsNew = ElasticsearchCustomConversions(emptyList<Any>())

    init {
        setConversions(conversionsNew)
    }

    override fun <R : Any?> readValue(source: Any?, property: ElasticsearchPersistentProperty, targetType: TypeInformation<R>): R? {
        if (source == null) {
            return null
        }

        if (source is Collection<*>) {
            return readCollectionValue(source, property, targetType) as R?;
        }

        return super.readValue(source, property, targetType);
    }

    private fun readCollectionValue(source: Collection<*>?, property: ElasticsearchPersistentProperty, targetType: TypeInformation<*>): Any? {

        if (source == null) {
            return null
        }

        val target = createCollectionForValue(targetType, source.size)

        for (value in source) {
            require(value != null) { "value must not be null" }

            if (isSimpleType(value)) {
                target.add(readSimpleValue(value, if (targetType.componentType != null) targetType.componentType!! else targetType))
            } else {
                if (value is MutableCollection<*>) {
                    target.add(readValue(value, property, property.typeInformation.actualType as TypeInformation<out Any>))
                } else {
                    @Suppress("UNCHECKED_CAST")
                    target.add(readEntity(computeGenericValueTypeForRead(property, value), value as MutableMap<String, Any>?))
                }
            }
        }

        return target
    }

    private fun readSimpleValue(value: Any?, targetType: TypeInformation<*>): Any? {

        val target = targetType.type

        @Suppress("SENSELESS_COMPARISON")
        if (value == null || target == null || ClassUtils.isAssignableValue(target, value)) {
            return value
        }

        if (conversionsNew.hasCustomReadTarget(value.javaClass, target)) {
            return conversionService.convert(value, target)
        }

        @Suppress("UNCHECKED_CAST")
        return when {
            Enum::class.java.isAssignableFrom(target) -> enumByName(target as Class<Enum<*>>, value.toString())
            else -> conversionService.convert(value, target)
        }
    }

    private fun enumByName(target: Class<Enum<*>>, name: String): Enum<*> {
        val enumValue = target.enumConstants.find { it.name == name }
        require(enumValue != null) { "no enum value found for name $name and targetClass $target" }
        return enumValue
    }

    private fun createCollectionForValue(collectionTypeInformation: TypeInformation<*>, size: Int): MutableCollection<Any?> {

        val collectionType = when {
            collectionTypeInformation.isCollectionLike -> collectionTypeInformation.type
            else -> MutableList::class.java
        }

        val componentType = when {
            collectionTypeInformation.componentType != null -> collectionTypeInformation.componentType
            else -> ClassTypeInformation.OBJECT
        }

        return when {
            collectionTypeInformation.type.isArray -> ArrayList(size)
            else -> CollectionFactory.createCollection(collectionType, componentType!!.type, size)
        }
    }

    private fun computeGenericValueTypeForRead(property: ElasticsearchPersistentProperty, value: Any): ElasticsearchPersistentEntity<*> {

        return when {
            ClassTypeInformation.OBJECT == property.typeInformation.actualType -> mappingContext.getRequiredPersistentEntity(value.javaClass)
            else -> mappingContext.getRequiredPersistentEntity(property.typeInformation.actualType!!)
        }
    }

    private fun isSimpleType(value: Any): Boolean {
        return isSimpleType(value.javaClass)
    }

    private fun isSimpleType(type: Class<*>): Boolean {
        return conversionsNew.isSimpleType(type)
    }

}

이후 일부 리포지토리 쿼리 문제가 해결됩니다.또한 사용하지 않도록 주의하십시오.spring-boot-starter-data-elasticsearch그렇지만spring-data-elasticsearch:4.0.0.BUILD-SNAPSHOT(시간이 좀 걸렸습니다.)

네, 코드는 못생겼지만, 그 후에spring-data-elasticsearch:4.0.0개봉하면 버려도 됩니다.

언급URL : https://stackoverflow.com/questions/55761140/how-to-integrate-elasticsearch-7-0-version-with-spring-boot

반응형