엔지니어링

Elasticsearch에 대한 실용적인 소개

편집자 노트(2021년 8월 3일): 이 포스팅은 더 이상 사용되지 않고 앞으로는 사라지게 될 기능을 사용합니다. 현재 지침은 리버스 지오코딩을 통한 사용자 정의 리전 매핑 설명서를 참조하세요.

이 포스팅을 하는 이유

최근에 라코루냐 대학교(University of A Coruña)의 정보 검색 및 시맨틱 웹 과정에서 마스터 클래스를 가르친 적이 있습니다. 이 수업에서 중점적으로 다룬 내용은 학생들에게 Elasticsearch의 일반적인 비전을 제공하여 이 과정의 과제에서 Elasticsearch를 사용할 수 있도록 하는 것이었습니다. 참석자는 Lucene에 이미 익숙한 사람부터 정보 검색이라는 개념을 처음 접하는 사람까지 다양했습니다. 저녁 7시 30분에 시작하는 매우 늦은 수업이었기 때문에 계속해서 학생들의 주의를 끄는 것(다시 말해서, 학생들이 잠들지 않도록 하는 것!)이 어려운 숙제 중 하나였습니다. 가르칠 때 계속해서 주의를 끄는 두 가지 기본적인 접근 방법이 있는데, 하나는 (저는 깜빡 잊고 가져오지 않았지만) 초콜릿을 가져오는 것이고 또 하나는 수업을 가능한 한 실용적으로 만드는 것입니다.

그리고 오늘의 포스팅에서 제공하는 것도 바로 그런 것입니다. 말씀드린 그 수업의 실용적인 부분을 살펴보려고 합니다. 목표는 Elasticsearch에서 모든 명령이나 요청을 배우는 것이 아니라(이런 것은 설명서를 참조하시면 됩니다), 30~60분짜리 안내 튜토리얼에서 사전 지식 없이 Elasticsearch를 사용하는 즐거움을 실험하는 것입니다. 결과를 보려면 모든 요청을 복사하여 붙여넣기만 하면 됩니다. 그리고 제안된 질문에 대한 해결책을 찾아보세요.

이 포스팅을 유용하게 활용할 수 있는 사람

몇 가지 주요 개념을 소개하기 위해 Elastic의 기본 기능을 보여드리고, 때로는 더 기술적이거나 복잡한 개념을 소개하며, 추가적으로 참조하실 수 있도록 설명서를 링크하겠습니다(참고: 추가적으로 참조하기 위한 것임을 잊지 마세요. 여러분은 그냥 계속해서 예제와 함께 진행하시면 됩니다. 설명서는 나중에 사용하세요.) 이전에 Elasticsearch를 사용한 적이 없으며 실행 중인 Elasticsearch를 보고 싶어하신다면(아울러 그러한 실행을 주관하는 사람이 되고 싶으시다면), 이 포스팅은 바로 여러분을 위한 것입니다. 이미 Elasticsearch를 사용해 본 경험이 있으시면, 우리가 사용할 데이터 세트를 살펴보세요. 친구가 Elasticsearch로 무엇을 할 수 있는지 물어볼 때 셰익스피어의 희곡들을 검색하는 것으로 더 쉽게 설명하실 수 있습니다!

다룰 내용과 다루지 않을 내용

먼저 일부 문서를 추가하고, 검색 및 제거 작업을 시작하겠습니다. 그런 다음, 검색 및 집계에 대한 더 많은 인사이트를 제공하기 위해 셰익스피어 데이터 세트를 사용할 것입니다. 이것은 "지금 당장 작동하는 것을 보기 시작하고 싶다"라는 방식의 직접 체험형 포스팅입니다.

구성이나 프로덕션 배포의 모범 사례와 관련된 내용은 다루지 않습니다. 그러니, 이 정보를 사용하여 Elasticsearch가 제공하는 내용을 살짝 맛보세요. 이를 통해 Elasticsearch가 여러분의 니즈를 어떻게 충족할 수 있는지 마음에 그려보기 시작하실 수 있을 것입니다.

설정

우선, Elasticsearch가 필요합니다. 설명서 지침에 따라 최신 버전을 다운로드하고 설치한 후 시작합니다. 기본적으로, 최신 버전의 Java가 필요합니다. 여러분이 사용하시는 운영 체제를 위한 Elasticsearch를 다운로드하여 설치하세요. 그리고 기본값인 bin/elasticsearch로 드디어 시작하시면 됩니다. 여기서는 현재 사용 가능한 최신 버전인 5.5.0을 사용하겠습니다.

