SSAFY 특화 프로젝트를 수행하면서 지리 정보를 다뤄야 할 일이 생겼다. Elasticsearch(ES)에 지리 정보를 다룰 수 있는 기능들이 많아서 이를 활용하기로 했다. Spring에서 기능을 사용하는 방법을 알아보고 활용해보자.
Spring Data Elasticsearch provides repository support for the Elasticsearch database. It eases development of applications with a consistent programming model that need to access Elasticsearch data sources.
위 글은 공식문서의 소개를 그대로 가져온 것이다.번역하면 Spring Data Elasticsearch는 ES에 대한 repository를 지원하며, Spring에서 제공하는 다른 Data 라이브러리(JPA, Redis 등)와 공통된 방식으로 사용할 수 있다고 한다.
기본적인 설정법과 사용법은 공식문서에 잘 나와 있으니, 이번 글에서는 Spring Data Elasticsearch에서 ES의 geo_point를 어떻게 사용할 수 있는지 알아보겠다.
Spring Data Elasticsearch에서는 JPA Entity처럼 애노테이션을 사용해서 ES의 Document를 정의한다.
@Getter
@Document(indexName = "books")
public class Book {
@Id
private String id;
@Field(type = FieldType.Text)
private String name;
@Field(type = FieldType.Text)
private String summary;
@Field(type = FieldType.Integer)
private Integer price;
public Book(String name, String summary, Integer price) {
this.name = name;
this.summary = summary;
this.price = price;
}
}
이렇게 Document 클래스를 생성한다고 Index가 생성되는건 아니다. Repository도 함께 만들어 주어야 한다.
public interface BookRepository extends Repository<Book, String> {
}
Springboot 실행 시 org.springframework.data.repository.Repository
인터페이스를 상속 받은 인터페이스를 자동으로 탐지해서 Repository의 데이터 타입이 @Document일 경우 index 존재 유무를 확인하고 없으면 생성한다.
ES에서 geo_point 기능을 사용하기 위해서는 해당 필드의 매핑 타입이 "geo_point"로 선언되어야 한다. "geo_point" 값들은 다양한 방식으로 입력 가능한데 문서에 따르면 object, text, geohash, 실수 배열 등 정말 다양한 형식으로 입력이 가능하다.
Spring에도 geo_point를 위한 클래스가 있다. 바로 org.springframework.data.geo.Point
이다. 멤버로 lat(위도), lon(경도)를 가지고 있는 데이터 타입이다. 이를 Docuement의 필드로 지정하면 바로 적용될까?
@Document(indexName = "markers")
public class Marker {
@Id
private String id;
private Point point;
}
안타깝게도 아래와 같이 매핑이 생성되지 않는다.
GET http://localhost:9200/markers/_mapping?pretty
{
"markers" : {
"mappings" : {
"properties" : {
"_class" : {
"type" : "keyword",
"index" : false,
"doc_values" : false
}
}
}
}
}
Spring의 Point를 geo_point로 매핑하기 위해서는 ES의 @GeoPointField 애노테이션을 사용해야 한다.
@Document(indexName = "markers")
public class Marker {
// ...
@GeoPointField
private Point point;
}
GET http://localhost:9200/markers/_mapping?pretty
{
"markers" : {
"mappings" : {
"properties" : {
"_class" : {
"type" : "keyword",
"index" : false,
"doc_values" : false
},
"point" : {
"type" : "geo_point"
}
}
}
}
}
위처럼 이제 잘 매핑되는 것을 볼 수 있다.
애노테이션을 사용하고 싶지 않다면 ES에서 지원하는 org.springframework.data.elasticsearch.core.geo.GeoPoint
를 사용하면 된다.
@Document(indexName = "markers")
public class Marker {
// ...
private GeoPoint point;
}