본문 바로가기
데이터 엔지니어( 실습 정리 )/elasticsearch

3. 엘라스틱서치 인덱스 설계 (3, 4, 5)

by 세용용용용 2024. 12. 10.

[ 엘라스틱 서치 바이블 ] 공부한 후 정리한 내용 입니다!!!

 

 

3. 애널라이저와 토크나이저

  • 애널라이저는 ( 캐릭터필터 >= 0 ), ( 토크나이저 = 1 ), ( 토큰 필터 >= 0 ) 로 구성
  • 캐릭터 필터 >>> 토크나이저 >>> 토큰 필터 순서로 동작 수행

 

(3-1) analyze API : 애널라이저 동작 테스트 하는 api

### _analyze 사용
curl -XGET "http://192.168.56.10:9200/_analyze?pretty" -H "Content-Type: application/json" -d \
'{
  "analyzer": "standard",
  "text": "Hello, HELLO, World!"
}'

>>> "Hello, HELLO, World!" standard 애널라이저 분석 결과 확인
>>> 최종 hello, hello, world 토큰으로 쪼개짐 확인 가능

분석 결과

 

 

(3-2) 캐릭터 필터 : 텍스트를 받아 문자 추가, 변경, 삭제

  • 애널라이저는 0개 이상 캐릭터 필터 지정 가능 ( 여러개 지정시 순차적 실행 )

es 내장 캐릭터 필터

### 캐릭터 필터 테스트
curl -XGET "http://192.168.56.10:9200/_analyze?pretty" -H "Content-Type: application/json" -d \
'{
  "char_filter": ["html_strip"],
  "text": "<p>I&apos;m so <b>happy</b>!</p>"
}'

>>> <p> 는 줄바꿈 치환, <b> 는 제거, &apos; 는 홀따음표로 디코딩 됨

캐릭터 필터 테스트 결과

 

 

(3-3) 토크나이저 :  캐릭터 스트림은 받아 여러 토큰으로 쪼갬

  • 애널라이저는 한 개의 토크나이저만 지정 가능

standard 토크나이저

- 기본 토크나이저

- 텍스트를 단어 단위 나눔

 

 

keyword 토크나이저

- 텍스트를 쪼개지 않고 내보냄 ( 단일 토큰 )

### keyword 토크나이저 테스트
curl -XGET "http://192.168.56.10:9200/_analyze?pretty" -H "Content-Type: application/json" -d \
'{
  "tokenizer": "keyword",
  "text": "Hello, HELLO, World!"
}'

keyword 토크나이저 테스트 결과

 

 

ngram 토크나이저

- 텍스트를 min_gram, max_gram 단위로 쪼갬

- token_chars 속성을 통해 토큰에 포함시킬 타입 문자 지정

- rdb "like" 유사한 검색 구현시, 자동 완성 관련 서비스 구현 시 활용

### ngram 토크나이저 테스트
curl -XGET "http://192.168.56.10:9200/_analyze?pretty" -H "Content-Type: application/json" -d \
'{
  "tokenizer": {
    "type": "ngram", "min_gram": 3, "max_gram": 4
  },
  "text": "Hello, World!"
}'
>>> 21개 토큰으로 쪼개짐 ( 의미없는 토큰도 포함 )

curl -XGET "http://192.168.56.10:9200/_analyze?pretty" -H "Content-Type: application/json" -d \
'{
  "tokenizer": {
    "type": "ngram", "min_gram": 3, "max_gram": 4, "token_chars": ["letter"]
  },
  "text": "Hello, World!"
}'
>>> 10개 토큰으로 쪼개짐 ( 글자로 분류되는 문자만 )

 

 

edge_ngram 토크나이저

- ngram 과 유사하지만 모든 토큰의 시작 글자를 단어 시작 글자로 고정 시켜 생성함