다음으로, Elasticsearch와 통신해야 합니다. 이는 REST API에 대해 HTTP 요청을 실행하여 수행됩니다. Elastic은 포트 9200에서 기본적으로 시작됩니다. 액세스하려면, 여러분의 전문 지식에 가장 적합한 도구를 사용하실 수 있습니다. Linux의 curl과 같은 명령줄 도구, 크롬이나 파이어폭스를 위한 웹 브라우저 REST 플러그인이 있으며, 또는 간단히 Kibana를 설치하고 콘솔 플러그인을 사용하실 수 있습니다. 각 요청은 HTTP 동사(GET, POST, PUT…), URL 엔드포인트 및 선택적 본문으로 구성됩니다. 대부분의 경우, 본문은 JSON 개체입니다.

예를 들어, Elasticsearch가 시작되었는지 확인해 보고자, 다음과 같이 기본 엔드포인트에 액세스하기 위해 기본 URL에 대해 GET를 수행해 봅시다(본문이 필요하지 않음).

GET localhost:9200

응답은 다음과 유사해야 합니다. 우리가 아무것도 구성하지 않았기 때문에, 인스턴스 이름은 다음과 같이 임의의 7자 스트링이 됩니다.

{
    "name": "t9mGYu5",
    "cluster_name": "elasticsearch",
    "cluster_uuid": "xq-6d4QpSDa-kiNE4Ph-Cg",
    "version": {
        "number": "5.5.0",
        "build_hash": "260387d",
        "build_date": "2017-06-30T23:16:05.735Z",
        "build_snapshot": false,
        "lucene_version": "6.6.0"
    },
    "tagline": "모두가 아는 Search!"
}

몇 가지 기본적인 예

깨끗한 Elasticsearch 인스턴스가 이미 초기화되어 실행 중입니다. 우리가 첫 번째로 하려는 일은 문서를 추가하고 검색하는 것입니다. Elasticsearch의 문서는 JSON 형식으로 표시됩니다. 또한, 문서는 인덱스에 추가되며 문서에는 유형이 있습니다. ID가 1인 사용자 유형의 문서를 계정이라는 인덱스에 추가합니다. 인덱스가 아직 존재하지 않기 때문에 Elasticsearch가 자동으로 생성합니다.

POST localhost:9200/accounts/person/1 
{
    "name" : "John",
    "lastname" : "Doe",
    "job_description" : "Systems administrator and Linux specialit"
}

응답은 다음과 같이 문서 생성에 대한 정보를 반환하게 됩니다.

{
    "_index": "accounts",
    "_type": "person",
    "_id": "1",
    "_version": 1,
    "result": "created",
    "_shards": {
        "total": 2,
        "successful": 1,
        "failed": 0
    },
    "created": true
}

이제 문서가 존재하므로, 다음과 같이 문서를 찾아서 가져올 수 있습니다.

GET localhost:9200/accounts/person/1 

결과에는 메타데이터와 아울러 전체 문서(_source 필드에 표시됨)도 포함됩니다.

{
    "_index": "accounts",
    "_type": "person",
    "_id": "1",
    "_version": 1,
    "found": true,
    "_source": {
        "name": "John",
        "lastname": "Doe",
        "job_description": "Systems administrator and Linux specialit"
    }
}

예리한 독자는 직무 설명에 오타(specialit)가 있다는 것을 이미 알고 계실 것입니다. 그럼 문서를 업데이트하여 이 오타를 정정해 보겠습니다(_update).

POST localhost:9200/accounts/person/1/_update
{
      "doc":{
          "job_description" : "Systems administrator and Linux specialist"
       }
}

작업이 성공하면, 문서가 변경됩니다. 다시 검색하여 응답을 확인해 봅시다.

{
    "_index": "accounts",
    "_type": "person",
    "_id": "1",
    "_version": 2,
    "found": true,
    "_source": {
        "name": "John",
        "lastname": "Doe",
        "job_description": "Systems administrator and Linux specialist"
    }
}

다음 작업을 준비하기 위해, ID가 2인 문서를 추가합니다.

POST localhost:9200/accounts/person/2
{
    "name" : "John",
    "lastname" : "Smith",
    "job_description" : "Systems administrator"
}

