도서 요약 / / 2022. 12. 22. 20:12

04. Mapping & Analysis

complete guide to elasticsearch를 정리한 자료입니다.
https://www.udemy.com/course/elasticsearch-complete-guide/

37. analysis 소개

Analysis

  • text analysis라고 불린다.
  • text 필드/값에 적용된다.
  • text 값은 문서를 인덱싱할 때 분석된다.
  • 결과는 효과적인 검색을 위해 데이터 구조에 저장된다.
  • _source 객체는 문서 검색할 때는 사용되지 않는다.
    • 문서 인덱싱할 때 정확한 값을 포함한다.

  • analyzer는 3가지 블록으로 구성되어 있다. (character filters, tokenizer, token filters)
  • text를 analyzing하는 결과는 검색가능한 데이터 구조에 저장된다.

Character filters

  • 원본 문자를 받아서 추가, 삭제, 변경을 통해 변형된다.
  • Analyzer는 0개 이상의 character filter를 포함한다.
  • character filter는 정의된 순서대로 적용된다.
  • 예(html_stripe 필터)
    • Input: "I'm in a good mood - and I love açaí!"
    • Output: "I'm in a good mood - and I love açaí!"

Tokenizers

  • Analyzer는 하나의 tokenizer를 포함한다.
  • 문자를 토크나이징한다. 예) 토큰으로 짜른다.
  • 문자는 토큰의 부분으로 자른다.
    • Input: "I REALLY like beer!"
    • Output: ["I", "REALLY", "like", "beer"]

Token filters

  • 입력으로 토크나이저이ㅡ 결과를 받는다. (Ex: 토큰)
  • token filter는 토큰을 추가, 수정, 삭제할 수 있다.
  • analyzer는 0개 이상의 token filter를 가진다.
  • token filter는 정의된 순서대로 적용된다.
  • 예: lowercase filter
    • Input: ["I", "REALLY", "like", "beer"]
    • Output: ["i", "really", "like", "beer"]

built-in, custom 컨포넌트

  • Built-in analyzer, character filters, token filters를 사용할 수 있다.
  • 또한 Custom analyzer를 사용할 수 있다.

[default analyzer의 동작방식]

38. Analyzer API 사용하기

아래 문장을 analyzer로 실행하면 standard analyzer로 실행이 된다.

POST _analyze
{
  "text": "2 guys walk into   a bar, but the third... DUCKS! :-)",
  "analyzer": "standard"
}

Standard analyzer는 특수문자는 모두 제거한다.

{
  "tokens" : [
    {
      "token" : "2",
      "start_offset" : 0,
      "end_offset" : 1,
      "type" : "<NUM>",
      "position" : 0
    },
    {
      "token" : "guys",
      "start_offset" : 2,
      "end_offset" : 6,
      "type" : "<ALPHANUM>",
      "position" : 1
    },
    {
      "token" : "walk",
      "start_offset" : 7,
      "end_offset" : 11,
      "type" : "<ALPHANUM>",
      "position" : 2
    },
    {
      "token" : "into",
      "start_offset" : 12,
      "end_offset" : 16,
      "type" : "<ALPHANUM>",
      "position" : 3
    },
    {
      "token" : "a",
      "start_offset" : 19,
      "end_offset" : 20,
      "type" : "<ALPHANUM>",
      "position" : 4
    },
    {
      "token" : "bar",
      "start_offset" : 21,
      "end_offset" : 24,
      "type" : "<ALPHANUM>",
      "position" : 5
    },
    {
      "token" : "but",
      "start_offset" : 26,
      "end_offset" : 29,
      "type" : "<ALPHANUM>",
      "position" : 6
    },
    {
      "token" : "the",
      "start_offset" : 30,
      "end_offset" : 33,
      "type" : "<ALPHANUM>",
      "position" : 7
    },
    {
      "token" : "third",
      "start_offset" : 34,
      "end_offset" : 39,
      "type" : "<ALPHANUM>",
      "position" : 8
    },
    {
      "token" : "ducks",
      "start_offset" : 43,
      "end_offset" : 48,
      "type" : "<ALPHANUM>",
      "position" : 9
    }
  ]
}

이는 아래와 같다.

POST _analyze
{
  "text": "2 guys walk into   a bar, but the third... DUCKS! :-)",
  "char_filter": [], 
  "tokenizer": "standard", 
  "filter": ["lowercase"]
}

39. 역색인(inverted indices) 이해하기

소개

  • 필드값들이 여라가지 데이터 구조 중 하나로 저장된다.
    • 데이터 구조는 필드 데이터 유형에 따라 다르다.
  • 효율적인 데이타 접근을 보장한다 - 예. 검색
  • Elasticsearch가 아니라 아파치 루씬이 처리한다.
  • 이번 내용은 역색인에 초점을 두고 있다.