### edge_nagram 토크나이저 테스트
curl -XGET "http://192.168.56.10:9200/_analyze?pretty" -H "Content-Type: application/json" -d \
'{
  "tokenizer": {
    "type": "edge_ngram", "min_gram": 3, "max_gram": 4, "token_chars": ["letter"]
  },
  "text": "Hello, World!"
}'

edge_ngram 테스트 결과

 

letter 토크나이저 : 언어로 분류되는 문자가 아닌 문자시 쪼갬 ( 공백, 특수문자 )

whitespace 토크나이저 : 공백 문자 만날시 쪼갬

pattern 토크나이저 : 정규표현식 단어의 구분자로 사용해 쪼갬

 

 

(3-4) 토큰 필터:  토큰 스트림을 받아 토큰을 추가, 변경, 삭제

  • 애널라이저에 0개 이상 토큰 필터 지정 가능 ( 여러 지정시 순차적 적용 )

es 내장 토큰 필터

### 토큰 필터 테스트
curl -XGET "http://192.168.56.10:9200/_analyze?pretty" -H "Content-Type: application/json" -d \
'{
  "filter": ["lowercase"],
  "text": "Hello, World!"
}'
>>> 토큰 소문자로 변경

토큰 필터 테스트 결과

 

 

(3-5) 내장 애널라이저

  • 애널라이저는 ( 캐릭터 필터 + 토크나이저 + 토큰 필터 ) 조합으로 구성
  • es는 ( 캐릭터 필터 + 토크나이저 + 토큰 필터 ) 이러한 조합을 미리 만들어 놓은 내장 애널라이저 있음

### fingerprint 애널라이저 테스트
curl -XGET "http://192.168.56.10:9200/_analyze?pretty" -H "Content-Type: application/json" -d \
'{
  "analyzer": "fingerprint",
  "text": "Yes yes, Godel ss ss this thiss."
}'

fingerprint 애널라이저 테스트

 

 

(3-6) 애널라이저 매핑에 적용

### 애널라이저 매핑에 적용 테스트
curl -XPUT "http://192.168.56.10:9200/analyzer_test" -H "Content-Type: application/json" -d \
'{
  "settings": {
    "analysis": {"analyzer": {"default": {"type": "keyword"}}}
  },
  "mappings": {
    "properties": {
      "defaultText": {"type": "text"},
      "standardText": {"type": "text", "analyzer": "standard"}
    }
  }
}'

>>> settings.analysis.analyzer.default : 해당 인덱스 디폴트 애널라이저 등록 가능 ( keyword )
>>> mappings 필드에 analyzer 옵션으로 각 필드 별로 애널라이저 지정 가능
( standardText 필드는 standard 애널라이저 지정 )

 

 

(3-7) 커스텀 애널라이저

  • es 내장 에널라이저로 목표 달성 못할시 커스텀 애널라이저 사용 고려
### 커스텀 애널라이저 테스트
curl -XPUT "http://192.168.56.10:9200/analyzer_test2" -H "Content-Type: application/json" -d \
'{
  "settings": {
    "analysis": {
      "char_filter": {
        "my_char_filter": {
          "type": "mapping",
          "mappings": ["i. => 1.", "ii. => 2.", "iii. => 3.", "iv. => 4."]
        }
      },
      
      "analyzer": {
        "my_analyzer": {
          "char_filter": ["my_char_filter"],
          "tokenizer" : "whitespace",
          "filter": ["lowercase"]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "myText": {"type": "text", "analyzer": "my_analyzer"}
    }
  }
}'


### 생성된 커스텀 애널라이저 테스트
curl -XGET "http://192.168.56.10:9200/analyzer_test2/_analyze?pretty" -H "Content-Type: application/json" -d \
'{
  "analyzer": "my_analyzer",
  "text": "i.Hello ii.World iii.Bye iv.World!"
}'

>>> 총 4개의 토큰으로 역색인 확인

커스텀 애널라이저 테스트

 

 

(3-8) 플러그인 설치를 통한 애널라이저 추가 및 한국어 형태소 분석