지금까지 우리는 ID로 문서를 찾아서 가져왔지만, 검색은 하지 않았습니다. Rest API를 사용하여 쿼리할 때 요청 본문에서 쿼리를 전달하거나 특정 구문을 사용하여 URL에서 직접 전달할 수 있습니다. 이 섹션에서는 URL에서 /_search?q=something 형식으로 직접 검색을 수행하려고 합니다.

GET localhost:9200/_search?q=john

두 문서 모두 john을 포함하므로, 이 검색에서는 두 문서가 모두 반환됩니다.

{
    "took": 58,
    "timed_out": false,
    "_shards": {
        "total": 5,
        "successful": 5,
        "failed": 0
    },
    "hits": {
        "total": 2,
        "max_score": 0.2876821,
        "hits": [
            {
                "_index": "accounts",
                "_type": "person",
                "_id": "2",
                "_score": 0.2876821,
                "_source": {
                    "name": "John",
                    "lastname": "Smith",
                    "job_description": "Systems administrator"
                }
            },
            {
                "_index": "accounts",
                "_type": "person",
                "_id": "1",
                "_score": 0.28582606,
                "_source": {
                    "name": "John",
                    "lastname": "Doe",
                    "job_description": "Systems administrator and Linux specialist"
                }
            }
        ]
    }
}

이 결과에서, 일치하는 문서와 아울러 쿼리에 대한 총 결과 수와 같은 일부 메타데이터를 볼 수 있습니다. 계속해서 좀더 검색해보겠습니다. 검색을 실행하기 전에, 어떤 문서를 검색할 것인지 직접 확인하세요(응답은 명령어 뒤에 옵니다).

GET localhost:9200/_search?q=smith

이 검색은 우리가 마지막으로 추가한 문서(smith가 포함된 유일한 문서)만 반환하게 됩니다.

GET localhost:9200/_search?q=job_description:john

이 검색은 어떤 문서도 반환하지 않습니다. 이 경우, 단어를 포함하지 않는 job_description 필드로만 검색을 제한하고 있습니다. 독자 여러분을 위한 연습으로, 다음을 해보세요.

  • ID가 1인 문서만 반환하게 되는 해당 필드의 검색
  • 두 문서를 모두 반환하는 해당 필드의 검색
  • ID가 2인 문서만 반환하는 필드의 검색 - 힌트: "q" 매개 변수는 쿼리 스트링과 동일한 구문을 사용합니다.

이 마지막 예에서는 특정 필드에서 검색을 수행할 수 있는가? 특정 인덱스 내에서만 검색이 가능한가? 등과 같은 관련 질문을 제기합니다. 정답은 "예"입니다. 인덱스를 지정하고 URL을 입력할 수 있습니다. 다음과 같이 해보세요.

GET localhost:9200/accounts/person/_search?q=job_description:linux

하나의 인덱스에서 검색하는 것 외에도, 쉼표로 구분된 인덱스 이름 목록을 제공하여 여러 인덱스를 동시에 검색할 수 있습니다. 유형에 대해서도 동일한 작업을 수행할 수 있습니다. 더 많은 옵션이 있습니다. 옵션에 대한 정보는 멀티 인덱스, 다중 유형에서 확인하실 수 있습니다. 독자 여러분을 위한 연습으로, 두 번째 (다른) 인덱스에 문서를 추가하고 두 인덱스에서 동시에 검색을 수행해 보세요.

이 섹션을 마무리하기 위해, 문서를 삭제한 다음 전체 인덱스를 삭제하겠습니다. 문서를 삭제한 후, 이 문서를 찾아서 가져오려고 하거나 검색에서 이 문서를 찾아보세요.

DELETE localhost:9200/accounts/person/1

응답 내용은 다음과 같은 확인이 될 것입니다.

{
    "found": true,
    "_index": "accounts",
    "_type": "person",
    "_id": "1",
    "_version": 3,
    "result": "deleted",
    "_shards": {
        "total": 2,
        "successful": 1,
        "failed": 0
    }
}

마지막으로, 전체 인덱스를 삭제할 수 있습니다.

DELETE localhost:9200/accounts

