Light Blue Pointer
본문 바로가기
Trouble Shooting

[Open Search] OpenSearch text/keyword 매핑 차이로 인한 검색 오류 해결기

by 개발바닥곰발바닥!!! 2025. 2. 18.

스터디 일정이 있어서 일찍 퇴근한 나는 내가 개발한 로그 페이지에 결과가 출력되지 않는다는 과장님의 카톡을 받게 된다.

다음날 아침 일찍부터 출근해서 문제를 파악해 보았다.

 

우리팀은 tts, stt, 대화엔진 등 여러 시스템을 거칠때마다 로그를 남긴다.

각 서비스간 연동 로그를 OpenSearch에서 조회해서 유의미한 정보로 가공한 후 스프링으로 백엔드 api를 구현하고 리액트로 프론트단까지 구현하는게 내 업무였다.

 

분명히 정말 동일한 코드로 생성된 동일한 쿼리가 날아가는데

왜 TB 환경에서는 되고 STG 환경에서는 되지 않는지 고민을 시작했다.

 

OpenSearch dashboard의 dev tools를 켜고 쿼리가 왜 안 되는지 조건을 하나씩 빼면서 실행해보았다

 

```

GET project_name_log_*/_search
{
  "query": {
      "bool" : {
    "must" : [
      {
        "term" : {
          "logType" : {
            "value" : "SESSION",
            "boost" : 1.0
          }
        }
      },
      {
        "range" : {
          "timestamp" : {
            "from" : "20000101000000000",
            "to" : "20250214235959000",
            "include_lower" : true,
            "include_upper" : true,
            "boost" : 1.0
          }
        }
      },
      {
        "term" : {
          "baseInfo.tenantCode" : {
            "value" : "TENANT1",
            "boost" : 1.0
          }
        }
      }
    ],
    "adjust_pure_negative" : true,
    "boost" : 1.0
  }
}
}
```
해당 쿼리를 STG에 날리면 결과가 0이고 TB에 날리면 결과가 1400개정도 나온다
그런데 여기서 다음과 같이 tenantCode 조건을 제거하면 STG에서도 결과가 0개가 아니게 된다
```
GET voicebot2_log_*/_search
{
  "query": {
      "bool" : {
    "must" : [
      {
        "term" : {
          "logType" : {
            "value" : "SESSION",
            "boost" : 1.0
          }
        }
      },
      {
        "range" : {
          "timestamp" : {
            "from" : "20000101000000000",
            "to" : "20250214235959000",
            "include_lower" : true,
            "include_upper" : true,
            "boost" : 1.0
          }
        }
      }
    ],
    "adjust_pure_negative" : true,
    "boost" : 1.0
  }
}
}

```

테넌트 코드를 도대체 왜 못 읽을까 싶어서 이거저거 해보다가 매핑 구조도 찍어보았다

TB

```

  "project_name_log_2025.02.12": {

    "mappings": {

      "baseInfo.tenantCode": {

        "full_name": "baseInfo.tenantCode",

        "mapping": {

          "tenantCode": {

            "type": "keyword"

          }

        }

      }

    }

  }

```

 

STG

```

"project_name_log_2025.02.12": {

    "mappings": {

      "baseInfo.tenantCode": {

        "full_name": "baseInfo.tenantCode",

        "mapping": {

          "tenantCode": {

            "type": "text",

            "fields": {

              "keyword": {

                "type": "keyword",

                "ignore_above": 256

              }

            }

          }

        }

      }

    }

  }

```

원인

TB는 매핑이 keyword로 되어있었고

STG는 매핑이 text로 되어있는 문제가 있었다

 

STG 환경

"type": "text",

"fields": {

  "keyword": {

    "type": "keyword",

    "ignore_above": 256

  }

}

  • 메인 필드가 text 타입이면 텍스트 분석(analyze)이 일어나며, 토큰화되어 저장되게 된다
  • keyword 서브필드는 원본 문자열을 그대로 저장하는 추가 필드이다

TB 환경

"type": "keyword"

  • keyword 타입은 문자열을 있는 그대로 저장하고 분석 과정이 없다

 

서치하다가 다음 사실을 발견했다

  1. term 쿼리는 정확한 값 매칭을 수행하며 분석을 거치지 않음
  2. STG에서 baseInfo.tenantCode로 term 쿼리를 하면 text 타입 필드를 검색하게 되어 원하는 결과가 안 나옴
  3. TB에서는 keyword 타입이라 term 쿼리가 바로 정확한 매칭을 수행할 수 있음

그래서 STG 환경에서는 baseInfo.tenantCode.keyword로 검색해야 TB와 동일한 결과를 얻을 수 있었던 것!

나는 두군데 다 동일한 코드, term 쿼리로 날렸기 때문에 필드의 매핑 구조가 정확하게 일치해야만 검색이 되어서 결과값이 0이 되었던 것이다

해결책 1

내가 코드로 생성하는 term 쿼리에 .keyword를 추가하여 수정한다

```

{

  "term": {

    "baseInfo.tenantCode.keyword": "TENANT1"  

  }

}

```

그러나 더 나은 해결책을 위해 더 찾아보았다

해결책 2

 

```

STG에 날릴 때는 termQuery대신 matchQuery로 날린다

{

  "match": {

    "baseInfo.tenantCode": "TENANT1"

  }

}

```

 

이렇게 하면 STG의 text 타입 필드에서도

TB의 keyword 타입 필드에서도 동일하게 작동하게 된다

 

해결책 3

STG의 타입 매핑을 keyword로 수정한다

 

해결책 2처럼 matchQuery를 날리면 키워드를 모두 분석해야 해서 성능에 안 좋다고 판단했다

해당 필드에 analyzer가 안 달려있을때는 termQuery와 matchQuery의 차이가 미미할 것 같기도 했지만

어차피 or 연산이나 유사한 검색까지 수행할 거 아니니까

가벼운 termQuery로 할 수 있게 필드를 바꾸는게 맞을 것 같았다

결국 로그 적재해주시는 분과 협의해서 필드의 매핑을 수정했다

재인덱싱을 해야 해서 오픈 후였다면 그냥 matchQuery를 썼을 것 같다