역색인

  • 텀(term) 사이 매핑과 어느 문서가 포함하는가
  • 분석기 외부 관점에서는 텀(term)이라는 용어를 사용한다.
    • 토큰(token)이라는 용어는 분석기 관점에서만 사용된다.
    • 대부분은 텀(term)이라고 사용한다.
  • 텀(term)은 알파벳순으로 정렬되어 있다.
  • 역색인은 단순히 텀(term)과 문서 ID보다 더 많은 정보를 가지고 있다.
    • 예) 스코어를 위한 정보
  • 하나의 텍스트 필드 당 하나의 역색인 파일이 생긴다.
  • 예를 들면 다른 데이터 타입은 BKD 트리를 사용한다. (예: 숫자, 날짜, 위치)

  • ducks 를 검색할 때 역색인에서 어느 문서가 ducks를 포함하고 있는지 확인하면 된다. (#1, #3)

  • 위와 같이 다른 2개의 문서를 인덱싱을 하면 역색인 파일은 각 필드별로 각각 만들어진다. (name 필드, description 필드)

40. Mapping 소개

mapping은 무엇인가?

  • 문서 구조를 정의한다 (예: 필드와 데이터 타입)
    • 또한 값이 어떻게 인덱싱되는지 구성하는데 사용
  • 관계형 DB에서 테이블 스키마와 유사
  • 명시적인(Explicit) 매핑
    • 우리가 직접 필드 매핑을 정의
  • 동적(Dynamic) 매핑
    • elasticsearch가 필드 매핑을 정의한다.

41. 데이터 타입

object, text, float, boolean, interger, double, short, date

object 데이터 타입

  • JSON object에 사용
  • objects는 nested가 될 수 있다.
  • Properties 파라미터를 사용하여 매핑된다.
  • objects는 루씬(Lucene)에 객체로 저장되지 않는다.
    • objects는 유효한 JSON을 인덱싱할 수 있도록 변형된다.
    • 특별한 경우에 objects는 flatten된다.

객체 array인 경우는 아래처럼 저장된다.

QUERY: MATCH products WHERE review.author == "John Doe" AND reviews.rating >= 4.0

위와 같이 쿼리를 실행하면 해당 문서가 검색되는 것으로 나온다. 하지만 실제로는 John Doe는 3.5이라서 검색되지 않아야 한다.

이는 우리가 원하는 결과가 아니다.

nested 데이터 타입

  • Object 데이터 타입과 유사하지만 객체간의 관계를 유지한다.
    • objects 배열을 인덱싱할 때 유용하다.
  • 독립적으로 objects를 쿼리할 수 있게 한다.
    • nested 쿼리를 사용해야 한다.
  • nested object는 숨겨진 문서로 저장된다.

QUERY: MATCH products WHERE review.author == "John Doe" AND reviews.rating >= 4.0

객체는 독립적으로 저장되기 때문에 쿼리는 예상대로 동작한다.

루씬은 object의 개념이 없기 때문에 이러한 객체는 어떻게 저장될까?

위와 같이 review를 10개 가진 하나의 문서(product)를 저장하면 총 11개의 문서가 저장된다. (1개의 product + 10개의 review)

keyword 데이터 타입

  • 정확한 값이 일치할 때 사용
  • 일반적으로 필터링, 집합(aggregation), 정렬에 사용된다.
  • 예) PUBLISHED 상태의 기사를 검색
  • Full-text 검색을 위해서는 text 데이터 타입을 사용해라.
    • 예) 기사 내용을 검색

42. Keyword 데이터 유형은 어떻게 동작하는가?

keyword 필드는 어떻게 분석되는가?

  • Keyword 필드는 keyword analyzer로 분석된다.
  • keyword analyzer는 no-op analyzer이다. (어떤 것도 하지 않는다는 뜻)
    • 단일 토큰으로 수정되지 않은 문자열을 출력
    • 이 토큰이 역색인 파일에 추가된다.
  • keyword 필드는 정확히 일치, 집합(aggregation), 정렬에 사용된다.
POST _analyze
{
  "text": "2 guys walk into   a bar, but the third... DUCKS! :-)",
  "analyzer": "keyword"
}
{
  "tokens" : [
    {
      "token" : "2 guys walk into   a bar, but the third... DUCKS! :-)",
      "start_offset" : 0,
      "end_offset" : 53,
      "type" : "word",
      "position" : 0
    }
  ]
}

43. type coercion 이해

  • 데이터 유형이 문서 인덱싱될 때 확인된다.
    • 검증되고 유효하지 않은 값들은 실패한다.
    • 예) text 필드에 객체 인덱싱할 때
  • 때로는, 잘못된 데이터 타입도 가능하다.

아래 쿼리를 차례대로 실행하자.

PUT coercion_test/_doc/1
{
  "price": 7.4
}

price에 float 타입으로 자동으로 지정된다.

PUT coercion_test/_doc/2
{
  "price": "7.4"
}

"7.4" 문자열 입력 시 7.4의 float타입으로 변경되어 저장된다.

PUT coercion_test/_doc/3
{
  "price": "7.4m"
}

"7.4m" 문자열 입력 시 float 타입으로 변경을 할 수 없으므로 실패한다.

{
  "error" : {
    "root_cause" : [
      {
        "type" : "mapper_parsing_exception",
        "reason" : "failed to parse field [price] of type [float] in document with id '3'. Preview of field's value: '7.4m'"
      }
    ],
    "type" : "mapper_parsing_exception",
    "reason" : "failed to parse field [price] of type [float] in document with id '3'. Preview of field's value: '7.4m'",
    "caused_by" : {
      "type" : "number_format_exception",
      "reason" : "For input string: \"7.4m\""
    }
  },
  "status" : 400
}

조회를 해보자.

GET coercion_test/_doc/1
{
  "_source" : {
     "price" : 7.4
  }
}

7.4 float타입으로 조회된다.

2번 문서를 조회하면

GET coercion_test/_doc/2
{
  "_source" : {
    "price" : "7.4"
  }
}

"7.4" 문자열로 저장된다.

_source object 이해

  • 인덱싱할 때 주어진 값을 가진다. ("7.4")
    • 인덱싱된 값이 아니다 (7.4)
  • 검색 쿼리는 인덱싱된 값을 사용한다. (_source값이 아니다)
    • BKD tree, 역색인, etc
  • _source는 값이 어떻게 인덱싱되었는지를 나타내지 않는다.
    • _source에서 값을 사용하려면 coercion를 명심해라.
    • 이 예제에서는 string 혹은 float이 될 수 있다.

몇 가지 더

  • Integer 필드에 floating point를 넣으면 integer로 들어갈 것이다.
  • coercion은 동적 매핑에서는 사용되지 않는다.
    • 새 필드에 "7.4"를 넣으면 text 매핑을 만들것이다.
  • 항상 올바른 데이터 유형을 사용하도록 노력해라.
    • 특히 필드를 처음 인덱싱 할 때
  • coercion을 비활성화 하는 것은 선택사항이다.
    • 기본값은 enabled이다.

44. Arrays 이해

  • Array 데이터 타입 같은 것은 없다.
  • 어떤 필드도 0개 이상의 값을 가질수 있다.
    • 특별한 설정이나 매핑정보가 필요 없다.
    • 문서 인덱싱할 때 array를 사용하면 된다.
  • Product 인덱스에 tags 필드를 사용했었다.

array를 위한 특별한 표시는 없다.

내부적으로 어떻게 저장이 되는 걸까?

{
  "text": ["Strings are simply", "merged together"],
  "analyzer": "standard"
}
{
  "tokens" : [
    {
      "token" : "strings",
      "start_offset" : 0,
      "end_offset" : 7,
      "type" : "<ALPHANUM>",
      "position" : 0
    },
    {
      "token" : "are",
      "start_offset" : 8,
      "end_offset" : 11,
      "type" : "<ALPHANUM>",
      "position" : 1
    },
    {
      "token" : "simply",
      "start_offset" : 12,
      "end_offset" : 18,
      "type" : "<ALPHANUM>",
      "position" : 2
    },
    {
      "token" : "merged",
      "start_offset" : 19,
      "end_offset" : 25,
      "type" : "<ALPHANUM>",
      "position" : 3
    },
    {
      "token" : "together",
      "start_offset" : 26,
      "end_offset" : 34,
      "type" : "<ALPHANUM>",
      "position" : 4
    }
  ]
}

