Elasticsearch db 안에 있는 데이터들의 위치와 나의 (또는 유저의) 좌표로 부터 얼마나 떨어져 있는지 거리를 나타내기
실습해보기 🐸
1. Elasticsearch distance_test 라는 index 추가
PUT distance_test
{
"mappings": {
"properties": {
"place": {
"type": "text"
},
"location": {
"type": "geo_point"
},
"reviews": {
"type": "text"
}
}
}
}
2. distance_test 인덱스에 데이터 추가
POST distance_test/_doc
{
"place": "추어탕집",
"location": [128.123123, 34.123123]
}
POST distance_test/_doc
{
"place": "국밥집",
"location": [127.000003, 32.723123]
}
POST distance_test/_doc
{
"place": "백반집",
"location": [126.393933, 35.0100123]
}
POST distance_test/_doc
{
"place": "제비집",
"location": [129.123123, 35.123123]
}
3. node.js 코드 작성
* elasticsearch + node.js + typescript 첫 시작 시 참고글
es 에서 모든 데이터를 가져와보기! 이렇게 가져온 데이터 결과는 아래와 같다.
const run = async() => {
const location = [127.9999, 34.9999];
const [lon, lat] = location;
const esData = await client.search({
index: 'distance_test',
body: {
min_score: 1,
query: {
match_all: {}
},
explain: false,
}
})
console.log('result =', esData);
}
run().catch(e => {
console.log('error=', e);
process.exit(1);
})
result = {
took: 1,
timed_out: false,
_shards: { total: 1, successful: 1, skipped: 0, failed: 0 },
hits: {
total: { value: 4, relation: 'eq' },
max_score: 1,
hits: [ [Object], [Object], [Object], [Object] ]
}
}
hits.hits 안에 위에서 입력한 데이터들 4개가 있다. 그런데 이제 location 변수 (내 좌표라고 해보면) 로부터 해당 장소들의 거리를 구하려면 es에 날리는 쿼리 Body안에 script_fields 를 작성해주어야 한다.
body: {
min_score: 1,
query: {
match_all: {}
},
script_fields: {
geo_distance: {
script: {
params: {
lat, lon,
},
source: "doc['location'].arcDistance(params.lat, params.lon)",
}
}
},
explain: false,
}
geo_distnace 안에 script에 params {위도, 경도} 순서로 작성, source에는 해당 params가 어떻게 계산될지 작성해준다.
* 참고 Doc https://www.elastic.co/guide/en/elasticsearch/painless/6.7/painless-api-reference.html
script_fields를 사용해서 가져온 데이터의 hits 를 살펴보면 그 안에
hits of hits = [
{
_index: 'distance_test',
_type: '_doc',
_id: 'S9RQyoIBnZsIFAhkhmpp',
_score: 1,
fields: { geo_distance: [Array] }
},
{
_index: 'distance_test',
_type: '_doc',
_id: 'a9RQyoIBnZsIFAhki2pc',
_score: 1,
fields: { geo_distance: [Array] }
},
{
_index: 'distance_test',
_type: '_doc',
_id: 'bNRQyoIBnZsIFAhkkGoL',
_score: 1,
fields: { geo_distance: [Array] }
},
{
_index: 'distance_test',
_type: '_doc',
_id: 'bdRQyoIBnZsIFAhkk2rZ',
_score: 1,
fields: { geo_distance: [Array] }
}
]
이렇게 fields 가 생겨있다! (이 전의 데이터는 _index, _type, _id, _score, _source: {} 이렇게 되어 잇음)
fields를 console.log 찍어보면,
fields = { geo_distance: [ 98144.06142685117 ] }
이런 어떤 숫자가 나온다. 이건 해당 document안의 location과 방금 script_fields로 입력한 나의 좌표값과의 거리 차이가 m 로 나온듯하다.
이제 이것을 가공해서 사용하면 된다.
const refinedResult = _.map(esData.hits.hits, ({_source = {}, _score, fields = {} }) => {
const difference = _.get(fields, 'geo_distance.0', -1);
const distance = `${(difference/1000).toFixed(3)}km`
return {
_source,
score: _score,
distance,
}
});
console.log('refined result=', refinedResult);
엇 다시보니 데이터가 들어있는 _source가 반환되지 않았다. 찾아보니 body에 stored_fields: [ "_source" ] 를 추가해 주면 된다고 한다 !
const esData = await client.search({
index: 'distance_test',
body: {
min_score: 1,
stored_fields: [
"_source",
],
query: {
match_all: {}
},
.
.
.
결과
refined result= [
{
_source: { place: '추어탕집', location: [Array] },
score: 1,
distance: '98.144km'
},
{
_source: { place: '국밥집', location: [Array] },
score: 1,
distance: '269.471km'
},
{
_source: { place: '백반집', location: [Array] },
score: 1,
distance: '146.274km'
},
{
_source: { place: '제비집', location: [Array] },
score: 1,
distance: '103.146km'
}
]
내 좌표로부터 각각 장소의 거리 차이가 Km로 잘 나왔다.
전체 코드
const run = async() => {
const location = [127.9999, 34.9999];
const [lon, lat] = location;
const esData = await client.search({
index: 'distance_test',
body: {
min_score: 1,
stored_fields: [
"_source",
],
query: {
match_all: {}
},
script_fields: {
geo_distance: {
script: {
params: {
lat, lon,
},
source: "doc['location'].arcDistance(params.lat, params.lon)",
}
}
},
explain: false,
}
})
console.log('result =', esData);
console.log('hits of hits =', esData.hits.hits);
console.log('fields =', esData.hits.hits[0].fields);
const refinedResult = _.map(esData.hits.hits, ({_source = {}, _score, fields = {} }) => {
const difference = _.get(fields, 'geo_distance.0', -1);
const distance = `${(difference/1000).toFixed(3)}km`
return {
_source,
score: _score,
distance,
}
});
console.log('refined result=', refinedResult);
}
run().catch(e => {
console.log('error=', e);
process.exit(1);
})
'Elasticsearch' 카테고리의 다른 글
[Elasticsearch] synonyms 동의어 사전 추가 및 업데이트 적용하기 (0) | 2022.09.02 |
---|---|
[elasticsearch] search after란? 사용 방법 알아보기 with node.js (0) | 2022.08.24 |
[elasticsearch] dis_max란 무엇인지 알아보기, 실습하기 (bool, must와 함께 쓰기) (0) | 2022.08.19 |
Elasticsearch geographic location query distance 거리로 가까운 곳 검색하기 (0) | 2022.08.16 |
운영중인 Elasticsearch reindex alias 무중단 예제 코드 및 사용 방법 / Zero downtime reindex alias (0) | 2022.07.20 |