Project/TroubleShooting

Elasticsearch 필터와 스코어링 전략: 매칭 로직 개선하기

w.llama 2025. 4. 26. 21:52

최근 매칭관련 매칭이 가능한 유저를 조회하는 로직에서 es를 사용하게 되었는데 이때 filter를 사용해서 충족되어야하는 조건, 그리고 조건에 포함되면안되는 조건, 선택 조건 등 검색시 조건을 추가하여 검색하는 로직을 만들었는데. 이를 정리하고자한다.

Elasticsearch 쿼리 조건 타입

ES에서 검색 조건을 설정할 때 주로 사용하는 세 가지 주요 쿼리 타입이 있다.

  1. must - 반드시 충족해야 하는 조건
    • 이 조건에 맞지 않는 문서는 결과에서 완전히 제외됨
    • 필수 조건으로 작동하며 AND 연산자와 유사함
  2. should - 충족하면 좋은 조건
    • 이 조건을 만족하면 문서의 관련성 점수가 높아짐
    • 가중치를 부여하는 방식으로 작동함
    • 필수는 아니지만 만족할수록 검색 결과 상위에 노출됨
  3. must_not - 포함되면 안 되는 조건
    • 이 조건에 맞는 문서는 결과에서 제외됨
    • NOT 연산자와 유사하게 작동함

현재 구현 방식과 개선 방향

현재 방식: 백엔드 정렬 방식

현재는 다음과 같은 2단계 접근법을 사용하고 있음.

  1. ES filter를 사용한 1차 필터링
    • 필수 조건(must)과 제외 조건(must_not)을 적용해 기본 후보군 추출
    • 조건에 맞는 사용자들만 1차적으로 필터링
  2. 백엔드에서의 가중치 계산 및 정렬
    • 필터링된 결과를 백엔드로 가져온 후
    • 각 조건별 가중치를 직접 계산해서 점수 부여
    • 최종 점수에 따라 정렬 후 클라이언트에 반환

이 방식은 구현이 직관적이고 백엔드에서 세밀한 제어가 가능하다는 장점이 있지만, 대량의 데이터를 백엔드로 가져와 처리해야 하는 부담이 있어.

개선 방향: script_score 활용

앞으로는 ES의 script_score 기능을 활용해 검색 단계에서 바로 가중치를 계산하는 방식으로 개선하려고 한다

//e.g
{
  "query": {
    "function_score": {
      "query": {
        "bool": {
          "must": [
            { "term": { "active": true } }
          ],
          "must_not": [
            { "term": { "blocked": true } }
          ],
          "should": [
            { "term": { "premium": true } }
          ]
        }
      },
      "script_score": {
        "script": {
          "source": "params.weight * doc['rating'].value + (doc['premium'].value ? 10 : 0)",
          "params": {
            "weight": 2.0
          }
        }
      }
    }
  }
}

이 방식의 장점은:

  1. 성능 향상 - ES 내부에서 스코어링과 정렬이 이루어져 백엔드 부하 감소
  2. 확장성 - 복잡한 스코어링 로직도 ES 내에서 처리 가능
  3. 효율성 - 이미 정렬된 상태로 결과를 받아 추가 처리 최소화

결론

현재는 필터링 후 백엔드에서 가중치 계산 및 정렬을 하는 방식을 사용하고 있지만, 곧 script_score를 활용한 방식으로 전환할 예정이야. 이렇게 하면 ES의 강력한 검색 및 스코어링 기능을 더 효과적으로 활용할 수 있고, 서비스 성능도 개선될 거라 기대중임.