이 의미는 string은 배열이 아니라 하나의 문자열로 취급된다는 의미이다.

제약사항

  • Array 값은 동일한 데이터 타입이어야 한다.
// correct data type
[ "electronics", "expensive", "popular" ]
[ 37, 45, 9 ]
[ true, false, true ] 
[ {"name": "Coffee Maker"}, {"name": "Toaster"}, {"name": "Blendar"} ]

//coercion
[ true, false, "true"]
[ "electronics", "expensive", 47]
[ 37, 45, "9" ]
[ true, false, "true" ]

// cannot coercion
[ {"name": "Coffee Maker"}, {"name": "Toaster"}, false]
  • Coercion은 이미 매핑된 필드에서만 동작한다.
    • 동적 매핑으로 필드 매핑을 만들려면 array는 동일한 데이터 타입이어야 한다.
  • coercion을 추천하지 않는다. (적어도 의도적으로는 사용하지 마라)

Nested arrays

  • Arrays는 nested array를 포함할 수도 있다.
  • Arrays는 인덱싱될 때 flatten 된다.
  • [ 1, [2, 3] ]은 [ 1, 2, 3 ]이 된다.

array 객체를 독립적으로 조회하려면 nested 데이터 타입을 사용해야 한다는 것을 꼭 기억하자.

45. 명시적인 매핑 추가

PUT reviews
{
  "mappings": {
    "properties": {
      "rating": { "type": "float" },
      "content": { "type": "text" },
      "product_id": { "type": "integer" },
      "author": {
        "properties": {
          "first_name": { "type": "text" }, 
          "last_name": { "type": "text" },
          "email": { "type": "keyword" }
        }
      }
    }
  }
}
PUT reviews/_doc/1
{
  "rating": 5.0,
  "content": "Outstanding course! Bo really taught me a lot about Elasticsearch!",
  "product_id": 123,
  "author": {
    "first_name": "John",
    "last_name": "Doe",
    "email": {}
  }
}

성공적으로 인덱싱된다.

email필드에 객체를 저장해보자.

PUT reviews/_doc/1
{
  "rating": 5.0,
  "content": "Outstanding course! Bo really taught me a lot about Elasticsearch!",
  "product_id": 123,
  "author": {
    "first_name": "John",
    "last_name": "Doe",
    "email": {}
  }
}

인덱싱이 실패하는 것을 확인할 수 있다.

{
  "error" : {
    "root_cause" : [
      {
        "type" : "mapper_parsing_exception",
        "reason" : "failed to parse field [author.email] of type [keyword] in document with id '1'. Preview of field's value: '{}'"
      }
    ],
    "type" : "mapper_parsing_exception",
    "reason" : "failed to parse field [author.email] of type [keyword] in document with id '1'. Preview of field's value: '{}'",
    "caused_by" : {
      "type" : "illegal_state_exception",
      "reason" : "Can't get text on a START_OBJECT at 8:14"
    }
  },
  "status" : 400
}

46. 매핑 조회

GET reviews/_mapping

결과는 아래와 같다.

{
  "reviews" : {
    "mappings" : {
      "properties" : {
        "author" : {
          "properties" : {
            "email" : {
              "type" : "keyword"
            },
            "first_name" : {
              "type" : "text"
            },
            "last_name" : {
              "type" : "text"
            }
          }
        },
        "content" : {
          "type" : "text"
        },
        "product_id" : {
          "type" : "integer"
        },
        "rating" : {
          "type" : "float"
        }
      }
    }
  }
}

특정 필드(content)를 조회

GET reviews/_mapping/field/content

결과는 아래와 같다.

{
  "reviews" : {
    "mappings" : {
      "content" : {
        "full_name" : "content",
        "mapping" : {
          "content" : {
            "type" : "text"
          }
        }
      }
    }
  }
}

author.email 필드를 조회

GET reviews/_mapping/field/author.email

결과는 아래와 같다.

{
  "reviews" : {
    "mappings" : {
      "author.email" : {
        "full_name" : "author.email",
        "mapping" : {
          "email" : {
            "type" : "keyword"
          }
        }
      }
    }
  }
}

47. 필드명에 .(dot)을 사용하기

PUT reviews_dot_notation
{
  "mappings": {
    "properties": {
      "rating": { "type": "float" },
      "content": { "type": "text" },
      "product_id": { "type": "integer" },
      "author.first_name": { "type": "text" }, 
      "author.last_name": { "type": "text" },
      "author.email": { "type": "keyword" }
    }
  }
}

mapping을 조회하자.

GET reviews_dot_notation/_mapping

결과는 아래와 같다.

elasticsearch는 결과를 아래와 같이 json 형식으로 변형을 한다.

{
  "reviews_dot_notation" : {
    "mappings" : {
      "properties" : {
        "author" : {
          "properties" : {
            "email" : {
              "type" : "keyword"
            },
            "first_name" : {
              "type" : "text"
            },
            "last_name" : {
              "type" : "text"
            }
          }
        },
        "content" : {
          "type" : "text"
        },
        "product_id" : {
          "type" : "integer"
        },
        "rating" : {
          "type" : "float"
        }
      }
    }
  }
}

객체로 설정한 것과 결과가 같다.

48. 기존 인덱스에 매핑 추가하기

Review 인덱스에 created_at 추가하기

PUT reviews/_mapping
{
  "properties": {
    "created_at": { "type": "date" }
  }
}

49. elasticsearch에 date는 어떻게 동작하는가?

date 필드 소개

  • 3가지 중 하나의 방법으로 정의된다.
    • Formatted 문자
    • Epoch(long) 이후 milliseconds
    • Epoch(integer) 이후 seconds
  • epoch는 1970년 1월 1일을 말한다.
  • Custom format이 지원된다.

date 필드의 기본 동작

  • 3가지 지원 포맷
    • time이 없는 date
    • time이 있는 date
    • epoch(long) 이후 milliseconds
  • 어떤 것도 설정되지 않으면 UTC timezone이라고 판단
  • Date는 ISO 8701 명세에 의해 포맷팅되어야 한다.

