elasticsearch는 검색 시 query 절 별로 관련성 점수(Relevance score)를 내고 높은 점수 순으로 나열한다.
관련성 점수(Relevance score) 는 "_score" 라는 API 메타데이터 필드에서 반환되는 양의 소수점 숫자로 표현된다.
{
"_shard": "[cf-spectrum-log-2024.06.25][0]",
"_node": "7eOtUDXAQGe3ZgXsFQBCLg",
"_index": "cf-spectrum-log-2024.06.25",
"_type": "_doc",
"_id": "cdfDT5ABhXQXL4awCoxV",
"_score": 4.212528,
...
관련성 점수(Relevance score)에 사용되는 알고리즘은 다음과 같은 요소(TF/IDF)들을 고려한다.
1. Term frequency(TF)
- 해당 용어가 필드에 노출되는 빈도. 빈도가 높을 수록 관련성 점수가 높다.
2. Inverse document frequency(IDF)
- 해당 용어가 다른 문서(document)에도 자주 등장한다면 관련성 점수가 낮다.
- 즉, 다른 문서와 비교하여 자주 등장하지 않는 용어 일수록 관련성 점수가 높다.
3. Field-length norm
- 필드 값 길이가 짧을 수록 관련성 점수가 높다.
참조 : https://www.elastic.co/guide/en/elasticsearch/guide/master/relevance-intro.html
match query를 사용하여 검색할 때 위 세가지 요소로 책정된 점수를 확인 할수 있다.
curl -XGET 127.0.0.1:9200/search_index/_search?explain=true \
-H 'Content-Type:application/json' \
-d @test.json
...
###### 점수 계산 설명 시작 ######
"_explanation": {
###### 최종 스코어 ######
"value": 4.212528,
"description": "weight(*** in 131509) [PerFieldSimilarity], result of:",
"details": [
{
"value": 4.212528,
"description": "score(freq=2.0), computed as boost * idf * tf from:",
"details": [
{
"value": 2.2,
"description": "boost",
"details": []
},
{
###### IDF ######
"value": 2.7842128,
"description": "idf, computed as log(1 + (N - n + 0.5) / (n + 0.5)) from:",
"details": [
{
"value": 51408,
"description": "n, number of documents containing term",
"details": []
},
{
"value": 832152,
"description": "N, total number of documents with field",
"details": []
}
]
},
{
###### TF ######
"value": 0.68772954,
"description": "tf, computed as freq / (freq + k1 * (1 - b + b * dl / avgdl)) from:",
"details": [
{
"value": 2,
"description": "freq, occurrences of term within document",
"details": []
},
{
"value": 1.2,
"description": "k1, term saturation parameter",
"details": []
},
{
"value": 0.75,
"description": "b, length normalization parameter",
"details": []
},
{
"value": 4,
"description": "dl, length of field",
"details": []
},
{
"value": 5.9198847,
"description": "avgdl, average length of field",
"details": []
}
]
}
]
}
]
}
},
원하는 검색에 근접하기 위하여 boost를 사용하여 쿼리 절 별로 가중치를 둘 수 있다.
아래와 같이 match_phrase 절에 boost값을 주어 어느 쿼리에 중점을 두어야 할지 결정 할 수 있다.
boost 2를 준 경우 2배의 가중치로 계산된다.
{
"_source": [ "dfg","sdf","asd"],
"query": {
"bool": {
"should": [
{
"match": {
"asd": {
"query": "aaa"
}
}
},
{
"match_phrase": {
"sdf": {
"query": "qwe qw",
"boost": 2
}
}
}
]
}
},
"from": 0,
"size": 2
}
bool 을 사용하여 match, match_phrase query를 조합하 였으며 match_phrase query절에 boost 2로 가중치를 주었다.
참고 : _source : 표출하고자 하는 필드. "dfg,"sdf","asd" 세 필드만 표출된다.
관련성 점수는 Query DSL에서 query절이 query context에서 실행되는지 filter context 에서 실행되는 지에 따라 달라진다.
1. query context
- query context에서 실행되는 query절은 "이 doc와 이 query 절이 얼마나 잘 일치하는가?"에 대한 질문에 응답한다. "얼마나" 라는 말에 표현되어 있드시 _score 메타데이터 필드의 관련성 점수도 함께 계산한다.
2. filter context
- filter context에서 실행되는 query절은 "이 doc와 이 query 절이 일치하는가?"에 대한 질문에 응답한다. 답은 yes/no 로 표현되며 관련성 점수도 계산하지 않는다.
참고 : https://www.elastic.co/guide/en/elasticsearch/reference/7.17/query-filter-context.html
query context, filter context 은 kibana GUI - Discover - 검색하는 부분에서 "Search"는 query context, "Add filter" 는 filter context로 생각할 수 있다.
다음의 예로 query context, filter context 의 차이점을 살펴 볼 수 있다.
1. title 필드에 Search 라는 단어가 포함되어 있는가?
2. content 필드에 Elasticsearch 라는 단어가 포함되어 있는가?
3. status 필드 값이 published 와 일치하는가?
4. publish_date 필드가 2015년 1월 1일보다 큰가?
GET /_search
{
### query context ###
"query": {
"bool": {
"must": [
{ "match": { "title": "Search" }},
{ "match": { "content": "Elasticsearch" }}
],
### filter context ###
"filter": [
{ "term": { "status": "published" }},
{ "range": { "publish_date": { "gte": "2015-01-01" }}}
]
}
}
}
1,2번은 match query를 써서 포함여부가 얼마나 정확한지를 관련성 점수를 책정한다.
3,4번은 그렇다, 그렇지 않다로 표현되며 관련성 점수를 책정하지 않는다.