문제
GET medicine_index/_search
{
"query": {
"bool": {
"must": [
{ "match": { "itemName": "아스피린" } }
],
"should": [
{ "term": { "color.keyword": "하양" } }
]
, "minimum_should_match": 1
}
}
}
엘라스틱서치에 위 쿼리를 날리는 과정에서 매칭되는 값이 존재하지 않는 결과가 나타났다.
일반적으로 매칭이 되는 데이터가 없다고 판단할 수도 있겠지만, minimum_should_match를 0으로 설정하면, 매칭되는 데이터들이 잘보이는 것이 확인된다.
즉 시스템에 문제가 있다는 뜻이다.
원인 파악
minimum_should_match는 should절의 조건 중 최소 몇개의 조건을 만족해야지 출력되는지 정할 수 있는 옵션이다.
나는 색상 리스트에 적어도 하나를 만족하는 약을 출력하고자 1로 설정하였는데, 이 필터링 조건이 잘 적용되지 않은 것이다.
문제의 원인은 단순했다. term의 경우 Keyword 필드 기준으로 정확하게 일치하는 값을 찾는데, 나는 색상 필드를 Text로 정의하였던 것이다. 따라서 keyword “하양”과 일치하는 도큐먼트가 존재하지 않았기 때문에 오류가 발생하였다.
해결
Color뿐만 아니라 검색할 때 정확한 값이 들어가야하는 필드들에 대해서 keyword 타입을 지정해주었다.
또한 color 타입 자체를 Keyword로 지정하였기 때문에, 쿼리를 아래와 같이 수정하였다.
color.keyword → color
GET medicine_index/_search
{
"query": {
"bool": {
"must": [
],
"should": [
{ "term": { "color": "하양" } }
]
, "minimum_should_match": 1
}
}
}
쿼리가 원하는대로 잘 작동하는 것을 확인하였기 때문에 Spring data elasticsearch를 통해 Native Query를 작성해주었다.
@Override
public List<MedicineDocument> findMedicineBySearching(String itemName, List<String> colors, String shape, Pageable pageable) {
Query boolQuery= QueryBuilders.bool(boolQueryBuilder->boolQueryBuilder
.must(QueryBuilder->QueryBuilder.match(matchQueryBuilder->matchQueryBuilder
.field("itemName")
.query(itemName)))
.should(colors!=null && !colors.isEmpty() ?
colors.stream().map(color ->
QueryBuilders.term(t->t.field("color").value(color))
).toList()
:List.of()
).minimumShouldMatch("1")
);
NativeQuery nativeQuery = NativeQuery.builder()
.withQuery(boolQuery)
.withPageable(pageable)
.build()
;
SearchHits<MedicineDocument> searchHits=elasticsearchOperations.search(nativeQuery, MedicineDocument.class);
return searchHits.getSearchHits()
.stream()
.map(SearchHit::getContent)
.collect(Collectors.toList());
}
그 결과 should 절이지만 색상 조건을 맞추지 못하면 응답 문서에 포함되지 않는 것을 확인할 수 있었다.
참고
- should절이 단독으로 사용되면 minimum_should_match의 값은 기본적으로 0이 들어가고, match, filter와 같이 사용되면 기본값으로 1이 들어간다. should 절을 필터링이 아닌, 관련성 점수 부여의 목적으로 사용하는 경우 minimum_should_match의 값이 1이면 예측할 수 없는 값이 발생할 수 있다. 따라서 이점을 유의할 것.
- 자세히 설명하지 않았지만, text 타입은 인덱스에 저장될 때 색인된 토큰 기반으로 검색되며, keyword의 경우 색인과정 없이 그대로 저장되어 조회된다. 따라서 인덱스에 매핑하려는 필드가 관련성 필드인지, 이진으로 검색되어야 하는 필드인지 파악하여 적절히 타입을 지정해주면 된다.
'Elasticsearch' 카테고리의 다른 글
[Elasticsearch] 도큐먼트 저장 방식과 작동 원리 정리 (0) | 2025.03.24 |
---|---|
[Elasticsearch 보안 설정] 비밀번호 인증 활성화 및 Spring, Kibana 연동 (0) | 2025.03.22 |
[Elasticsearch] Docker 환경에서 스냅샷(Snapshot) 백업 및 자동화 설정 (0) | 2025.03.21 |
[ElasticSearch] 인기 검색어 쿼리 구현 (0) | 2025.03.11 |
[ElasticSearch] ElasticSearch Native Query 최신 버전 작성 (0) | 2025.03.04 |