date 필드는 어떻게 저장되는가

  • 내부적으로 epoch(long) 이후 milliseconds로 저장된다.
  • 인덱싱 때 제공한 값은 내부적으로 long 으로 변형된다.
  • Date는 UTC timestamp로 변형된다.
  • 조회할 때도 동일하게 변형된다.

50. 필드를 생략하면 어떻게 처리될까?

필드 생략

  • 모든 필드는 선택사항이다.
  • 문서를 인덱싱할 때 필드를 생략할 수 있다.
  • 예) null값을 허용하는 RDB와는 다르다.
  • 애플리케이션 레벨에서 무결성 체크가 필요하다.
    • 예) 필수값
  • 필드매핑을 추가하는 것은 필드를 필수값으로 만들지는 않는다.
  • 검색은 자동으로 생략된 필드를 처리한다.

51. 매핑 파라미터

format 파라미터

  • Date 필드의 포맷을 정의
  • 가능하다면 기본 포맷을 사용하기를 권장
    • "strict_date_optional_time||epoch_millis"
  • java의 DateFormatter 구문 사용하기
    • 예) "dd/MM/yyyy"
  • 내장 포맷 사용하기
    • 예) "epoch_second"

properties 파라미터

  • object와 nested 필드의 nested 필드를 정의한다.

coerce 파라미터

  • 값의 coercion을 가능/불가능하게 한다. (기본값: enabled)

doc_values 소개

  • elasticsearch는 다양한 데이터 구조를 사용한다.
    • 하나의 데이터 구조는 다양한 용도로 사용하지 못한다.
  • 역색인은 text를 검색하는데 효과적이다.
    • 이 타입은 다른 다양한 패턴을 잘 처리하지 못한다.
  • "Doc values"는 루씬에서 사용되는 다른 데이터 구조이다.
    • 다른 데이터 패턴에 최적화되어 있다 (document -> terms)
  • 특히 inverted되지 않은 역색인
  • 정렬, 집합, 스크립팅
  • 대체용이 아닌 추가적인 데이터 구조이다.
  • elsticsearch는 자동으로 적절한 데이터 구조를 조회한다.

doc_values 비활성화

  • 디스크 저장공간을 절약하기 위해 doc_values를 false로 설정
    • 인덱싱 속도를 약간 증가시킨다.
  • 집합, 정렬, 스크립팅을 사용하지 않는다면 doc values를 비활성화해라.
  • 특히 대규모 인덱스에 유용하다. 작을 때는 의미 없다.
  • 문서를 새 인덱스로 reindexing하지 않고는 변경될 수 없다.

norms 파라미터

  • scoring을 위한 Normalization 요소
  • 종종 결과를 필터링하지 않고 순위를 매길수 있다.
  • Norms는 디스크 공간을 저장하기 위해 비활성화 될 수 있다.
    • scoring에 사용되지 않는 필드에 유용하다.
    • 여전히 필드는 필터링과 집합에 사용될 수 있다.

index 파라미터

  • 필드를 인덱싱하지 않는다.
  • 값은 여전히 _source 내에 저장된다.
  • 필드를 검색쿼리에서 사용하지 않을 때 유용하게 사용될 수 있다.
  • 디스크 공간 절약하는 것은 인덱싱 속도를 약간 증가시킨다.
  • 종종 time series 데이터에 사용된다.
  • indexing을 disabled한 필드는 여전히 집합(aggregation)에 사용될 수 있다.

null_value 파라미터

  • NULL 값은 인덱싱 혹은 검색될 수 없다.
  • NULL값을 다른 값으로 변경하기 위해 이 파라미터를 사용해라.
  • NULL 값에서만 동작한다.
  • 변경될 값은 필드의 데이터 타입과 동일해야 한다.
  • _source내에 저장된 값에는 영향을 주지 않는다.

copy_to 파라미터

  • 다양한 필드 값을 group 필드로 복사하는 데 사용
  • 단순히 대상 필드의 이름을 값으로 명시
  • 예) first_name과 last_name -> full_name
  • term/token이 아닌 값이 복사된다.
    • 대상 필드의 analyzer는 값에 대해 사용된다.
  • 대상 필드는 _source의 일부분이 아니다.

52. 기존 매핑 수정

  • product ID는 문자를 포함한다고 하자.
  • product_id 필드의 데이터 타입을 text 혹은 keyword로 변경하자
    • Full-text 검색으로 사용하지 않을 것이다.
    • 필터링 용으로 사용해서 keyword 데이터 타입이 좋다.

기존에 integer로 되어 있는 product_id를 keyword로 변경하자.

PUT reviews/_mapping
{
  "properties": {
    "product_id": {
      "type": "keyword"
    }
  }
}
{
  "error" : {
    "root_cause" : [
      {
        "type" : "illegal_argument_exception",
        "reason" : "mapper [product_id] of different type, current_type [integer], merged_type [keyword]"
      }
    ],
    "type" : "illegal_argument_exception",
    "reason" : "mapper [product_id] of different type, current_type [integer], merged_type [keyword]"
  },
  "status" : 400
}

오류가 발생한다.

매핑 수정의 제약사항

  • 일반적으로 elasticsearch 필드 매핑은 변경될 수 없다.
  • 새 필드 매핑을 추가할 수 있다.
  • 몇 개의 매핑 파라미터는 기존 매핑에 수정될 수 있다.

email필드에 ignore_above를 추가

PUT reviews/_mapping
{
  "properties": {
    "author": {
      "properties": {
        "email": {
          "type": "keyword",
          "ignore_above": 256
        }
      }
    }
  }
}
  • 매핑을 수정하는 것은 기존 문서에 문제가 될 수 있다.
    • 예를 들면 Text 값은 이미 분석되었다.
    • 어떤 데이터 타입간의 변경이 전체 데이터 구조를 새로 만들어야 할 수도 있다.
  • 인덱스에 데이터가 없는 경우라도 매핑을 수정할 수 없다.
  • 필드 매핑은 삭제될 수 없다.
    • 다만 문서를 인덱싱할 때 필드를 남겨둔다.
  • update by query API는 디스크 공간을 복구하는데 사용될 수 있다.
  • 해결책은 문서를 새 인덱스로 reindex하는 것이다.

53. Reindex API로 문서를 재색인하기

PUT reviews_new
{
  "mappings" : {
      "properties" : {
        "author" : {
          "properties" : {
            "email" : {
              "type" : "keyword",
              "ignore_above" : 256
            },
            "first_name" : {
              "type" : "text"
            },
            "last_name" : {
              "type" : "text"
            }
          }
        },
        "content" : {
          "type" : "text"
        },
        "created_at" : {
          "type" : "date"
        },
        "product_id" : {
          "type" : "keyword"
        },
        "rating" : {
          "type" : "float"
        }
      }
    }
  }
}