이로써 첫 번째 섹션이 끝났습니다. 우리가 한 작업을 요약해 보면 다음과 같습니다.

  1. 문서를 추가했습니다. 암시적으로, 인덱스가 생성되었습니다(이전에는 인덱스가 존재하지 않았습니다).
  2. 문서를 찾아 가져왔습니다.
  3. 문서를 업데이트하여 오타를 정정하고 올바르게 정정되었는지 확인했습니다.
  4. 두 번째 문서가 추가되었습니다.
  5. 암시적으로 모든 필드를 사용하는 검색과 한 필드에만 초점을 맞춘 검색을 포함하여 검색했습니다.
  6. 몇 가지 검색 연습을 제안했습니다.
  7. 여러 인덱스와 유형을 동시에 검색할 때의 기본 사항을 설명했습니다.
  8. 여러 인덱스를 동시에 검색하는 것이 제안했습니다.
  9. 하나의 문서를 삭제했습니다.
  10. 전체 인덱스를 삭제했습니다.

이 섹션의 항목에 대한 자세한 내용은 다음 링크를 참조하세요.

더 흥미로운 데이터를 가지고 놀기

지금까지, 우리는 몇몇 허구적인 데이터를 가지고 놀이처럼 작업해 보았습니다. 이 섹션에서는, 셰익스피어의 희곡들을 탐색할 것입니다. 첫 번째 단계는 Kibana: 샘플 데이터 로딩에서 사용 가능한 shakespeare.json 파일을 다운로드하는 것입니다. Elasticsearch는 대량으로, 즉 한 번에 많이 추가, 삭제, 업데이트 및 생성 작업을 수행할 수 있는 벌크 API를 제공합니다. 이 파일에는 이 API를 사용하여 수집될 준비가 된 데이터가 포함되어 있으며, Shakespeare라는 인덱스에 색인될 준비가 되어 있고, 막(act), 장(scene), 줄(line) 유형의 문서가 포함되어 있습니다. 벌크 API에 대한 요청의 본문은 한 줄에 1개의 JSON 개체로 구성됩니다. 추가 작업의 경우, 파일에 있는 것과 같이, 추가 작업에 대한 메타데이터를 나타내는 1개의 JSON 개체와 다음 줄에 추가할 문서가 포함된 두 번째 JSON 개체가 있습니다.

{"index":{"_index":"shakespeare","_type":"act","_id":0}}
{"line_id":1,"play_name":"Henry IV","speech_number":"","line_number":"","speaker":"","text_entry":"ACT I"}

벌크 API에 대해서는 더 자세히 다루지 않겠습니다. 관심이 있으시면, 벌크 설명서를 참조하세요.

이제 이 모든 데이터를 Elasticsearch로 가져오겠습니다. 이 요청의 본문은 상당히 크기 때문에(200,000줄 이상) 파일로부터 요청의 본문을 로드할 수 있는 도구(예를 들어, curl)를 사용하여 이 작업을 수행하는 것이 좋습니다.

curl -XPOST "localhost:9200/shakespeare/_bulk?pretty" --data-binary @shakespeare.json

데이터가 로드되면, 몇 가지 검색을 시작할 수 있습니다. 이전 섹션에서는 URL에서 쿼리를 전달하는 검색을 수행했습니다. 이 섹션에서는, 쿼리를 정의하기 위해 검색 요청의 본문에 사용할 JSON 형식을 지정하는 Query DSL을 소개합니다. 작업 유형에 따라, GET과 POST 동사를 둘다 사용하여 쿼리를 실행할 수 있습니다. 가장 간단한 것부터 시작하겠습니다. 모든 문서를 가져오는 것입니다. 이를 위해, 본문에서 query 키를 지정하고 값에 대해 match_all 쿼리를 지정합니다.

GET localhost:9200/shakespeare/_search
{
    "query": {
            "match_all": {}
    }
}

결과에는 10개의 문서가 표시되며, 부분 출력은 다음과 같습니다.