### 엘라스틱 서치 한국어 플러그인 설치 ( analysis-nori )
!! 플러그인 설치시 es 클러스터 구성하는 모든 노드에 설치 !!
/usr/share/elasticsearch/bin/elasticsearch-plugin install analysis-nori
>>> 각 노드 설치해 엘라스틱서치 재가동

### nori 애널라이저 테스트
curl -XGET "http://192.168.56.10:9200/_analyze?pretty" -H "Content-Type: application/json" -d \
'{
  "analyzer": "nori",
  "text": "세용이는 컴퓨터를 다룬다."
}'

nori 애널라이저 테스트 결과

 

 

(3-9) 노멀라이저 :  토그나이저 없이 ( 캐릭터 필터 + 토큰 필터 ) 구성

  • 적용 대상이 keyword 타입의 필드
  • 단일 토큰 생성
  • es 제공 노멀라이저는 lowercase 밖에 없음 ( 다른 방법 사용시 커스텀 노멀라이저 조합 해야 됨 )
### 커스텀 노멀라이저 테스트
curl -XPUT "http://192.168.56.10:9200/normalizer_test" -H "Content-Type: application/json" -d \
'{
  "settings": {
    "analysis": {
      "normalizer": {
        "my_normalizer": {
          "type": "custom",
          "char_filter": [],
          "filter": ["asciifolding","uppercase"]
        }
      }
    }
  },
  
  "mappings": {
    "properties": {
      "myNormalizerKeyword": {"type": "keyword", "normalizer": "my_normalizer"},
      "lowercaseKeyword": {"type": "keyword", "normalizer": "lowercase"},
      "defaultKeyword": {"type": "keyword"}
    }
  }
}'
>>> myNormalizerKeyword 필드는 my_normalizer 노멀라이저
>>> lowercaseKeyword 필드는 lowercase 노멀라이저
>>> defaultKeyword 필드는 아무것도 지정 안됨 ( 애널라이저는 standard 애널라이저 적용하지만 노멀라이저는 X )


### 노멀라이저 동작 테스트
curl -XGET "http://192.168.56.10:9200/normalizer_test/_analyze?pretty" -H "Content-Type: application/json" -d \
'{
  "field": "myNormalizerKeyword",
  "text": "Happy sy0218"
}'

curl -XGET "http://192.168.56.10:9200/normalizer_test/_analyze?pretty" -H "Content-Type: application/json" -d \
'{
  "field": "lowercaseKeyword",
  "text": "Happy sy0218"
}'

curl -XGET "http://192.168.56.10:9200/normalizer_test/_analyze?pretty" -H "Content-Type: application/json" -d \
'{
  "field": "defaultKeyword",
  "text": "Happy sy0218"
}'
>>> myNormalizerKeyword 필드 : "HAPPY SY0218"
>>> lowercaseKeyword 필드 : "happy sy0218"
>>> defaultKeyword 필드 : "Happy sy0218"

 

 

 

4. 인덱스 템플릿

  • 템플릿을 사전에 정의함 으로써 업무 효율성 향상 반복 작업을 줄일수 있음

(4-1) 인덱스 템플릿

### 인덱스 템플릿 테스트
!! index_patterns 필드에 인덱스 패턴을 지정 ( 와일드 카드 지정 가능 ) !!
!! priority 값을 이용해 인덱스 템플릿 간 우선 순위 지정 가능 !!
curl -XPUT "http://192.168.56.10:9200/_index_template/my_template" -H "Content-Type: application/json" -d \
'{
  "index_patterns": ["pattern_test_index-*", "another_pattern-*"],
  "priority": 1,
  "template": {
    "settings": {
      "number_of_shards": 2,
      "number_of_replicas": 2
    },
    "mappings": {
      "properties": {
        "myTextField": {"type": "text"}
      }
    }
  }
}'

### 인덱스 템플릿 조회
curl -XGET "http://192.168.56.10:9200/_index_template/my_template?pretty"