reviews를 reviews_new로 reindex해보자.

POST _reindex
{
  "source": {
    "index": "reviews"
  },
  "dest": {
    "index": "reviews_new"
  }
}

_source 데이터 타입

  • 데이터 타입은 값이 어떻게 인덱싱되는지 반영되지 않는다.
  • _source 는 인덱싱될 때 제공된 필드값을 포함한다.
  • 검색 결과에 _source값을 사용하는 것이 일반적이다.
    • Keyword 필드에 문자열을 기대할 것이다.
  • reindexing할 때 _source를 수정할 수 있다.
  • 선택적으로 애플리케이션 레벨에서 처리될 수 있다.

reindexing할 때 _source값을 변경해보자.

POST _reindex
{
  "source": {
    "index": "reviews"
  },
  "dest": {
    "index": "reviews_new"
  },
  "script": {
    "source": """
      if (ctx._source.product_id != null) {
        ctx._source.product_id = ctx._source.product_id.toString();
      }
    """
  }
}

Matching 쿼리를 사용하여 문서를 reindexing하기

POST _reindex
{
  "source": {
    "index": "reviews",
    "query": {
      "match_all": {}
    }
  },
  "dest": {
    "index": "reviews_new"
  }
}

필드를 제거하고 reindexing하기

POST _reindex
{
  "source": {
    "index": "reviews",
    "_source": ["content", "created_at", "rating"]
  },
  "dest": {
    "index": "reviews_new"
  }
}

필드명 변경 (content -> comment)

POST _reindex
{
  "source": {
    "index": "reviews"
  },
  "dest": {
    "index": "reviews_new"
  },
  "script": {
    "source": """
      ctx._source.comment = ctx._source.remove("content");
    """
  }
}

rating이 4.0이하는 무시하기

POST _reindex
{
  "source": {
    "index": "reviews"
  },
  "dest": {
    "index": "reviews_new"
  },
  "script": {
    "source": """
      if (ctx._source.rating < 4.0) {
        ctx.op = "noop"; # delete로 설정할 수도 있다.
      }
    """
  }
}

script로 ctx.op 사용하기

  • 일반적으로 query 파라미터 사용은 가능하다.
  • 좀 더 폭넓은 경우에 ctx.op가 사용될 수 있다.
  • query 파라미터를 사용하는 것이 성능상 더 좋다.
  • "delete" 를 명시하면 대상 인덱스에서 문서를 삭제한다.

reindex API 파라미터

  • 더 많은 파라미터가 사용될 수 있다.
    • 예) version conflict 처리
  • 문서를 reindexing하기 전에 snapshot이 생성된다.
  • version conflict가 발생하면 기본적으로 취소된다.
  • 대상 인덱스는 반드시 데이터가 있어야 한다.

batch & throttling

  • reindex API는 batch로 동작한다.
    • Update by query와 delete by query API와 같다.
    • 내부적으로 scroll API를 사용한다.
    • 수백만 건의 문서가 효과적으로 인덱싱될 수 있다.
  • throttling은 성능에 영향도를 제한하기 위해 사용될 수 있다.
    • 운영 cluster에 유용하다.
  • 많은 문서를 reindex하려면 문서를 확인해라.

54. 필드 별칭(alias) 정의

필드 별칭(alias)

  • 필드명은 문서를 reindexing할 때 변경될 수 있다.
    • 대부분의 문서에서는 의미없는 일일 수도 있다.
  • 필드 별칭을 사용하는 것은 선택사항이다.
    • 문서를 reindex할 필요가 없다.
    • comment를 content로 변경해보자.
    • 별칭은 쿼리 내에서 사용될 수 있다.
    • 별칭은 필드매핑을 정의된다.

content에 별칭으로 comment를 추가하자.

PUT reviews/_mapping
{
  "properties": {
    "comment": {
      "type": "alias",
      "path": "content"
    }
  }
}

content로 검색하면 결과가 나온다.

GET reviews/_search
{
  "query": {
    "match": {
      "content": "outstanding"
    }
  }
}

별칭으로 설정한 comment로 검색해도 결과가 나온다.

GET reviews/_search
{
  "query": {
    "match": {
      "comment": "outstanding"
    }
  }
}

필드 별칭 업데이트

  • 필드 별칭은 실제로 업데이트 될 수 있다.
    • 실제 대상 필드
  • 단순히 새 path값으로 매핑 업데이트를 수행한다.
  • 별칭은 인덱싱에 영향을 주지 않으므로 가능하다.
    • Query 수준의 구성이다.

index 별칭

  • 필드 별칭과 유사하게 elasticsearch는 index 별칭을 지원한다.
  • 일반적으로 대용량을 처리할 때 사용된다.

55. multi 필드 매핑

PUT multi_field_test
{
  "mappings": {
    "properties": {
      "description": {
        "type": "text"
      },
      "ingredients": {
        "type": "text",
        "fields": {
          "keyword": {
            "type": "keyword" 
          }
        }
      }
    }
  }
}

ingredients에 text타입 외에 keyword 타입을 정의

POST multi_field_test/_doc
{
  "description": "To make this spaghetti carbonara, you first need to...",
  "ingredients": ["Spaghetti", "Bacon", "Eggs"]
}

아래와 같이 검색해도 동일한 결과가 나온다.

GET multi_field_test/_search
{
  "query": {
    "match": {
      "ingredients": "Spaghetti"
    }
  }
}

GET multi_field_test/_search
{
  "query": {
    "term": {
      "ingredients.keyword": "Spaghetti"
    }
  }
}

56. index template

index template 소개

  • index template은 settings와 mappings를 명시한다.
  • 한개 이상의 패턴과 일치하는 인덱스에 적용된다.
  • 패턴은 wildcard를 포함할 수 있다.
  • index template은 새 인덱스를 만들 때 적용된다.
PUT _template/access-logs
{
  "index_patterns": ["access-logs-*"], 
  "settings": {
    "number_of_shards": 2,
    "index.mapping.coerce": false
  }, 
  "mappings": {
    "properties": {
      "@timestmap": {
        "type": "date"
      },
      "url.original": {
        "type": "keyword"
      },
      "http.request.referer": {
        "type": "keyword"
      },
      "http.response.status.code": {
        "type": "long"
      }
    }
  }
}

Index template을 정의했다.