{
    "took": 7,
    "timed_out": false,
    "_shards": {
        "total": 5,
        "successful": 5,
        "failed": 0
    },
    "hits": {
        "total": 111393,
        "max_score": 1,
        "hits": [
              ...          
            {
                "_index": "shakespeare",
                "_type": "line",
                "_id": "19",
                "_score": 1,
                "_source": {
                    "line_id": 20,
                    "play_name": "Henry IV",
                    "speech_number": 1,
                    "line_number": "1.1.17",
                    "speaker": "KING HENRY IV",
                    "text_entry": "The edge of war, like an ill-sheathed knife,"
                }
            },
            ...          

검색을 위한 형식은 매우 간단합니다. 다양한 유형의 검색을 사용할 수 있습니다. Elastic은 직접 검색("이 용어 검색", "이 범위의 요소 검색" 등) 및 복합 쿼리(“a AND b”, “a OR b” 등)을 제공합니다. 전체 참조는 Query DSL 설명서에서 찾아보실 수 있습니다. 여기서는 사용할 수 있는 방법을 숙지하기 위해 몇 가지 예만 살펴보겠습니다.

POST localhost:9200/shakespeare/scene/_search/
{
    "query":{
        "match" : {
            "play_name" : "Antony"
        }
    }
}

이전 쿼리에서는 희곡 제목에 Antony가 포함되는 모든 장(scene)(URL 참조)을 검색하고 있습니다. 이 검색을 세분화하고 Demetrius가 화자인 장(scene)도 선택할 수 있습니다.

POST localhost:9200/shakespeare/scene/_search/
{
    "query":{
        "bool": {
            "must" : [
                {
                    "match" : {
                        "play_name" : "Antony"
                    }
                },
                {
                    "match" : {
                        "speaker" : "Demetrius"
                    }
                }
            ]
        }
    }
}

독자 여러분을 위한 첫 번째 연습으로, 이전 쿼리를 수정하여 검색에서 화자가 Demetrius인 장(scene)뿐만 아니라 화자가 Antony인 장(scene)도 반환하도록 하세요. 힌트: 부울(boolean) should 절을 확인해 보세요. 독자 여러분을 위한 두 번째 연습으로, 검색 시 요청 본문에서 사용할 수 있는 다양한 옵션을 탐색해 보실 수 있습니다. 예를 들어, 결과의 어떤 위치에서 시작할 것인지, 페이징 처리를 위해 몇 개의 결과를 찾아서 가져오려고 하는지 등을 선택하세요.

지금까지, Query DSL을 사용하여 몇 가지 쿼리를 수행했습니다. 우리가 찾고 있는 콘텐츠를 찾아서 가져오는 것 외에 분석도 할 수 있다면 어떨까요? 이 지점이 바로 집계가 중요한 역할을 하는 부분입니다. 집계를 통해 데이터를 보다 심층적으로 파악할 수 있습니다. 예를 들어, 현재 데이터 세트에 얼마나 많은 다양한 희곡이 존재하는가? 작품당 평균 몇 개의 장(scene)이 있는가? 더 많은 장(scene)이 있는 작품은 무엇인가?

실용적인 예로 들어가기 전에, 우리가 Shakespeare 인덱스를 만들었을 때로 한 걸음 돌아가 봅시다. 왜냐하면 약간의 이론 없이 계속 진행하는 것은 낭비일 것이기 때문입니다. 많은 데이터 유형이 있는데, Elastic에서 우리는 숫자 필드, 키워드 필드, 텍스트 필드 등 가능한 다양한 필드에 대해 데이터 유형을 정의하는 인덱스를 만들 수 있습니다. 인덱스가 가질 수 있는 데이터 유형은 매핑을 통해 정의됩니다. 이 경우, 문서를 색인하기 전에 인덱스를 생성하지 않았기 때문에 Elastic이 각 필드의 유형이 무엇인지 결정했습니다(인덱스의 매핑을 생성했습니다). 텍스트 필드에 대해 유형 text가 선택되었습니다. 이 유형은 분석되며, Antony를 검색하는 것만으로 play_name Antony and Cleopatra를 찾을 수 있었습니다. 기본적으로 분석된 필드에서는 집계를 수행할 수 없습니다. 이 작업을 하기 위해 필드가 유효하지 않은 경우, 집계를 어떻게 표시하게 될까요? Elastic은 또한 각 필드의 유형을 결정할 때 집계/정렬/스크립트를 수행하고자 하는 경우를 대비하여 텍스트 필드(keyword라고 함)의 분석되지 않은 버전을 추가했습니다. 즉, 집계에서 play_name.keyword를 사용하면 됩니다. 독자 여러분을 위한 연습으로, 현재 매핑을 검사하는 방법이 남아 있습니다.

그리고 이 비교적 작고 비교적 이론적인 수업이 끝나면, 키보드와 집계로 돌아가도록 하겠습니다! 다음과 같이 우리에게 몇 편의 다른 희곡이 있는지 그 수를 확인하여 데이터 검사를 시작할 수 있습니다.

GET localhost:9200/shakespeare/_search
{
    "size":0,
    "aggs" : {
        "Total plays" : {
            "cardinality" : {
                "field" : "play_name.keyword"
            }
        }
    }
}

참고로 우리는 문서에 관심이 없기 때문에, 0개의 결과를 나타내기로 했습니다. 또한 전체 인덱스를 탐색하고 싶기 때문에, 쿼리 섹션이 없습니다. 쿼리를 수행하는 모든 문서를 사용하여 집계가 계산되며, 이 경우 기본값은 match_all입니다. 마지막으로, play_name 필드에 대해 얼마나 많은 고유 값을 가지고 있는지 알려주게 될 cardinality 집계를 사용하기로 결정했습니다.

{
    "took": 67,
    "timed_out": false,
    "_shards": {
        "total": 5,
        "successful": 5,
        "failed": 0
    },
    "hits": {
        "total": 111393,
        "max_score": 0,
        "hits": []
    },
    "aggregations": {
        "Total plays": {
            "value": 36
        }
    }
}

이제 데이터 세트에 좀더 자주 나타나는 희곡을 나열해 보겠습니다:

GET localhost:9200/shakespeare/_search
{
    "size":0,
    "aggs" : {
        "Popular plays" : {
            "terms" : {
                "field" : "play_name.keyword"
            }
        }
    }
}

그 결과는 다음과 같습니다.

{
    "took": 35,
    "timed_out": false,
    "_shards": {
        "total": 5,
        "successful": 5,
        "failed": 0
    },
    "hits": {
        "total": 111393,
        "max_score": 0,
        "hits": []
    },
    "aggregations": {
        "Popular plays": {
            "doc_count_error_upper_bound": 2763,
            "sum_other_doc_count": 73249,
            "buckets": [
                {
                    "key": "Hamlet",
                    "doc_count": 4244
                },
                {
                    "key": "Coriolanus",
                    "doc_count": 3992
                },
                {
                    "key": "Cymbeline",
                    "doc_count": 3958
                },
                {
                    "key": "Richard III",
                    "doc_count": 3941
                },
                {
                    "key": "Antony and Cleopatra",
                    "doc_count": 3862
                },
                {
                    "key": "King Lear",
                    "doc_count": 3766
                },
                {
                    "key": "Othello",
                    "doc_count": 3762
                },
                {
                    "key": "Troilus and Cressida",
                    "doc_count": 3711
                },
                {
                    "key": "A Winters Tale",
                    "doc_count": 3489
                },
                {
                    "key": "Henry VIII",
                    "doc_count": 3419
                }
            ]
        }
    }
}

play_name의 가장 인기 있는 값 10개를 볼 수 있습니다. 설명서를 좀더 살펴보며 집계에서 더 많거나 더 적은 값을 표시하는 방법을 알아내는 것은 독자 여러분의 몫입니다.

여기까지 성공적으로 따라오셨다면, 다음 단계인 집계 결합을 확실히 파악하실 수 있습니다. 우리는 인덱스에 몇 개의 장(scene), 막(act), 줄(line)이 있는지에 관심이 있을 수 있지만, 또한 희곡당 동일한 값에 관심이 있을 수도 있습니다. 집계 내부에 집계를 중첩하여 이를 수행할 수 있습니다.

GET localhost:9200/shakespeare/_search
{
    "size":0,
    "aggs" : {
        "Total plays" : {
            "terms" : {
                "field" : "play_name.keyword"
            },
            "aggs" : {
                "Per type" : {
                    "terms" : {
                        "field" : "_type"
                     }
                }
            }
        }
    }
}

그리고 응답의 일부는 다음과 같습니다.

    "aggregations": {
        "Total plays": {
            "doc_count_error_upper_bound": 2763,
            "sum_other_doc_count": 73249,
            "buckets": [
                {
                    "key": "Hamlet",
                    "doc_count": 4244,
                    "Per type": {
                        "doc_count_error_upper_bound": 0,
                        "sum_other_doc_count": 0,
                        "buckets": [
                            {
                                "key": "line",
                                "doc_count": 4219
                            },
                            {
                                "key": "scene",
                                "doc_count": 20
                            },
                            {
                                "key": "act",
                                "doc_count": 5
                            }
                        ]
                    }
                },
                ...

Elasticsearch에는 집계의 결과를 사용하는 집계, cardinality와 같은 메트릭 집계, terms와 같은 버킷 집계 등 수많은 다양한 집계가 있습니다… 목록을 살펴보고 여러분이 이미 염두에 두고 계시는 특정 사용 사례에 어떤 집계가 적합한지 직접 결정하세요! 예외적으로 자주 등장하는 단어를 찾기 위해 Significant terms 집계가 필요할까요?

이제 두 번째 섹션도 끝났습니다. 우리가 한 작업을 요약해 보면 다음과 같습니다.

  1. 벌크 API를 사용하여 셰익스피어의 희곡을 추가했습니다.
  2. Query DSL을 통해 쿼리를 수행할 일반 형식을 검사하는 단순 검색을 수행했습니다.
  3. 필드에서 텍스트를 검색하는 리프 검색을 수행했습니다.
  4. 두 개의 텍스트 검색을 결합한 복합 검색을 수행했습니다.
  5. 두 번째 복합 검색을 추가할 것을 제안했습니다.
  6. 요청 본문의 다양한 옵션을 테스트할 것을 제안했습니다.
  7. 매핑 및 필드 유형에 대한 간략한 검토와 함께 집계 개념을 소개했습니다.
  8. 데이터 세트에 있는 희곡이 몇 편인지를 계산했습니다.
  9. 데이터 세트에 좀더 자주 나타나는 희곡이 무엇인지 검색했습니다.
  10. 여러 개의 집계를 결합하여 가장 빈번한 10개의 희곡 각각에 몇 개의 장(scene), 막(act), 줄(line)이 있는지 확인했습니다.
  11. Elastic에서 몇 가지 집계를 좀더 탐색할 것을 제안했습니다.

몇가지 추가적인 조언

이 연습을 진행하는 동안, 우리는 Elasticsearch에서 유형이라는 개념을 조금 활용해 보았습니다. 결국, 유형은 내부 추가 필드일 뿐입니다. 버전 6부터는 단일 유형의 인덱스만 생성할 수 있게 되며, 버전 7부터는 유형이 제거될 것으로 예상됩니다. 자세한 내용은 이 블로그 포스팅에서 찾아보실 수 있습니다.

결론

이 포스팅에서는, Elasticsearch에 대한 약간의 경험을 제공해드리기 위해 몇 가지 예를 사용했습니다.

Elasticsearch와 Elastic 스택에서는 이 간단한 글에 나온 것보다 훨씬 더 많은 것들이 탐색해 보실 수 있습니다. 글을 마무리하기 전에, 정확도라는 것에 대해 잠시 말씀드리고 싶습니다. 충분히 그럴 가치가 있는 개념입니다. Elasticsearch는 *이 문서가 내 검색 요건을 충족하는가?*에 관한 것일 뿐만 아니라 *이 문서가 내 검색 요건을 얼마나 잘 충족하는가?*에 대해서도 다루며, 먼저 쿼리와 가장 관련된, 즉 가장 정확한 검색 결과를 제공합니다. 설명서는 광범위하고 예제로 가득합니다.

새로운 사용자 정의 기능을 구현하기 전에 설명서를 살펴보며 우리가 이미 그러한 기능을 구현했는지 확인해 보시기 바랍니다. 그러면 여러분의 프로젝트에 쉽게 활용하실 수 있을 것입니다. 우리의 개발 로드맵은 사용자와 개발자가 원한다고 알려주는 내용에 의해 크게 영향을 받기 때문에, 여러분이 유용하리라고 생각하시는 기능이나 아이디어가 이미 제공되고 있을 수도 있습니다!

인증/액세스_제어/암호화/감사가 필요하신 경우, 이러한 기능은 이미 Security에서 제공되고 있습니다. 클러스터를 모니터링하셔야 하는 경우, Monitoring에서 이를 사용하실 수 있습니다. 결과에서 필요에 따라 필드를 생성해야 하는 경우, 이미 스크립트 필드를 통해 필드를 생성하실 수 있도록 하고 있습니다. 이메일/Slack/Hipchat/등으로 경보를 만드셔야 하는 경우, Alerting을 통해 이를 사용하실 수 있습니다. 데이터와 집계를 그래프로 시각화하셔야 하는 경우, 이미 이를 위한 풍부한 환경인 Kibana를 제공하고 있습니다. 데이터베이스, 로그 파일, 관리 대기열 또는 상상할 수 있는 거의 모든 소스에서 데이터를 색인하셔야 하는 경우, LogstashBeats를 통해 제공됩니다. 무엇인가 필요하시다면, 어떤 것이 필요하시다면, 어떤 필요든 있으시다면… 이미 갖추고 계실 수 있는지 확인하세요!