elevne's Study Note
Elasticsearch on Java Spring + (Search Queries...) 본문
Java Spring 프레임워크에서 Elasticsearch를 사용하여 검색 API를 구축하고자 하였다. 우선 Spring Initializr에서 다음 Dependencies를 추가해주고 Gradle Project를 생성해주었다.
프로젝트 폴더를 열고 필요한 파일들을 Gradle에서 자동으로 다운로드 되게끔 한다. application.properties 파일에 다음과 같이 작성해주었다.
elasticsearch.url=localhost:9200
logging.level.org.springframework.data.elasticsearch.client.WIRE=TRACE
Elasticsearch의 default 포트 값인 9200을 그대로 사용하였기에 위와 같이 적어주었고, 밑의 logging.level.org.springframework.data.elasticsearch.client.WIRE=TRACE를 적어주게 되면 Eleasticsearch에 전송하는 쿼리를 로그에 출력하여 볼 수 있다.
그 후, 프로젝트에서 가장 먼저 configuration 패키지를 생성한 후 그 안에 Configuration 클래스를 다음과 같이 적어주었다.
@Configuration
@EnableElasticsearchRepositories(basePackages = "com.test.elasticsearch.repository")
@ComponentScan(basePackages = {"com.test.elasticsearch"})
public class Config extends AbstractElasticsearchConfiguration {
@Value("${elasticsearch.url}")
public String elasticsearchUrl;
@Bean
@Override
public RestHighLevelClient elasticsearchClient() {
final ClientConfiguration config = ClientConfiguration.builder().connectedTo(elasticsearchUrl).withConnectTimeout(30000).build();
return RestClients.create(config).rest();
}
}
EnableElasticsearchRepositories 어노테이션을 사용하여 작성한 Repository를 자동으로 Scan 할 수 있게끔 한다.
또, Elasticsearch에 접근할 때는 RestHighLevelClient 객체를 만들어서 접근하게 된다. 위와 같은 코드로 Bean에 RestHighLevelClient 객체를 등록해준다. 위 코드에서는 ClientConfiguration 객체를 만들고, 이를 위에 Value로 등록해둔 elasticesarch.url에 연결해준다. 그 후, RestClients.create 함수를 사용하여 Elasticsearch에 연결하기 위한 RestHighLevelClient를 만들어줄 수 있다.
이후, Elasticsearch Index를 자동적으로 색인/삭제할 수 있게끔 코드를 작성하였다. 우선 Json Index mapping, setting 파이를을 읽어올 수 있게끔 Util이라는 Class 밑에 다음과 같은 함수를 작성해주었다.
public static String loadAsString(final String path) {
try {
final File resource = new ClassPathResource(path).getFile();
return new String(Files.readAllBytes(resource.toPath()));
} catch (final Exception e) {
e.printStackTrace();
return null;
}
}
Java에서는 File 클래스를 통해 파일과 디렉토리를 다룰 수 있다. 이후에 만들어줄 Elasticsearch Index setting, mapping Json 파일드을 불러오기 위해 File 클래스를 사용한다. File은 readAllLines, readAllBytes 등의 함수로 그 내용을 읽어올 수 있다.
파일을 읽어올 수 있게끔 하였으니 그 파일들을 작성해본다. Index의 mapping 설정에 대한 내용을 담고 있는 Json 파일과 Index의 Setting에 대한 내용들을 담고 있는 Json 파일 두 개를 만들어주면 된다.
index1.json
{
"properties" : {
"field1": {
"type": "text",
"copy_to": "field2"
},
"field2": {
"type": "text",
"analyzer" : "ngram_analyzer",
"search_analyzer" : "ngram_analyzer"
}
}
index1_settings.json
{
"index" : {
"analysis" : {
"analyzer" : {
"ngram_analyzer" : {
"type" : "custom",
"tokenizer" : "ngram_tokenizer",
"filter" : [
"lowercase", "trim"
]
},
"html_analyzer" : {
"type" : "custom",
"tokenizer" : "standard",
"filter" : [ "lowercase" ],
"char_filter" : [ "html_strip" ]
}
},
"tokenizer" : {
"ngram_tokenizer" : {
"type" : "nGram",
"min_gram" : "1",
"max_gram" : "50",
"decompound_mode" : "mixed",
"token_chars" : [ "letter", "digit", "punctuation", "symbol" ]
}
},
"max_ngram_diff" : 50,
"number_of_shards" : 6,
"number_of_replicas" : 1
}
}
위와 같이 작성해줄 수 있다. Elasticsearch에서 기본적으로 제공하는 N-Gram, Html-Strip 등을 사용할 수 있게끔 설정에 적어주었다. 설정에 적어둔 analyzer들을 field를 정의할 때 analyzer 혹은 search_analyzer 안에 넣어줄 수 있다. analyzer은 document들이 indexing 될 때 설정해둔 analyzer가 적용되어 저장되게 하며, search_analyzer은 검색을 진행할 때 검색어에 대해서 analyzer를 적용시켜 검색할 수 있게끔 한다.
그 후 Service 패키지를 만들고 그 안에 IndexService라는 이름의 Class를 만들어준다.
@PostConstruct
public void createIndices() { createIndexes(false); }
private String loadMappings(String indexName) {
final String mappings = Util.loadAsString("static/mappings/"+indexName+".json");
if (mappings == null) {
return null;
}
return mappings;
}
public void createIndexes(final boolean deleteExisting, String indexName) {
String settings = Util.loadAsString("static/" + indexName + "_settings.json");
try {
boolean indexExists = client.indices().exists(new GetIndexRequest(indexName), RequestOptions.DEFAULT);
if (indexExists) {
if (!deleteExisting) {
throw new Exception();
}
client.indices().delete(new DeleteIndexRequest(indexName), RequestOptions.DEFAULT);
}
CreateIndexRequest createIndexRequest = new CreateIndexRequest(indexName);
createIndexRequest.settings(settings, XContentType.JSON);
String mappings = loadMappings(indexName);
if (mappings != null) {
createIndexRequest.mapping(mappings, XContentType.JSON);
}
client.indices().create(createIndexRequest, RequestOptions.DEFAULT);
} catch (Exception e) {
e.printStackTrace();
}
}
loadMappings 함수로 이전에 만든 Util 클래스 안의 파일을 읽어올 수 있는 함수를 사용할 수 있게끔 한다. Index를 삭제/생성하는데에는 Configuration에서 정의한 client가 필요하다. client.indices().exist 함수를 통해 생성하려는 index가 이미 Elasticsearch 내에 존재하는지 확인할 수 있다. 이는 Boolean 값으로 return 된다. client.indices().delete 함수를 통해 존재하는 index를 삭제할 수 있다. 생성하는 것은 CreateIndexRequest 클래스를 사용하게 된다. 이 CreateIndexRequest 개체에 .settings 함수를 통해 setting 파일을 읽어와 얻은 String 데이터를 넣어주고, mapping 함수를 통해 mapping을 설정해준 데이터도 넣어준다. 그 후, client.indices.create() 함수를 통해서 새로운 인덱스를 원하는 설정, 매핑과 함께 생성해줄 수 있다.
위에서는 PostConstruct 어노테이션을 사용하였다. 이 어노테이션은 의존성 주입이 이루어진 이후 초기화를 수행해주는 메서드이다. @PostConstruct가 붙은 것은 Service를 수행하기 전에 발생하며, 다른 리소스에서 호출되지 않는다고 해도 실행된다. 이 어노테이션과 함께 Index를 생성/삭제해주는 함수를 넣어주며 프로젝트 실행과 함께 Index 생성/삭제를 할 수 있게끔 하였다. (한 번 생성해준 Index에 변경없이 계속 쓰고자 한다면 안의 값을 false로 넣어주면 되겠다.)
이제 Index에서 실질적으로 검색을 진행하기 위해 필요한 코드들을 작성할 차례이다. 그 전에 Elasticsearch에서 사용할 수 있는 검색쿼리들의 종류를 몇 가지 알아보고자 하였다.
1. Match_all Query:
Match_all Query는 별다른 조건 없이 해당 인데스의 모든 Document를 검색하는 쿼리이다.
2. Match Query:
가장 일반적인 쿼리로, 필드와 검색어를 전달하면 해당 필드에서 해당 검색어가 포함된 문서를 반환한다.
GET index명/_search
{
"query": {
"match": {
"field명": "검색어"
}
}
}
3. Bool Query:
본문 검색에서 여러 쿼리를 조합할 수 있게끔 해주는 쿼리이다. Must, Must_Not, Should, Filter 네 가지의 조건 중 하나를 넣는 쿼리마다 걸어주어 다양한 조건의 검색을 진행할 수 있다.
GET index명/_search
{
"query": {
"bool": {
"must": [
{ 쿼리 }, …
],
"must_not": [
{ 쿼리 }, …
],
"should": [
{ 쿼리 }, …
],
"filter": [
{ 쿼리 }, …
]
}
}
}
4. Range Query:
숫자나 날짜 형식의 데이터에 대해 범위를 지정해주어 해당 범위의 값들을 지닌 문서를 반환한다. GTE(Greater then or equivalent), LTE(Less then or equivalent)를 범위 지정을 위해 사용한다.
GET index명/_search
{
"query": {
"range": {
"field명": {
"gte": 값1,
"lt": 값2
}
}
}
}
이 외에도 Elasticsearch에서 제공하는 다양한 검색 기능들이 있지만 우선 이 정도만 알아보았다. 위 검색쿼리들을 Java에서 Elasticsearch API를 사용하여 간단하게 구현해볼 예정이다.
출처:
'ETC > Elasticsearch' 카테고리의 다른 글
Logstash 활용 (0) | 2022.12.23 |
---|---|
Elasticsearch on Java Spring (2) (0) | 2022.10.27 |
Elasticsearch 정리! (0) | 2022.10.24 |