새 인덱스를 만들자.

PUT access-logs-2020-01-01

매핑 정보를 조회해보자.

GET access-logs-2020-01-01/_mapping

index pattern에서 만들 패턴으로 생성이 되었다.

{
  "access-logs-2020-01-01" : {
    "mappings" : {
      "properties" : {
        "@timestmap" : {
          "type" : "date"
        },
        "http" : {
          "properties" : {
            "request" : {
              "properties" : {
                "referer" : {
                  "type" : "keyword"
                }
              }
            },
            "response" : {
              "properties" : {
                "status" : {
                  "properties" : {
                    "code" : {
                      "type" : "long"
                    }
                  }
                }
              }
            }
          }
        },
        "url" : {
          "properties" : {
            "original" : {
              "type" : "keyword"
            }
          }
        }
      }
    }
  }
}

index template의 우선순위

  • 새 인덱스는 여러가지 index template에 해당될 수 있다.
  • Order 파라미터가 index template의 우선순위를 정의하는데 사용될 수 있다.
    • 값은 integer이다.
    • 작은 값을 가진 template이 우선이다.

index template 수정하기

PUT _template/access-logs

index template 조회하기

GET _template/access-logs

index template 삭제하기

DELETE _template/access-logs

57. Elastic Common Schema(ECS) 소개

ECS는 무엇인가?

  • 공통 필드에 대한 명세이고 어떻게 매핑되는가
  • ECS 전에필드 이름간 연결이 없었다.
  • nginx에서 로그를 취합하는 것은 apache와 다른 이름을 사용할 것이다.
  • ECS는 동일한 것에 대한 공통 필드를 의미한다.
    • 예) @timestamp
  • 사용 케이스와 무관하다.
  • 필드 그룹은 필드 셋을 의미한다.

ECS의 사용

  • ECS에서 문서는 이벤트를 의미한다.
    • ECS는 이벤트가 아닌 필드를 사용할 수 없다.
  • 주로 표준 이벤트에 유용하다.
    • 예) 웹서버 로그, 운영체제 지표 등
  • ECS는 자동으로 elastic stack 제품군에서 처리된다.
    • 만일 elastic stack을 사용한다면 ECS를 설정할 필요가 없다.
  • ECS를 사용할 필요가 없지만 무엇인지 알 필요는 있다.

58. 동적(dynamic) 매핑 소개

POST my-index/_doc
{
  "tags": ["computer", "electronics"],
  "in_stock": 4,
  "created_at": "2020/01/01 00:00:00"
}

문서를 추가하면 아래 처럼 자동으로 매핑이 생긴다.

{
  "my-index" : {
    "mappings" : {
      "properties" : {
        "created_at" : {
          "type" : "date",
          "format" : "yyyy/MM/dd HH:mm:ss||yyyy/MM/dd||epoch_millis"
        },
        "in_stock" : {
          "type" : "long"
        },
        "tags" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        }
      }
    }
  }
}
JSON ELASTICSEARCH
string 다음 중 하나 keyword 매핑을 가진 text 필드 date 필드 (float 혹은 long 필드)
integer long
floating point number float
boolean boolean
object object
Array 첫번째 값이 non-null인지에 따라

59. 명시적인 매핑과 동적 매핑 연결

first_name은 text타입으로 매핑하고 last_name은 동적매핑으로 추가하면 어떻게 구성될까?

first_name 매핑 추가

PUT people
{
  "mappings": {
    "properties": {
      "first_name": {
        "type": "text"
      }
    }
  }
}

last_name 필드 추가

POST people/_doc
{
  "first_name": "Bo",
  "last_name": "Andersen"
}

people 조회

GET people/_mapping

first_name은 text로 매핑되지만 last_name은 text와 keyword로 매핑된다.

{
  "people" : {
    "mappings" : {
      "properties" : {
        "first_name" : {
          "type" : "text"
        },
        "last_name" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        }
      }
    }
  }
}

60. 동적 매핑 구성하기

PUT people
{
  "mappings": {
    "dynamic": false,
    "properties": {
      "first_name": {
        "type": "text"
      }
    }
  }
}
POST people/_doc
{
  "first_name": "Bo",
  "last_name": "Andersen"
}

매핑을 조회하면

GET people/_mapping

first_name만 매핑으로 연결되어 있다.

{
  "people" : {
    "mappings" : {
      "dynamic" : "false",
      "properties" : {
        "first_name" : {
          "type" : "text"
        }
      }
    }
  }
}

데이터를 조회하면 데이터는 입력되어 있다.

GET people/_search

dynamic을 false로 설정

  • 새 필드가 무시된다.
    • 인덱싱이 되지 않지만 _source에는 여전히 표시된다.
  • last_name 필드에 역색인파일이 생성이 되지 않는다.
    • 필드 조회를 하면 결과가 나오지 않는다.
  • 필드는 매핑없이 인덱싱되지 않는다.
    • enabled될 때 값을 인덱싱하기 전에 동적 매핑을 생성한다.
  • 새 필드는 명시적인 매핑되어야 한다.

더 좋은 방법

  • dynamic을 strict으로 설정
  • Elsticsearch는 매핑되지 않은 필드를 무시한다.
    • 모든 필드는 명시적으로 매핑되어야 한다.
    • 관계형 DB와 유사
PUT people
{
  "mappings": {
    "dynamic": "strict",
    "properties": {
      "first_name": {
        "type": "text"
      }
    }
  }
}

매핑되지 않은 값을 입력하면

POST people/_doc
{
  "first_name": "Bo",
  "last_name": "Andersen"
}

오류가 발생한다.

{
  "error" : {
    "root_cause" : [
      {
        "type" : "strict_dynamic_mapping_exception",
        "reason" : "mapping set to strict, dynamic introduction of [last_name] within [_doc] is not allowed"
      }
    ],
    "type" : "strict_dynamic_mapping_exception",
    "reason" : "mapping set to strict, dynamic introduction of [last_name] within [_doc] is not allowed"
  },
  "status" : 400
}

61. 동적 template

매핑을 정의

PUT dynamic_template_test
{
  "mappings": {
    "dynamic_templates": [
      {
        "integers": {
          "match_mapping_type": "long",
          "mapping": {
            "type": "integer"
          }
        }
      }
    ]
  }
}

데이터 추가

POST dynamic_template_test/_doc
{
  "in_stock": 123
}

데이터 조회

GET dynamic_template_test/_mapping

62. 매핑 권장사항

