5.5 정확값 쿼리 - Exact Value Query
이 문서의 허가되지 않은 무단 복제나 배포 및 출판을 금지합니다. 본 문서의 내용 및 도표 등을 인용하고자 하는 경우 출처를 명시하고 김종민([email protected])에게 사용 내용을 알려주시기 바랍니다.
지금까지 살펴본 풀 텍스트 검색은 스코어 점수 기반으로 정확도(relevancy)가 높은 결과부터 가져옵니다. Elasticsearch는 정확도를 고려하는 풀 텍스트 외에도 검색 조건의 참 / 거짓 여부만 판별해서 결과를 가져오는 것이 가능합니다. 풀 텍스트와 상반되는 이 특성을 정확값(Exact Value) 이라고 하는데 말 그대로 값이 정확히 일치 하는지의 여부 만을 따지는 검색입니다. Exact Value 에는 term, range 와 같은 쿼리들이 이 부분에 속하며, 스코어를 계산하지 않기 때문에 보통 bool 쿼리의 filter 내부에서 사용하게 됩니다.

bool : filter

bool쿼리의 filter 안에 하위 쿼리를 사용하면 스코어에 영향을 주지 않습니다. 다음 3개의 검색 결과를 비교 해 보도록 하겠습니다.
request
response
match 쿼리로 fox 검색
1
GET my_index/_search
2
{
3
"query": {
4
"match": {
5
"message": "fox"
6
}
7
}
8
}
Copied!
match 쿼리로 fox 검색 결과
1
{
2
"took" : 1,
3
"timed_out" : false,
4
"_shards" : {
5
"total" : 1,
6
"successful" : 1,
7
"skipped" : 0,
8
"failed" : 0
9
},
10
"hits" : {
11
"total" : {
12
"value" : 4,
13
"relation" : "eq"
14
},
15
"max_score" : 0.32951736,
16
"hits" : [
17
{
18
"_index" : "my_index",
19
"_type" : "_doc",
20
"_id" : "1",
21
"_score" : 0.32951736,
22
"_source" : {
23
"message" : "The quick brown fox"
24
}
25
},
26
{
27
"_index" : "my_index",
28
"_type" : "_doc",
29
"_id" : "4",
30
"_score" : 0.32951736,
31
"_source" : {
32
"message" : "Brown fox brown dog"
33
}
34
},
35
{
36
"_index" : "my_index",
37
"_type" : "_doc",
38
"_id" : "2",
39
"_score" : 0.23470737,
40
"_source" : {
41
"message" : "The quick brown fox jumps over the lazy dog"
42
}
43
},
44
{
45
"_index" : "my_index",
46
"_type" : "_doc",
47
"_id" : "3",
48
"_score" : 0.23470737,
49
"_source" : {
50
"message" : "The quick brown fox jumps over the quick dog"
51
}
52
}
53
]
54
}
55
}
Copied!
request
response
match 쿼리로 fox 와 quick 검색
1
GET my_index/_search
2
{
3
"query": {
4
"bool": {
5
"must": [
6
{
7
"match": {
8
"message": "fox"
9
}
10
},
11
{
12
"match": {
13
"message": "quick"
14
}
15
}
16
]
17
}
18
}
19
}
Copied!
match 쿼리로 fox 와 quick 검색 결과
1
{
2
"took" : 1,
3
"timed_out" : false,
4
"_shards" : {
5
"total" : 1,
6
"successful" : 1,
7
"skipped" : 0,
8
"failed" : 0
9
},
10
"hits" : {
11
"total" : {
12
"value" : 3,
13
"relation" : "eq"
14
},
15
"max_score" : 0.9468958,
16
"hits" : [
17
{
18
"_index" : "my_index",
19
"_type" : "_doc",
20
"_id" : "1",
21
"_score" : 0.9468958,
22
"_source" : {
23
"message" : "The quick brown fox"
24
}
25
},
26
{
27
"_index" : "my_index",
28
"_type" : "_doc",
29
"_id" : "3",
30
"_score" : 0.8762741,
31
"_source" : {
32
"message" : "The quick brown fox jumps over the quick dog"
33
}
34
},
35
{
36
"_index" : "my_index",
37
"_type" : "_doc",
38
"_id" : "2",
39
"_score" : 0.6744513,
40
"_source" : {
41
"message" : "The quick brown fox jumps over the lazy dog"
42
}
43
}
44
]
45
}
46
}
Copied!
request
response
must 로 fox 및 filter 으로 quick 검색
1
GET my_index/_search
2
{
3
"query": {
4
"bool": {
5
"must": [
6
{
7
"match": {
8
"message": "fox"
9
}
10
}
11
],
12
"filter": [
13
{
14
"match": {
15
"message": "quick"
16
}
17
}
18
]
19
}
20
}
21
}
Copied!
must 로 fox 및 filter 으로 quick 검색 결과
1
{
2
"took" : 2,
3
"timed_out" : false,
4
"_shards" : {
5
"total" : 1,
6
"successful" : 1,
7
"skipped" : 0,
8
"failed" : 0
9
},
10
"hits" : {
11
"total" : {
12
"value" : 3,
13
"relation" : "eq"
14
},
15
"max_score" : 0.32951736,
16
"hits" : [
17
{
18
"_index" : "my_index",
19
"_type" : "_doc",
20
"_id" : "1",
21
"_score" : 0.32951736,
22
"_source" : {
23
"message" : "The quick brown fox"
24
}
25
},
26
{
27
"_index" : "my_index",
28
"_type" : "_doc",
29
"_id" : "2",
30
"_score" : 0.23470737,
31
"_source" : {
32
"message" : "The quick brown fox jumps over the lazy dog"
33
}
34
},
35
{
36
"_index" : "my_index",
37
"_type" : "_doc",
38
"_id" : "3",
39
"_score" : 0.23470737,
40
"_source" : {
41
"message" : "The quick brown fox jumps over the quick dog"
42
}
43
}
44
]
45
}
46
}
Copied!
첫번째는 match 쿼리로 fox 를 검색했을 때 4개의 도큐먼트가 검색되었고 가장 높은 스코어는 "_score" : 0.32951736 입니다. 두번째는 검색에 match 쿼리로 quick 을 추가했을 때 3개의 도큐먼트가 검색되었고 가장 높은 스코어는 "_score" : 0.9468958 입니다. 세번째는 첫번째의 검색에 filter 구문 안에 quick 을 추가했는데 3개의 도큐먼트가 검색되었고 가장 높은 스코어는 첫번째 쿼리와 같은 "_score" : 0.32951736 입니다.
이렇게 filter는 검색에 조건은 추가하지만 스코어에는 영향을 주지 않도록 제어할 때 사용합니다. 보통 쇼핑몰에서 검색어로 정확도가 높은 상품명을 검색하면서 생산 업체를 다시 필터링 하는 등의 용도로 사용이 가능합니다.
filter 내부에서 must_not 과 같은 다른 bool 쿼리를 포함하려면 filter 내부에 bool 쿼리를 먼저 넣고 그 안에 다시 must_not 을 넣어야 합니다. 다음은 fox 를 포함하면서 dog 는 포함하지 않는 도큐먼트를 검색하는 쿼리입니다. dog 를 제외하는 must_not 쿼리가 filter 안에 있기 때문에 스코어는 fox 에만 영향을 받습니다.
request
response
must 로 fox 검색 후 must_not 으로 dog 제거
1
GET my_index/_search
2
{
3
"query": {
4
"bool": {
5
"must": [
6
{
7
"match": {
8
"message": "fox"
9
}
10
}
11
],
12
"filter": [
13
{
14
"bool": {
15
"must_not": [
16
{
17
"match": {
18
"message": "dog"
19
}
20
}
21
]
22
}
23
}
24
]
25
}
26
}
27
}
Copied!
must 로 fox 검색 후 must_not 으로 dog 제거 결과
1
{
2
"took" : 2,
3
"timed_out" : false,
4
"_shards" : {
5
"total" : 1,
6
"successful" : 1,
7
"skipped" : 0,
8
"failed" : 0
9
},
10
"hits" : {
11
"total" : {
12
"value" : 1,
13
"relation" : "eq"
14
},
15
"max_score" : 0.32951736,
16
"hits" : [
17
{
18
"_index" : "my_index",
19
"_type" : "_doc",
20
"_id" : "1",
21
"_score" : 0.32951736,
22
"_source" : {
23
"message" : "The quick brown fox"
24
}
25
}
26
]
27
}
28
}
Copied!
위 검색에서 결과는 하나만 리턴 되었지만 스코어는 "_score" : 0.32951736으로 match 쿼리로 fox만 검색했을 때의 결과와 동일합니다.

