Kotlin + SpringBoot + Elasticsearch
๐Ÿ™ˆ

Kotlin + SpringBoot + Elasticsearch

Created
Jun 12, 2024 12:51 AM
Last edited time
Last updated June 14, 2024
Tags
Backend
Spring
Kotlin
ElasticSearch
Language
kotlin
Java
URL

Intro::

์ฝ”ํ‹€๋ฆฐ ์Šคํ”„๋ง์„ ํ™œ์šฉํ•œ ์—˜๋ผ์Šคํ‹ฑ์„œ์น˜ ๋„ํ๋จผํŠธ ์ธ๋ฑ์‹ฑ ํ”„๋กœ์ ํŠธ ์ •๋ฆฌ์ž…๋‹ˆ๋‹ค. ์—˜๋ผ์Šคํ‹ฑ์„œ์น˜ ์‹ค๋ฌด๊ฐ€์ด๋“œ์—์„œ ์ œ๊ณตํ•˜๋Š” ์Šค๋ƒ…์ƒท์„ ์‚ฌ์šฉํ•˜์˜€์Šต๋‹ˆ๋‹ค.
ย 

์ดˆ๊ธฐ ์„ค์ •

docker-compose.yml

services: es01: image: docker.elastic.co/elasticsearch/elasticsearch:7.8.1 container_name: es01 environment: - cluster.name=cluster - node.name=es01 - network.host=0.0.0.0 - http.port=9200 - transport.tcp.port=9300 - cluster.initial_master_nodes=es01 - path.repo=/es/book_backup/search_example - bootstrap.memory_lock=true - "ES_JAVA_OPTS=-Xms512m -Xmx512m" ulimits: memlock: soft: -1 hard: -1 volumes: - data01:/usr/share/elasticsearch/data - ./search_example:/es/book_backup/search_example ports: - 9200:9200 - 9300:9300 networks: - elastic kibana: image: docker.elastic.co/kibana/kibana:7.8.1 container_name: kibana restart: always environment: ELASTICSEARCH_HOSTS: http://es01:9200 ports: - 5601:5601 depends_on: - es01 networks: - elastic volumes: data01: driver: local networks: elastic: driver: bridge

build.gradle.kts

plugins { id("org.springframework.boot") version "3.3.0" id("io.spring.dependency-management") version "1.1.5" kotlin("jvm") version "1.9.24" kotlin("plugin.spring") version "1.9.24" } group = "elastic" version = "0.0.1-SNAPSHOT" java { toolchain { languageVersion = JavaLanguageVersion.of(17) } } repositories { mavenCentral() } dependencies { implementation("org.elasticsearch.client:elasticsearch-rest-high-level-client:7.8.1") implementation("org.springframework.boot:spring-boot-starter-web") implementation("com.fasterxml.jackson.module:jackson-module-kotlin") implementation("org.jetbrains.kotlin:kotlin-reflect") implementation("org.springframework.boot:spring-boot-starter-validation") developmentOnly("org.springframework.boot:spring-boot-devtools") testImplementation("org.springframework.boot:spring-boot-starter-test") testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") testRuntimeOnly("org.junit.platform:junit-platform-launcher") } kotlin { compilerOptions { freeCompilerArgs.addAll("-Xjsr305=strict") } } tasks.withType<Test> { useJUnitPlatform() }

Elasticsearch

๋„์ปค ์ปจํ…Œ์ด๋„ˆ๋กœ ์‹คํ–‰ํ•œ Elasticsearch์˜ ๋ฒ„์ „์ด 7.8.1์ธ ๊ฒฝ์šฐ, ๋ณด์•ˆ ์„ค์ •์ด ๊ธฐ๋ณธ์ ์œผ๋กœ ํ™œ์„ฑํ™”๋˜์–ด ์žˆ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. 8๋ฒ„์ „ ์ด์ƒ๋ถ€ํ„ฐ๋Š” ๋ณด์•ˆ ์„ค์ •์ด ๊ธฐ๋ณธ์ ์œผ๋กœ ํ™œ์„ฑํ™”๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ ์ฃผ์˜๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ Elasticsearch๋Š” ๋ฒ„์ „์— ๋Œ€ํ•œ ์ข…์†์„ฑ์ด ๊ฐ•ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฒ„์ „ ํ˜ธํ™˜์„ฑ์— ์œ ์˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
์—˜๋ผ์Šคํ‹ฑ์„œ์น˜๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ํฌ๊ฒŒ ์„ธ๊ฐ€์ง€๊ฐ€ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.
  1. spring data elasticsearch
  1. elasitcsearch-rest-client
  1. rest-high-level-client
ํ•ด๋‹น ํ”„๋กœ์ ํŠธ์—์„œ๋Š” rest-high-level-client ๋ฅผ ์‚ฌ์šฉํ•˜์˜€์Šต๋‹ˆ๋‹ค.
// ElasticsearchConfig ํŒŒ์ผ package elastic.elasticSpring.core.config import org.apache.http.HttpHost import org.apache.http.impl.nio.client.HttpAsyncClientBuilder import org.elasticsearch.client.RestClient import org.elasticsearch.client.RestHighLevelClient import org.springframework.beans.factory.annotation.Value import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration @Configuration class ElasticsearchConfig { @Value("\${elasticsearch.host}") lateinit var host: String @Value("\${elasticsearch.port}") lateinit var esPort: String @Bean fun restHighLevelClient(): RestHighLevelClient { return RestHighLevelClient( RestClient.builder( HttpHost(host, esPort.toInt(), "http") ).setHttpClientConfigCallback { httpClientBuilder: HttpAsyncClientBuilder -> httpClientBuilder } ) } }
ย 

๊ตฌํ˜„

๋‹จ์ผ ๋„ํ๋จผํŠธ ์ธ๋ฑ์‹ฑ

fun add(movie: Movie): IndexResponse { val indexRequest = IndexRequest(index) .source(objectMapper.writeValueAsString(movie), XContentType.JSON) if (!movie.id.isNullOrEmpty()) { indexRequest.id(movie.id) } return restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT) }

bulk API ๋ฅผ ํ†ตํ•œ ๋„ํ๋จผํŠธ ์ธ๋ฑ์‹ฑ

fun bulkIndex(movieList: List<Movie>): BulkResponse { val bulkRequest = BulkRequest() for (movie in movieList) { val indexRequest = IndexRequest(index) .source(objectMapper.writeValueAsString(movie), XContentType.JSON) if (!movie.id.isNullOrEmpty()) { indexRequest.id(movie.id) } bulkRequest.add(indexRequest) } return restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT) }

_update_by_query๋ฅผ ํ†ตํ•œ ๋„ํ๋จผํŠธ ์ˆ˜์ •

fun addGenre(movieDto: MovieAddGenreRequestDto): BulkByScrollResponse { val updateByQueryRequest = UpdateByQueryRequest(index) val script = Script( ScriptType.INLINE, "painless", "ctx._source.genreAlt.add(params.genre)", mapOf("genre" to movieDto.genre) ) val query = QueryBuilders.matchQuery("movieNm", movieDto.movieNm) updateByQueryRequest.setScript(script) updateByQueryRequest.setQuery(query) return restHighLevelClient.updateByQuery(updateByQueryRequest, RequestOptions.DEFAULT) }

References::

ย 

Loading Comments...