Elasticsearch 7.17 기반으로 작성했습니다.
geo_point
필드 타입은 위도와 경도를 쌍으로 가진다.
위치 기반 쿼리를 이용해 반경 내 쿼리, 위치 기반 집계, 위치별 정렬 등을 사용할 수 있다.
이 중에서도 오늘은 정렬 기능
을 활용해본다.
간단한 스키마를 작성한다.
PUT http://localhost:9200/restaurant-index
{
"mappings": {
"dynamic": "strict",
"properties": {
"id": {
"type": "long"
},
"name": {
"type": "keyword"
},
"location": {
"type": "geo_point"
},
"rate": {
"type": "double"
}
}
},
"settings": {
"index": {
"number_of_shards": "1",
"number_of_replicas": "0"
}
}
}
총 6개의 음식점 데이터를 색인한다.
구글맵에서 경도와 위도를 직접 찾아서 기입했다.
임의로 찾은 가게들이다. (방문한 적 없음 🙅)
POST http://localhost:9200/_bulk
{"index":{"_index":"restaurant-index","_id":1}}
{"name":"맥도날드서울시청점","location":{"lat":37.566465,"lon":126.978116},"rate":3.9}
{"index":{"_index":"restaurant-index","_id":2}}
{"name":"남포면옥","location":{"lat":37.5672,"lon":126.9819},"rate":3.7}
{"index":{"_index":"restaurant-index","_id":3}}
{"name":"청진옥","location":{"lat":37.5719,"lon":126.9795},"rate":3.9}
{"index":{"_index":"restaurant-index","_id":4}}
{"name":"명동칼국수","location":{"lat":37.5688,"lon":126.9763},"rate":4.1}
{"index":{"_index":"restaurant-index","_id":5}}
{"name":"암소서울","location":{"lat":37.5699,"lon":126.9729},"rate":4.6}
{"index":{"_index":"restaurant-index","_id":6}}
{"name":"무교동북어국집","location":{"lat":37.5679,"lon":126.9799},"rate":4.5}
GET http://localhost:9200/restaurant-index/_search
{
"query": {
"bool": {
"must": {
"match_all": {}
}
}
},
"from": 0,
"size": 10,
"sort": [
{
"_geo_distance": {
"location": {
"lat": 37.5692,
"lon": 126.9787
},
"order": "asc",
"unit": "m",
"mode": "min"
}
}
],
"aggs": {}
}
쿼리 결과는 아래와 같다.
{
...
"hits": {
"total": {
"value": 6,
...
},
"hits": [
{
...
"_id": "6",
"_score": null,
"_source": {
"name": "무교동북어국집",
...
},
"sort": [
179.11076153612436
]
},
{
...
"_id": "4",
"_score": null,
"_source": {
"name": "명동칼국수",
...
},
"sort": [
253.22551326764062
]
},
...
]
}
}
geo_distance.location
으로 넣은 위치는 청계천
이다. (아래 사진 참조)
청계천으로부터 직선거리로 음식점들의 거리를 계산하고 가까운 순(asc
)으로 정렬한다.
검색 결과는 무교동북어국집
> 명동칼국수
> 맥도날드서울시청점
> 청진옥
> 남포면옥
> 암소서울
순서로 정렬된다.
검색 결과 항목 중에 sort
는 실제 직선거리를 나타낸다.(여기서는 meter 단위)
GET http://localhost:9200/restaurant-index/_search
{
"query": {
"bool": {
"must": {
"match_all": {}
}
}
},
"from": 0,
"size": 10,
"sort": [
{
"_script": {
"type": "number",
"script": {
"lang": "painless",
"source": "doc['location'].arcDistance(params.lat, params.lon)",
"params": {
"lat": 37.5692,
"lon": 126.9787
}
},
"order": "asc"
}
}
],
"aggs": {}
}
arcDistance
를 활용한 스크립트 정렬방식이다.
앞서 살펴봤던 _geo_distance
를 이용한 정렬 방식과 동일한 결과를 볼 수 있다.
하지만 스크립트를 사용하면 다른 데이터와 부가적인 계산을 하거나 유연한 계산식을 구현할 수 있을거 같다.
위에서 사용한 스크립트는 https://github.com/goldcrestwilma/elasticsearch/tree/main/restaurant-index에서 확인할 수 있다.