본문 바로가기

Lucene

Lucene CustomScoreQuerySample #2 - 응용편 -

우선 이 포스트에 나오는 sample 소스는
http://blog.mono-koubou.net/wp-content/uploads/2008/01/valuesourcequerysample.txt
의 소스를 살짝 수정한 소스 입니다.

앞선 포스트에서는 루씬의 CustomScoreQuery 기초적인 사용법을 보았습니다.

이번에는 이 CustomeScoreQuery를 이용해서 조금 응용을 해보겠습니다.

아래 이미지를 봐주세요.



바로 집을 기준으로 거리가 5이내에 있는 스시 집을 찾아보도록 하겠습니다.
일단, 위와 같은 결과를 얻으려면 일단 스시인 집들을 찾고 그 안에서 각 좌표를 통해 거리를 계산해야 할 것 입니다.
거리를 구하는 공식은.. 피타고라스의 정리를 이용하면 아래와 같이 구해집니다.
(x'-x)^2 + (y' - y)^2 = 거리^2
에서 거리 = sqrt((x'-x)^2 + (y' - y)^2) 가 됩니다.

이것을 이용해 보겠습니다.

일단 코드입니다.


DistanceQuerySample.java
하나하나 살펴보겠습니다. 일단, main 메서드 입니다.


여기서는 "스시"집 중에서 집(5,5)를 기준으로 거리 5이내에 있는 스시집을 찾고자 하는 질의 입니다.
색인 데이터는

이렇게 각 가게의 이름과 좌표를 주고 있습니다.

색인하는 메서드는 이전 포스트에 있으니까 넘어가고 바로
search메서드를 살펴보겠습니다.


좌표와 거리를 받아서 쿼리 구문을 만듭니다.


이 쿼리에 의해서 "스시"집들이 검색이 될 것 입니다.
CustomQuery는 앞에서 말씀드렸지만 기본 질의 쿼리와 valueSource 쿼리를 사용해서 점수를 계산합니다.
기본질의 쿼리의 점수와 valueSourceQuery의 점수를 마음대로 사용 할 수 있지요.

그런데, 이상한 클래스가 하나 있습니다.
DistanceFunction 이라는 클래스입니다.
이 클래스는 아래와 같은 모양을 갖습니다.

보시면 아시겠지만 이 클래스는 FloatFieldSource를 상속받고 있습니다. 앞에서, CustomScoreQuery를 사용 할 때
FloatFieldSource를 사용해서 ValueSourceQuery를 만들었던 것 기억이 나시나요?



위 구문으로 ValueSourceQuery가 던져주는 score는 count필드의 값 그 자체가 되었었습니다.
현재 우리는 특정 필드의 값 자체가 필요한 것이 아니고
그 필드의 값 (좌표)를 통해 거리를 계산해야 하기 때문에 그것을 계산하기 위한 DistanceFunction 클래스를 만들었고
이것을 이용해 ValueSourceQuery를 만들어야하기 때문에 FloatFieldSource를 상속 받았습니다.

그럼 거리를 계산하는 메서드를 보겠습니다.
IndexReader를 통해서 해당 필드의 값을 모두 뽑아 내고 있습니다. 여기서는 좌표값들이 나오겠죠..

그런데 여기서 return값이 계산 된 거리 값일 것 같았는데 DocValues 입니다.
CustomScoreQuery내부적으로는 ValueSourceQuery를 사용해서 ValueSourceQuery의 score를 계산하는데, 이 ValueSourceQuery는
ValueSourceScore를 계산하고 이때 사용 되는 것이 DocValues입니다. 때문에, 위 메서드에서 DocValues를 상속한
DistnaceDocValues를 리턴해주고 있는 것 입니다.

실제적으로 IndexReader를 통해서 뽑아온 좌표들을 사용해서 기준점과 거리를 계산하고 그 값을
리턴해주는 클래스는 아래의 클래스입니다.

이 클래스에서는 기준점과 대상점을 받아서 거리를 계산하고 있습니다.
public float floatVal(int doc) 메서드의 이름으로 유추해볼때 i번째 검색된 document에 대한 거리 값을 리턴 할 수
있도록 되어 있는 것 같습니다.

distance = (float)d_ - distance;

가 들어가 있는 것은 기준 되는 거리(5) 에서 계산 된 거리를 뺄 경우 가장 가까울수록 큰 값이 나오기 때문입니다.
이 값 그 자체가 score가 되기 때문에 위와 같이 distance를 계산해서 넘겨주면 가장 가까운 가게가 가장 먼저
나오게 됩니다.

distance = distance > 0f ? distance : 0f;

이 부분이 들어가 있는 이유는 5이내의 가게를 찾는 것이기 때문에 거리가 5 이상이 되는 것들은 맨 밑으로 내려버리기 위함입니다.

위와 같이 DocValues를 상속받아 클래스를 만들면 원하는 값으로 점수를 마음대로 산정 할 수 있게 됩니다.

위 클래스 실행 결과는 아래와 같습니다.
1.1985767 : 스시 / 7,7
1.1038789 : 스시 / 5,2
0.48399264 : 스시 / 6,9
0.41801658 : 스시 / 2,8
0.0 : 스시 / 1,9