### 인덱스 템플릿을 사용하여 인덱스 생성
curl -XPUT "http://192.168.56.10:9200/pattern_test_index-1"
>>> 인덱스 조회 : curl -XGET "http://192.168.56.10:9200/pattern_test_index-1?pretty"


### 인덱스 템플릿 리스트 조회
curl -XGET "http://192.168.56.10:9200/_index_template?pretty" | jq '.index_templates[].name'

인덱스 템플릿 테스트 결과

 

 

(4-2) 컴포넌트 템플릿

  • 인덱스 템플릿 사용시 중복되는 부분 존재.. 이런 부분 재사용할 수 있는 작은 템플릿 블록으로 쪼갠것이 컴포넌트 템플릿
### 인덱스 컴포넌트 템플릿 테스트
1) mappings 필드 컴포넌트 템플릿
curl -XPUT "http://192.168.56.10:9200/_component_template/timestamp_mappings" -H "Content-Type: application/json" -d \
'{
  "template": {
    "mappings": {
      "properties": {
        "timestamp": {"type": "date"}
      }
    }
  }
}'

2) settings 필드 컴포넌트 템플릿
curl -XPUT "http://192.168.56.10:9200/_component_template/my_shard_settings" -H "Content-Type: application/json" -d \
'{
  "template": {
    "settings": {"number_of_shards": 2, "number_of_replicas": 2}
  }
}'
>>> timestamp_mappings 템플릿 에는 timestamp 필드 설정을 담음
>>> my_shard_settings 템플릿 에는 샤드와 관련된 설정을 담음

### 컴포넌트 템플릿 나열
curl -XGET "http://192.168.56.10:9200/_component_template?pretty" | jq '.component_templates[].name'


### 인덱스 템플릿 생성시 컴포넌트 템플릿 사용 테스트
!! composed_of 필드에 재사용할 컴포넌트 템플릿 블록을 넣음 !!
curl -XPUT "http://192.168.56.10:9200/_index_template/my_template2" -H "Content-Type: application/json" -d \
'{
  "index_patterns": ["timestamp_index-*"],
  "composed_of": ["timestamp_mappings", "my_shard_settings"]
}'


### 컴포넌트 템플릿을 사용한 인덱스 템플릿을 활용해 인덱스 생성
curl -XPUT "http://192.168.56.10:9200/timestamp_index-001"
>>> 설정 확인 : curl -XGET "http://192.168.56.10:9200/timestamp_index-001?pretty"

컴포넌트 템플릿 테스트 결과

 

 

(4-3) 레거시 템플릿 : 이전 버전 인덱스 템플릿 ( 컴포넌트 템플릿을 조합하지 못하는 거 이외 동일 )

 

(4-4) 동적 템플릿

  • 인덱스에 새로 들어온 필드 매핑을 사전에 정의한 대로 동적으로 생성하는 기능
### 동적 템플릿 테스트
!! mappings 안에 dynamic_templates 필드를 통해 지정 !!
curl -XPUT "http://192.168.56.10:9200/_index_template/dynamic_mapping_template" -H "Content-Type: application/json" -d \
'{
  "index_patterns": ["dynamic_mapping*"],
  "priority": 1,
  "template": {
    "settings": {"number_of_shards": 2, "number_of_replicas": 2},
    "mappings": {
      "dynamic_templates": [
        {
          "my_text": {
            "match_mapping_type": "string", "match": "*_text",
            "mapping": {"type": "text"}
          }
        },
        
        {
          "my_keyword": {
            "match_mapping_type": "string", "match": "*_keyword",
            "mapping": {"type": "keyword"}
          }
        }
      ]
    }
  }
}'
>>> 새로운 필드가 문자열일떄 _text로 끝나면 text타입, _keyword로 끝나면 keyword 타입 동적 지정


### 동적 템플릿 테스트
curl -XPUT "http://192.168.56.10:9200/dynamic_mapping-0218"
>>> 인덱스 조회 : curl -XGET "http://192.168.56.10:9200/dynamic_mapping-0218?pretty"

