Elasticsearch

Elasticsearch Query DSL

illho 2024. 3. 22. 21:59

정확값 쿼리 - Exact Value Query

 

bool : filter

 

Elasticsearch는 정확도를 고려하는 풀 텍스트 외에도 검색 조건의 참/거짓 여부만 판별해서 결과를 가져오는 것이 가능하다. 풀 텍스트와 상반되는 이 특성을 정확값이라고 하는데 정확히 일치 하는지 여부 만을 따지는 검색이다.

Exact Value 에는 term, range 와 같은 쿼리들이 속한다.

스코어를 계산하지 않기 때문에 보통 bool 쿼리의 filter 내부에서 사용하게 된다.

 

테스트 데이터

POST sport_5/_bulk
{"index":{"_id":1}}
{"message":"The quick brown fox"}
{"index":{"_id":2}}
{"message":"The quick brown fox jumps over the lazy dog"}
{"index":{"_id":3}}
{"message":"The quick brown fox jumps over the quick dog"}
{"index":{"_id":4}}
{"message":"Brown fox brown dog"}
{"index":{"_id":5}}
{"message":"Lazy jumping dog"}

 

 

명령어

GET sport_5/_search
{
  "query" : {
    "match": {
      "message": "fox"
    }
  }
  
}

 

결과

 

명령어

 

GET sport_5/_search
{
  "query" : {
    "bool" : {
      "must": [
        {
          "match" : {
            "message" : "fox"
          }
        }
      ],
      "filter": [
          {
            "match":{
              "message": "quick"
            }
          }
        ]
    }
  }
}

 

 

결과

 

첫번째 match 쿼리로 fox를 검색했을때는 

가장 높은 스코어가 _score : 0.32951736 이다.

첫번째의 검섹에 filter 구문 안에 quick을 추가했는데 가장 높은 스코어는 첫번째 쿼리와 같은  _score : 0.32951736 이다.

 

filter는 검색에 조건은 추가하지만 스코어에는 영향을 주지 않도록 제어할때 사용한다.

 

keyword

 

keyword에 대해서는 문자열과, 공백, 대소문자까지 정확히 일치하는 데이터만을 결과로 리턴한다.

 

명령어

 

GET sport_5/_search
{
  "query" : {
    "bool" : {
      "filter": [
          {
            "match":{
              "message.keyword": "The quick brown fox"
            }
          }
        ]
    }
  }
  
}

 

결과

 

 

keyword 타입으로 저장된 필드는 스코어를 계산하지 않고 정확값의 일치 여부만을 따지기 때문에 스코어가 0.0으로 나오게 된다. 스코어를 계산하지 않기 때문에 keyword 값을 검색 할 때는 filter 구문 안에 넣는다.

 

 

역 인덱스 - Inverted Index

 

ID Text
doc1  the soccer is good
doc2  the soccer is very good
doc3  the soccer is  difficult
doc4  the soccer difficult
doc5  the baseball  

 

 

일반적으로 관계형 DB에서는 위 내용을 보이는 대로 테이블 구조로 저장한다.

위 테이블에서 soccer가 포함된 행들을 가져온다고 하면 text 열을 한 줄 씩 찾아 내려가면서 soccer 가 있으면 가져오고 없으면 넘어가는 식이다.

 

엘라스틱 서치는 데이터를 저장할 때 역 인덱스라는 구조를 만들어 저장한다.

 

ID ID
the doc1 doc2 doc3 doc4 doc5 soccer doc1 doc2 doc3 doc4
is doc1 doc2 doc3 very doc2
difficult doc3 doc4 baseball doc5

 

 

엘라스틱서치에서는 추출된 각 키워드를 텀이라고 부른다.

역 인덱스가 있으면 soccer 를 포함하고 있는 도큐먼트들의 id를 바로 얻어올 수 있다.

엘라스틱서치는 데이터가 늘어나도 역 인덱스가 가리키는 id의 배열값이 추가되는 것뿐이기 때문에 속도의 저하 없이 빠른속도로 검색이 가능하다.

 

텍스트 분석 - Text Analysis

 

elasticsearch 에 저장되는 도큐먼트는 모든 문자열 필드 별로 역 인덱스를 생성한다.

elasticsearch에 문자열 필드가 저장될 때 데이터에서 검색어 토큰을 저장하기 위해 여러 단계의 처리 과정을 거친다.

이 전체 과정을 텍스트 분석 이라고 하고 이 과정을 처리하는 기능을 애널라이저라고 한다.

