Trouble Shooting

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

개발바닥곰발바닥!!! 2025. 2. 18. 00:19

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

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

 

우리팀은 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를 썼을 것 같다