keyword

문자열 데이터는 keyword 형식으로 저장하여 정확값 검색이 가능합니다. Keyword 에 대해서는 뒤에 매핑에서 다시 설명 하겠습니다. 아래의 쿼리는 message 필드값이 "Brown fox brown dog" 문자열과 공백, 대소문자까지 정확히 일치하는 데이터만을 결과로 리턴합니다.
request
response
keyword 필드 검색
1
GET my_index/_search
2
{
3
"query": {
4
"bool": {
5
"filter": [
6
{
7
"match": {
8
"message.keyword": "Brown fox brown dog"
9
}
10
}
11
]
12
}
13
}
14
}
Copied!
keyword 필드 검색 결과
1
{
2
"took" : 0,
3
"timed_out" : false,
4
"_shards" : {
5
"total" : 1,
6
"successful" : 1,
7
"skipped" : 0,
8
"failed" : 0
9
},
10
"hits" : {
11
"total" : {
12
"value" : 1,
13
"relation" : "eq"
14
},
15
"max_score" : 0.0,
16
"hits" : [
17
{
18
"_index" : "my_index",
19
"_type" : "_doc",
20
"_id" : "4",
21
"_score" : 0.0,
22
"_source" : {
23
"message" : "Brown fox brown dog"
24
}
25
}
26
]
27
}
28
}
Copied!
keyword 타입으로 저장된 필드는 스코어를 계산하지 않고 정확값의 일치 여부만을 따지기 때문에 스코어가 "_score" : 0.0 으로 나오게 됩니다. 스코어를 계산하지 않기 때문에 keyword 값을 검색 할 때는 filter 구문 안에 넣도록 합니다.
filter 안에 넣은 검색 조건들은 스코어를 계산하지 않지만 캐싱이 되기 때문에 쿼리가 더 가볍고 빠르게 실행됩니다. keyword 와 뒤에 설명할 range 쿼리와 같이 스코어 계산이 필요하지 않은 쿼리들은 모두 filter 안에 넣어서 실행하는 것이 좋습니다.
Copy link