명시적인 매핑을 사용하라.

  • 동적 매핑은 편하지만 운영에서는 좋은 방법이 아니다.
  • 많은 문서를 저장할 때 디스크 공간을 절약하라
  • dynamic을 false가 아닌 strict로 설정하라.
    • 기대하지 않은 결과를 없애준다.

text 필드 매핑

  • string을 text와 keyword 둘다 매핑하지 마라.
    • 일반적으로 하나만 필요하다.
    • 각 매핑은 디스크 공간을 필요로 한다.
  • Full-text 검색이 필요한가?
    • Text 매핑을 추가하라
  • 집합, 정렬, 필터링이 필요한가?
    • keyword를 사용하라.

coercion을 비활성화해라

  • coercion은 잘못될 일이 발생할 수 있다.
  • 의도한 대로 하도록 노력해라.
  • 가능하면 올바른 데이터 타입을 사용해라.

적절한 numeric 데이터 타입을 사용하라.

  • 일반적인 숫자에서는 integer 데이터 타입이면 충분하다.
    • long은 큰 숫자를 저장할 수 있지만 많은 디스크 공간을 사용한다.
  • decimal 숫자에서는 float 데이터 타입이면 충분하다.
    • double은 좀 더 정확하지만 디스크 공간을 2배 사용한다.
    • 보통 float이면 충분하다.

매핑 파라미터

  • 정렬, 집합, 스크립팅이 필요없다면 doc_values를 false로 설정하라.
  • scoring이 필요없다면 norms를 false로 설정하라.
  • 값을 필터링할 필요 없다면 index를 false로 설정하라.
    • 여전히 집합(aggregation)을 사용할 수 있다.

63. stemming & stop words

)

stemming 소개

  • stemming은 단어들을 기본형으로 줄여준다.
    • 예) loved -> love, drinking -> drink
    • 모든 단어를 올바르게 줄이지는 못한다.

stop words 소개

  • Text 분석에 필터링 되어야 할 단어들
    • "a", "the", "at", "of", "on" 등과 같은 일반적인 단어들
  • 이런 단어들은 scoring할 때 크게 의미 없다.
  • 특정 단어를 없앨 때 사용
    • 과거보다는 현재 elasticsearch에서 덜 사용한다.
      • Relevance 알고리즘이 많이 좋아졌다.
  • 기본은 제거되지 않고 일반적으로 권장되지 않는다.

64. Analyzer와 검색 쿼리

65. 내장 analyzer

standard analyer

  • text를 단어 단위로 짜르고 구두점을 제거한다.
    • standard tokenizer
  • lowercase token filter로 문자를 소문자로 바꾼다.
  • stop token filter를 포함 (기본은 비활성화)

예제

simple analyzer

  • standard analyzer와 유사
    • 문자 외에 다른 단어를 만나면 토큰으로 구분
  • 문자를 lowercase tokenizer로 소문자로 변환

whitespace analyzer

  • 공백으로 토큰을 분리
  • 문자를 소문자로 바꾸지 않는다.

keyword analyzer

  • 입력 text를 그대로
    • 하나의 토큰으로 사용
  • Keyword 필드에 기본으로 사용

pattern analyzer

  • 토큰을 분리하기 위해 정규 표현식이 사용된다.
    • 일치하는 text를 토큰으로 구분한다.
  • 이 analyzer는 유연하게 사용할 수 있다.
  • 기본 패턴을 단어가 글자이다. (\W+)
  • 소문자가 기본이다.

english analyzer

내장 analyzer 구성하기

PUT products
{
  "settings": {
    "analysis": {
      "analyzer": {
        "remove_english_stop_words": {
          "type": "standard",
          "stopwords": "_english_"
        }
      }
    }
  }
}

68. custom analyzer 생성

PUT analyzer_test
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_custom_analyzer": {
          "type": "custom",
          "char_filter": ["html_strip"],
          "tokenizer": "standard",
          "filter": [
            "lowercase",
            "stop",
            "asciifolding"
          ]
        }
      }
    }
  }
}
POST analyzer_test/_analyze
{
  "analyzer": "my_custom_analyzer", 
  "text": "I&apos;m in a <em>good</em> mood&nbsp;-&nbsp;and I <strong>love</strong> açaí!"
}

결과는 다음과 같다.

{
  "tokens" : [
    {
      "token" : "i'm",
      "start_offset" : 0,
      "end_offset" : 8,
      "type" : "<ALPHANUM>",
      "position" : 0
    },
    {
      "token" : "good",
      "start_offset" : 18,
      "end_offset" : 27,
      "type" : "<ALPHANUM>",
      "position" : 3
    },
    {
      "token" : "mood",
      "start_offset" : 28,
      "end_offset" : 32,
      "type" : "<ALPHANUM>",
      "position" : 4
    },
    {
      "token" : "i",
      "start_offset" : 49,
      "end_offset" : 50,
      "type" : "<ALPHANUM>",
      "position" : 6
    },
    {
      "token" : "love",
      "start_offset" : 59,
      "end_offset" : 72,
      "type" : "<ALPHANUM>",
      "position" : 7
    },
    {
      "token" : "acai",
      "start_offset" : 73,
      "end_offset" : 77,
      "type" : "<ALPHANUM>",
      "position" : 8
    }
  ]
}

69. 기존 인덱스에 analyzer 추가

위에서 만든 custom_analyer를 변경하자.

PUT analyzer_test/_settings
{
  "analysis": {
    "analysis": {
      "analyzer": {
        "my_second_analyzer": {
          "type": "custom",
          "char_filter": ["html_strip"],
          "tokenizer": "standard",
          "filter": [
            "lowercase",
            "stop",
            "asciifolding"
          ]
        }
      }
    }
  }
}

실행하면 아래와 같은 오류가 발생한다.

{
  "error" : {
    "root_cause" : [
      {
        "type" : "illegal_argument_exception",
        "reason" : "Can't update non dynamic settings [[index.analysis.analysis.analyzer.my_second_analyzer.char_filter, index.analysis.analysis.analyzer.my_second_analyzer.type, index.analysis.analysis.analyzer.my_second_analyzer.tokenizer, index.analysis.analysis.analyzer.my_second_analyzer.filter]] for open indices [[analyzer_test/iINbtwEpRW6k9TXwQDEoVA]]"
      }
    ],
    "type" : "illegal_argument_exception",
    "reason" : "Can't update non dynamic settings [[index.analysis.analysis.analyzer.my_second_analyzer.char_filter, index.analysis.analysis.analyzer.my_second_analyzer.type, index.analysis.analysis.analyzer.my_second_analyzer.tokenizer, index.analysis.analysis.analyzer.my_second_analyzer.filter]] for open indices [[analyzer_test/iINbtwEpRW6k9TXwQDEoVA]]"
  },
  "status" : 400
}