애널라이저는 0~3개 캐릭터 필터와 1개의 토크나이저(Tokenizer) 그리고 0~n 개의 토큰 필터(Token Filter)로 이루어 진다.

 

텍스트 데이터가 입력되면 가장 먼저 필요에 따라 전체 문장에서 특정 문자를 대치하거나 제거하는데 이 과정을 담당하는 기능이 캐릭터 필터이다. 

 

문장에 속한 단어들을 텀 단위로 하나씩 분리 해 내는 처리 과정을 거치는데 이 과정을 담당하는 기능이 토크나이저이다.

토크나이저는 반드시 1개만 적용이 가능하다. 

 

분리된 텀 들을 하나씩 가공하는 과정을 거치는데 이 과정을 담당하는 기능이 토큰 필터 이다.

토큰 필터는 0개 부터 여러개를 적용할 수 있다.

 

텀 중에서는 검색어로서의 가치가 없는 단어들이 있는데 이런 단어를 불용어라고 한다.

불용어는 검색어 토큰에서 제외된다.

 

필요에 따라서 동의어를 추가 하기도 한다.

synonym 토큰 필터를 사용하여 quick 텀에 동의어로 fast를 지정하면 fast 로 검색했을 때도 같은 의미인 quick 을 포함하는 도큐먼트가 검색되도록 할 수 있다.

 

_analyze

 

elasticsearch 에서는 분석된 문장을 _analyze API를 이용해서 확인할 수 있다.

토크나이저는 tokenizer , 토큰 필터는 filter 항목의 값으로 입력하면 된다. 토크나이저는 하나만 적용되기 때문에 바로 입력하고, 토큰필터는 여러개를 적용할 수 있기 때문에 배열 형식으로 입력한다.

The quick brown fox jumps over the lazy dog 문장을 whitespace 토크나이저와 lowercase, stop, snowball 토큰 필터를 적용해봤다.

 

토큰필터 lowercase 는 대문자를 모두 소문자로 바꿔준다. 대소문자 구별없이 검색이 가능하게 된다.

stop은 불용어들을 제거해준다.

snowball은 문법상 변형된 단어를 일반적으로 검색에 쓰이는 기본 형태로 변환하여 검색이 가능하게 한다.

예를들면 jumps , jumping 을 ~s와 ~ing을 제거해서 jump로 변경 된다.

 

명령어

GET _analyze
{
  "text" : "The quick brown fox jumps over the lazy dog",
  "tokenizer" : "whitespace",
  "filter" : [
      "lowercase",
      "stop",
      "snowball"
    ]
}

 

결과

 

 

 

명령어

GET _analyze
{
  "text" : "The quick brown fox jumps over the lazy dog",
  "analyzer" : "snowball"
}

 

whitespace 토크나이저 , lowercase, stop, snowball 토큰 필터들을 조합한 것이 snowball 애널라이저 이다.

 

 

인덱스에 애널라이저를 지정한다.

 

명령어

PUT sport_6
{
  "mappings": {
    "properties": {
      "message": {
        "type": "text",
        "analyzer": "snowball"
      }
    }
  }
}

 

테스트 데이터를 넣는다

PUT sport_6/_doc/1
{
  "message": "The quick brown fox jumps over the lazy dog"
}

 

 

결과 : match 쿼리로 jump , jumping 또는 jumps 중 어떤 값으로 검색 해도 결과가 나타난다.

snowball 애널라이저를 거쳐 jumping을 입력해도 jump로 검색한다.

역인덱스에는 jumps 가 아닌 jump로 저장 되어있다.

GET sport_6/_search
{
  "query": {
    "match": {
      "message": "jumping"
    }
  }
}

 

 

 

Term 쿼리

 

elasticsearch에서 제공하는 쿼리 중에는 term 쿼리가 있다. match 쿼리와 문법은 유사하지만 term 쿼리는 입력한 검색어는 애널라이저를 적용하지 않고 입력된 검색어 그대로 일치하는 텀을 찾는다. 

jumps , jumping 으로 검색하면 결과가 나타나지 않고 jump 로 검색해야 결과가 나타난다.

 

명령어

GET sport_6/_search
{
  "query": {
    "term": {
      "message": "jumps"
    }
  }
}

 

결과

 

jump로 검색해야만 데이터가 조회된다.

 

 

 

referecence

https://esbook.kimjmin.net/06-text-analysis/6.3-analyzer-1/6.3-analyzer