엘라스틱서치 풀텍스트 쿼리 관련 오류

쭈·2022년 8월 23일
0

에러

목록 보기
2/9

문제원인

 GET slowdelivery/_analyze
   {
     "text" : "신전 떡볶이"
   }
{
    "tokens": [
        {
            "token": "신전",
            "start_offset": 0,
            "end_offset": 2,
            "type": "<HANGUL>",
            "position": 0
        },
        {
            "token": "떡볶이",
            "start_offset": 3,
            "end_offset": 6,
            "type": "<HANGUL>",
            "position": 1
        }
    ]
}

신전 떡볶이를 검색했을 때 '신전'과 '떡볶이'가 인덱스 키가 되어 아래와 같이 가게명과 메뉴명에 신전을 포함하거나 떡볶이를 포함하는 모든 결과가 검색된다.

[
    {
        "shopName": "신전떡볶이",
        "id": 1,
        "shopId": 1,
        "category": "분식",
        "menu": [
            {
                "menuId": 1,
                "menuName": "떡볶이"
            },
            {
                "menuId": 2,
                "menuName": "치즈떡볶이"
            }
        ]
    },
    {
        "shopName": "엽기떡볶이",
        "id": 2,
        "shopId": 2,
        "category": "분식",
        "menu": [
            {
                "menuId": 3,
                "menuName": "엽기떡볶이"
            },
            {
                "menuId": 4,
                "menuName": "엽기분모자떡볶이"
            }
        ]
    },
    {
        "shopName": "bhc",
        "id": 3,
        "shopId": 3,
        "category": "치킨",
        "menu": [
            {
                "menuId": 5,
                "menuName": "뿌링클"
            },
            {
                "menuId": 6,
                "menuName": "분모자로제떡볶이"
            },
            {
                "menuId": 7,
                "menuName": "치즈볼"
            }
        ]
    }
]

현재코드

// 검색 (가게명, 메뉴명, 카테고리)
        QueryBuilder boolQueryBuilder = QueryBuilders.boolQuery()
                .should(QueryBuilders.multiMatchQuery(keyword, "shopName", "menu.menuName","category"));

단순하게 풀텍스트쿼리에 모든 필드를 때려넣고있었는데 ..

match 검색에 여러 개의 검색어를 집어넣게 되면 디폴트로 OR 조건으로 검색이 되어 입력된 검색어 별로 하나라도 포함된 모든 문서를 모두 검색하게 된다.

따라서 shopName과 menuName 필드에 신전이나 떡볶이가 포함된 결과 모두 나오게 된 것이다.

해결방안

1. match + operator

검색어가 여럿일 때 operator 옵션을 사용하면 검색 조건이 OR가 아닌 AND 조건 로 바뀌게 된다.

QueryBuilders.multiMatchQuery(keyword, "shopName", "menu.menuName").operator(Operator.AND);

(예를들어, shopName과 menuName 필드에 신전과 떡볶이를 모두 포함하는 결과가 나오는)

2. match_phrase

그렇다면 신전과 떡볶이를 각각 포함하는 결과가 아닌 '신전 떡볶이' 자체를 포함하는 결과를 만들고 싶으면 match_phrase 쿼리를 사용하면 된다.

이때, slop 이라는 옵션을 이용하여 slop에 지정된 값 만큼 단어 사이에 다른 검색어가 끼어드는 것을 허용할 수 있다.

        MatchPhraseQueryBuilder matchShop = QueryBuilders.matchPhraseQuery("shopName", keyword).slop(1);
        MatchPhraseQueryBuilder matchMenu = QueryBuilders.matchPhraseQuery("menu.menuName", keyword).slop(1);

결론

QueryBuilder boolQueryBuilder = QueryBuilders.boolQuery()
                .should(QueryBuilders.matchPhraseQuery("shopName", keyword).slop(1))
                .should(QueryBuilders.matchPhraseQuery("menu.menuName", keyword).slop(2))
                .should(QueryBuilders.matchQuery("category", keyword));

should는 match_phrase 와 함께 유용하게 사용할 수 있다. 검색 결과 중에서 입력한 검색어 전체 문장이 정확히 일치하는 결과를 맨 상위에 위치시키고 다른 결과들을 누락시키지 않는다.


참고
https://esbook.kimjmin.net/05-search
https://esbook.kimjmin.net/05-search/5.4-keyword

profile
🌱

0개의 댓글