open & closed 인덱스

  • Open 인덱스는 인덱싱하고 조회할 때 사용할 수 있다.
  • Closed 인덱스는 요청을 거부한다.
    • 읽기/쓰기 요청이 거부된다.
    • 특정 동작을 수행할 때 필요하다.

dynamic, static settings

  • dynamic settings는 인덱스를 close하지 않고 변경될 수 있다.
    • 다운타임이 필요 없다.
  • static settings는 인덱스를 먼저 closed해야 한다.
    • 인덱스는 사용할 수 없다.
  • Analysis settings는 static settings이다.

analyzer_test를 close해보자.

POST analyzer_test/_close

다시 custom_analyzer를 변경하자.

PUT analyzer_test/_settings
{
  "analysis": {
    "analysis": {
      "analyzer": {
        "my_second_analyzer": {
          "type": "custom",
          "char_filter": ["html_strip"],
          "tokenizer": "standard",
          "filter": [
            "lowercase",
            "stop",
            "asciifolding"
          ]
        }
      }
    }
  }
}

인덱스를 open하자.

POST analyzer_test/_open

_settings를 조회하면 analyzer가 추가된 것을 확인할 수 있다.

GET analyzer_test/_settings

인덱스 open/close

  • 빠르지만 운영 클러스터에서는 사용해서는 안된다.
    • 예) 다운타임이 있으면 안되는 중요 업무
  • 선택적으로 문서를 새 인덱스로 reindex할 때
    • 새로운 setting정보로 인덱스를 생성할 때
    • 인덱스 별칭(alias)을 사용

70. Analyzer 수정하기

my_custom_analyzer 추가

PUT analyzer_test/_mapping
{
  "properties": {
    "description": {
      "type": "text", 
      "analyzer": "my_custom_analyzer"
    }
  }
}

문서 추가

POST /analyzer_test/_doc
{
  "description": "Is that Peter's cute-looking dog?"
}

문서 조회

GET analyzer_test/_search
{
  "query": {
    "match": {
      "description": {
        "query": "that",
        "analyzer": "keyword"
      }
    }
  }
}

결과가 나오지 않는다.

그 이유는 analyzer에 stop 필터가 추가되어서 that이 제거되었기 때문이다.

해당 analyzer를 변경해보자.

우선 인덱스를 close 하자

POST analyzer_test/_close

그리고 나서 anaylzer를 변경하자.

PUT analyzer_test/_settings
{
  "analysis": {
    "analyzer": {
      "my_custom_analyzer": {
        "type": "custom",
        "tokenizer": "standard",
        "char_filter": ["html_strip"],
        "filter": [
          "lowercase",
          "asciifolding"
        ]
      }
    }
  }
}

다시 인덱스를 open을 하자.

POST analyzer_test/_open

setting을 조회해보면 변경됬음을 확인할 수 있다.

GET analyzer_test/_settings

결과

{
  "analyzer_test" : {
    "settings" : {
      "index" : {
        "routing" : {
          "allocation" : {
            "include" : {
              "_tier_preference" : "data_content"
            }
          }
        },
        "number_of_shards" : "1",
        "provided_name" : "analyzer_test",
        "creation_date" : "1662965220554",
        "analysis" : {
          "analyzer" : {
            "my_custom_analyzer" : {
              "filter" : [
                "lowercase",
                "asciifolding"
              ],
              "char_filter" : [
                "html_strip"
              ],
              "type" : "custom",
              "tokenizer" : "standard"
            }
          }
        },
        "number_of_replicas" : "1",
        "uuid" : "0WZ0Nxc6T2yvcUlIGHHCGA",
        "version" : {
          "created" : "7110099"
        }
      }
    }
  }
}

데이터를 1건 더 입력해보자.

POST analyzer_test/_doc
{
  "description": "Is that Peter's cute-looking dog?"
}

다시 조회를 해보면

GET analyzer_test/_search
{
  "query": {
    "match": {
      "description": {
        "query": "that",
        "analyzer": "keyword"
      }
    }
  }
}

1건이 나오는 것을 알 수 있다.

하지만 이전에 입력했던 데이터는 나오지 않는다.

2가지 유형의 analyzer가 동시에 사용되고 있음을 알 수 있다. (첫 번째는 stop 필터가 적용, 두번째는 stop필터가 적용되지 않은 버전)

analyzer_test를 조회해보면 2가지 데이터가 나오는 것을 확인할 수 있다.

GET analyzer_test/_search

결과

{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "analyzer_test",
        "_type" : "_doc",
        "_id" : "rKN1MIMB72KeG_cXOvn6",
        "_score" : 1.0,
        "_source" : {
          "description" : "Is that Peter's cute-looking dog?"
        }
      },
      {
        "_index" : "analyzer_test",
        "_type" : "_doc",
        "_id" : "raN6MIMB72KeG_cXnPlw",
        "_score" : 1.0,
        "_source" : {
          "description" : "Is that Peter's cute-looking dog?"
        }
      }
    ]
  }
}

정상적으로 조회하기 위해서 해당 인덱스를 새 인덱스로 생성해야 할 필요가 있다.

좀 더 간단한 방법으로 Update By Query를 사용할 수 있다.

아래 쿼리를 실행해보자.

POST analyzer_test/_update_by_query?conflicts=proceed

그런 다음 다시 조회해보자.

GET analyzer_test/_search
{
  "query": {
    "match": {
      "description": {
        "query": "that",
        "analyzer": "keyword"
      }
    }
  }
}

결과

{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : 0.13353139,
    "hits" : [
      {
        "_index" : "analyzer_test",
        "_type" : "_doc",
        "_id" : "rKN1MIMB72KeG_cXOvn6",
        "_score" : 0.13353139,
        "_source" : {
          "description" : "Is that Peter's cute-looking dog?"
        }
      },
      {
        "_index" : "analyzer_test",
        "_type" : "_doc",
        "_id" : "raN6MIMB72KeG_cXnPlw",
        "_score" : 0.13353139,
        "_source" : {
          "description" : "Is that Peter's cute-looking dog?"
        }
      }
    ]
  }
}

결과가 2건이 나오는 것을 확인할 수 있다.

반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유