동적 템플릿 테스트 결과

 

인덱스 필드 동적 템플릿 조건 지정 필드

- match_mapping_type : 들어오는 데이터 타입 확인 후 적용

- match / umatch : 필드이름 지정된 패턴 매칭 확인 / 지정된 패턴 미매칭 확인

- path_match / path_unmatch : match / umatch 동일 하지만 전체 경로 이용 (ex. my_object.name.text*)

 

 

(4-5) 빌트인 인덱스 템플릿

  • 엘리스틱서치 미리 정의된 인덱스 템플릿 ( 내장 인덱스 템플릿 )

 

 

5. 라우팅 : 인덱스 구성 샤드 중 몇번 샤드 대상으로 작업 수행할지 지정하기 위한 값

  • 색인 시 라우팅 지정했으면, ( 조회, 업데이트, 삭제, 검색 ) 작업에도 똑같이 라우팅 지정!!
### 라우팅 테스트
1) 샤드 5를 가진 인덱스 생성
curl -XPUT "http://192.168.56.10:9200/routing_test" -H "Content-Type: application/json" -d \
'{
  "settings": {"number_of_shards": 5, "number_of_replicas": 1}
}'

2) 라우팅 미 지정 색인, 라우팅 지정 색인
curl -XPUT "http://192.168.56.10:9200/routing_test/_doc/1" -H "Content-Type: application/json" -d \
'{
  "login_id": "sy0218",
  "comment": "오!! 오!! 오!!",
  "created_at": "2024-12-13"
}'

curl -XPUT "http://192.168.56.10:9200/routing_test/_doc/2?routing=msg1" -H "Content-Type: application/json" -d \
'{
  "login_id": "jiwow0725",
  "comment": "하!! 하!! 하!!",
  "created_at": "2024-12-13"
}'

라우팅 지정, 미지정 색인 조회 결과

  • 라우팅 미 지정시 _routing 필드가 없지만 지정하여 색인한 문서는 "_routing" : "msg1"
  • es는 검색시 라우팅 기입 않하면 전체 샤드 대상 검색, 라우팅 값 명시 하면 단일 샤드 대상으로 검색
### 라우팅 지정, 미 지정 검색 테스트
1) 라우팅 미 지정 검색
curl -XGET "http://192.168.56.10:9200/routing_test/_search?pretty"

2) 라우팅 지정 검색
curl -XGET "http://192.168.56.10:9200/routing_test/_search?routing=msg1&pretty"

 

1) 라우팅 미지정 검색 ( 5개 샤드 검색 )

 

2) 라우팅 지정 검색 ( 단일 샤드 검색 )

 

 

(5-1) 인덱스 내 _id 고유성 보장

  • 인덱스 내 _id 값의 고유성 검증은 샤드 단위로 보장
  • 즉, 라우팅 값 다르게 지정하게 되면 한 인덱스 내에 같은 _id를 가진 문서가 여러개 생길수 있음...
  • 이것은 사용자 책임 ㅠㅠ, 문서 색인시 항상 라우팅 값을 올바르게 지정해야됨

 

 

(5-2) 인덱스 매핑에서 라우팅 필수로 지정

### 인덱스 매핑시 라우팅 필수 지정 설정 테스트
curl -XPUT "http://192.168.56.10:9200/routing_test2" -H "Content-Type: application/json" -d \
'{
  "mappings": {
    "_routing": {
      "required": true
    }
  }
}'

>>> mappings 필드에 ( "required": true ) 라우팅 값 팔수 설정
>>> 해당 인덱스에 라우팅 값이 명시되지 않음 ( 색인, 조회, 업데이트, 삭제 ) 작업은 실패하게 됨

### 라우팅 테스트
curl -XPUT "http://192.168.56.10:9200/routing_test2/_doc/1?pretty" -H "Content-Type: application/json" -d \
'{
  "comment": "no no routing routing"
}'

인덱스 문서 색인시 라우팅 미지정