Elasticsearch를 설치하고 실행할 때, 아래와 같은 오류가 나서 실행이 되지 않는다.

[WARN ][o.e.b.Natives            ] unable to loa
d JNA native support library, native methods will be disabled.
java.lang.UnsatisfiedLinkError: /tmp/elasticsearch.PRdXLpjT/jna--19853545
63/jna2901201660730912229.tmp: /tmp/elasticsearch.PRdXLpjT/jna--198535456
3/jna2901201660730912229.tmp: failed to map segment from shared object: O
peration not permitted
        at java.lang.ClassLoader

Operation not permitted
tmp/elasticsearch 를 허가하지않는것(tmp디렉토리 접근 관련 권한 문제)

해결 방법
jvm.options 파일의 -Djava.io.tmpdir 의 경로를 루트의 tmp가 아닌 elasticsearch를 사용하는 유저의 디렉토리로 설정 해서 해결
 68 # log4j 2
 69 -Dlog4j.shutdownHookEnabled=false
 70 -Dlog4j2.disable.jmx=true
 71
 72 #-Djava.io.tmpdir=${ES_TMPDIR} ##원본
 73 -Djava.io.tmpdir=/home/userid/es_temp ##수정 한부분

개인적인 테스트를 진행한 결과를 정리한 개인의 생각이므로, 제가 잘못알고 있는 것이면 댓글 달아주세요.

index segments _max count = 1 ?

인덱스를 고정하기 전에 먼저 force_merge를 실행하는 것이 좋습니다. 그러면 샤드별로 디스크에 하나의 세그먼트만 있게 됩니다. 또한, 압축률이 훨씬 향상되고, 고정 인덱스에 대해 집계 또는 정렬된 검색 요청을 실행할 때 필요한 데이터 구조가 간소화됩니다. 여러 세그먼트가 있는 고정 인덱스에 대한 검색을 실행하면 최대 수백 배의 성능 오버헤드가 발생할 수 있습니다.

POST /sampledata/_forcemerge?max_num_segments=1

https://www.elastic.co/kr/blog/creating-frozen-indices-with-the-elasticsearch-freeze-index-api

실습

기존 인덱스의 segments 의 수를 보자

아래를 보면 하나의 샤드당 segments 카운트는 8개로 나뉘어져있다.

이것을 하나의 세그먼트로 force merge를 해보자

$car_wash/_forcemerge?max_num_segments=1

해보기전

$curl -X GET "localhost:9200/car_wash/_segments?verbose=true&pretty" > car_wash_segments_default

Max_segments 크기 변경

$curl -X POST "localhost:9200/car_wash/_forcemerge/?max_num_segments=1&pretty"

{
  "_shards" : {
    "total" : 10,
    "successful" : 5,
    "failed" : 0
  }
}

변경 후 force merge

$curl -X GET "localhost:9200/car_wash/_segments?verbose=true&pretty" > car_wash_segments_forcemerge

car_wash_segments_default 와 car_was_segments_forcemenrge 두개의 파일을 비교 해보자

car_wash_segments_default는 한 샤드당 9개의 세그먼트로 각 세그먼트당 "memory_in_bytes" 의 값을 더해보았다.

값을 다 더해보면 61158 의 값이 나왔고,

"_shards" : {
    3     "total" : 10,
    4     "successful" : 5,
    5     "failed" : 0
    6   },
    7   "indices" : {
    8     "car_wash" : {
    9       "shards" : {
   10         "0" : [
   11           {
   12             "routing" : {
   13               "state" : "STARTED",
   14               "primary" : true,
   15               "node" : "uCyuJNg-TVu3E7g7nzRAZw"
   16             },
   17             "num_committed_segments" : 9,
   18             "num_search_segments" : 9,
   19             "segments" : {
   20               "_0" : {
   21                 "generation" : 0,
   22                 "num_docs" : 25,
   23                 "deleted_docs" : 0,
   24                 "size_in_bytes" : 23400,
   25                 "memory_in_bytes" : 6033,
   26                 "committed" : true,
   27                 "search" : true,
   28                 "version" : "7.5.0",
   29                 "compound" : true,
   30                 "ram_tree" : [
   31                   {
   32                     "description" : "postings [PerFieldPostings(segment=_0 formats=1)]",
   33                     "size_in_bytes" : 4794,
   34                     "children" : [
   35                       {
   36                         "description" : "format 'Lucene50_0' [BlockTreeTermsReader(fields=21,delegate=Lucene50Po      stingsReader(positions=true,payloads=false))]",
   37                         "size_in_bytes" : 4594,
   ....

car_was_segments_forcemenrge 의 segments의 갯수는 하나이고,

memory_in_bytes 의 값은 12268로 메모리가 줄어든 것을 확인할 수 있었다.

7   "indices" : {
   8     "car_wash" : {
   9       "shards" : {
  10         "0" : [
  11           {
  12             "routing" : {
  13               "state" : "STARTED",
  14               "primary" : true,
  15               "node" : "uCyuJNg-TVu3E7g7nzRAZw"
  16             },
  17             "num_committed_segments" : 1,
  18             "num_search_segments" : 1,
  19             "segments" : {
  20               "_9" : {
  21                 "generation" : 9,
  22                 "num_docs" : 2774,
  23                 "deleted_docs" : 0,
  24                 "size_in_bytes" : 1128655,
  25                 "memory_in_bytes" : 12268,
  26                 "committed" : true,
  27                 "search" : true,
  28                 "version" : "7.5.0",
  29                 "compound" : false,
  30                 "ram_tree" : [
  31                   {
  32                     "description" : "postings [PerFieldPostings(segment=_9 formats=1)]",
  33                     "size_in_bytes" : 11073,
  34                     "children" : [
  35                       {
  36                         "description" : "format 'Lucene50_0' [BlockTreeTermsReader(fields=21,delegate=Lucene50Pos     tingsReader(positions=true,payloads=false))]",
  37                         "size_in_bytes" : 10873,

일반적인 샤드의 segments의 크기는 61158

forcemerge 한 세그먼트의 메모리 byte 크기12268

Force merge 의 세그먼트의 메모리가 확 줄어든것을 볼수 있다.

세그먼트의 verbose로 살펴본 결과 segments.compound 의 값이 변경된것을 알수 있다. 무엇을 의미 하는것일까 ?

compound
    (Boolean) If true, Lucene merged all files from the segment into a single file to save file descriptors.

왜 그런것일까 ??

https://discuss.elastic.co/t/what-is-in-segments-memory/61651

indices.segments.terms_memory_in_bytes ??

역인덱싱 으로 되어있어서, segments로 나뉘어있어서, 하나로 합쳐지니까 메모리가 줄여지는거 아닐까??

아래의 내용들은 답을 이해 하기 위해 찾다가 참고가 된 문서인데 확실하지는 않지만, segments 에서 Terms 이 동일한것이 많아서 줄어들거나,

참고

Lucene Merge 설명

https://juanggrande.wordpress.com/2011/02/07/merge-policy-internals/

Maybe in your case some segments shares same terms or something else that duplicates in memory but belongs to different segments. After segment reduction same terms merged and so you have a little gain in memory. But it just my guess.

https://discuss.elastic.co/t/segments-memory-in-bytes-goes-down-after-reduction-of-segments-restart/41258/7

In my case, after merging segments into 1 segment using force merge 46, segments.terms_memory was decreased by 30%. (note that this percentage depends on your index characteristics)

I hope segment merging help you prevent adding more nodes (or resources, or money).

https://discuss.elastic.co/t/reducing-heap-usage-dedicated-to-indices-segments-terms-memory-in-bytes/68899

생각과 추측이 맞는지 테스트 해보기segments의 크기를 1로 테스트 해보기

조건 : 같은 데이터를 사용해서 segments가 여러개일때와 segments를 하나로 합쳤을때의 결과를 비교해봤다.

아래는 테스트 내용이다.

##인덱스 생성
PUT segments_test
##세그먼트 확인
GET segments_test/_segments



##데이터 삽입 X 3번 
POST segments_test/_doc
{
  "name":"cup",
  "text" : "the bowllike part of a goblet or the like."
}

3번의 데이터를 삽입한 결과 1번 샤드에 두개의 도큐먼트가 삽입된것을 알수 있다. 같은 도큐먼트 이지만, 메모리는 각 세그먼트 마다 가지고 있다.

{
  "_shards" : {
    "total" : 10,
    "successful" : 5,
    "failed" : 0
  },
  "indices" : {
    "segments_test" : {
      "shards" : {
        "0" : [
          {
            "routing" : {
              "state" : "STARTED",
              "primary" : true,
              "node" : "uCyuJNg-TVu3E7g7nzRAZw"
            },
            "num_committed_segments" : 0,
            "num_search_segments" : 0,
            "segments" : { }
          }
        ],
        "1" : [
          {
            "routing" : {
              "state" : "STARTED",
              "primary" : true,
              "node" : "uCyuJNg-TVu3E7g7nzRAZw"
            },
            "num_committed_segments" : 0,
            "num_search_segments" : 2,
            "segments" : {
              "_0" : {
                "generation" : 0,
                "num_docs" : 1,
                "deleted_docs" : 0,
                "size_in_bytes" : 4092,
                "memory_in_bytes" : 1698,
                "committed" : false,
                "search" : true,
                "version" : "7.5.0",
                "compound" : true,
                "attributes" : {
                  "Lucene50StoredFieldsFormat.mode" : "BEST_SPEED"
                }
              },
              "_1" : {
                "generation" : 1,
                "num_docs" : 1,
                "deleted_docs" : 0,
                "size_in_bytes" : 4092,
                "memory_in_bytes" : 1698,
                "committed" : false,
                "search" : true,
                "version" : "7.5.0",
                "compound" : true,
                "attributes" : {
                  "Lucene50StoredFieldsFormat.mode" : "BEST_SPEED"
                }
              }
            }
          }
        ],
        "2" : [
          {
            "routing" : {
              "state" : "STARTED",
              "primary" : true,
              "node" : "uCyuJNg-TVu3E7g7nzRAZw"
            },
            "num_committed_segments" : 0,
            "num_search_segments" : 1,
            "segments" : {
              "_0" : {
                "generation" : 0,
                "num_docs" : 1,
                "deleted_docs" : 0,
                "size_in_bytes" : 4092,
                "memory_in_bytes" : 1698,
                "committed" : false,
                "search" : true,
                "version" : "7.5.0",
                "compound" : true,
                "attributes" : {
                  "Lucene50StoredFieldsFormat.mode" : "BEST_SPEED"
                }
              }
            }
          }
        ],
        "3" : [
          {
            "routing" : {
              "state" : "STARTED",
              "primary" : true,
              "node" : "uCyuJNg-TVu3E7g7nzRAZw"
            },
            "num_committed_segments" : 0,
            "num_search_segments" : 0,
            "segments" : { }
          }
        ],
        "4" : [
          {
            "routing" : {
              "state" : "STARTED",
              "primary" : true,
              "node" : "uCyuJNg-TVu3E7g7nzRAZw"
            },
            "num_committed_segments" : 0,
            "num_search_segments" : 0,
            "segments" : { }
          }
        ]
      }
    }
  }
}

여기서 segments 의 크기를 병합 후에 확인 한다면?

POST segments_test/_forcemerge/?max_num_segments=1

GET segments_test/_segments

아래의 결과대로 1번의 샤드의 사용되는 메모리는 1698 바이트 의 두배가 되어야한다고 생각했지만, 메모리의 사용량은 1698 바이트 그대로 인것을 보면 segments의 term이 중복되는 경우에는 메모리의 양이 줄어든 것을 알수 있다.

{
  "_shards" : {
    "total" : 10,
    "successful" : 5,
    "failed" : 0
  },
  "indices" : {
    "segments_test" : {
      "shards" : {
        "0" : [
          {
            "routing" : {
              "state" : "STARTED",
              "primary" : true,
              "node" : "uCyuJNg-TVu3E7g7nzRAZw"
            },
            "num_committed_segments" : 0,
            "num_search_segments" : 0,
            "segments" : { }
          }
        ],
        "1" : [
          {
            "routing" : {
              "state" : "STARTED",
              "primary" : true,
              "node" : "uCyuJNg-TVu3E7g7nzRAZw"
            },
            "num_committed_segments" : 1,
            "num_search_segments" : 1,
            "segments" : {
              "_2" : {
                "generation" : 2,
                "num_docs" : 2,
                "deleted_docs" : 0,
                "size_in_bytes" : 4165,
                "memory_in_bytes" : 1698,
                "committed" : true,
                "search" : true,
                "version" : "7.5.0",
                "compound" : false,
                "attributes" : {
                  "Lucene50StoredFieldsFormat.mode" : "BEST_SPEED"
                }
              }
            }
          }
        ],
        "2" : [
          {
            "routing" : {
              "state" : "STARTED",
              "primary" : true,
              "node" : "uCyuJNg-TVu3E7g7nzRAZw"
            },
            "num_committed_segments" : 1,
            "num_search_segments" : 1,
            "segments" : {
              "_0" : {
                "generation" : 0,
                "num_docs" : 1,
                "deleted_docs" : 0,
                "size_in_bytes" : 4092,
                "memory_in_bytes" : 1698,
                "committed" : true,
                "search" : true,
                "version" : "7.5.0",
                "compound" : true,
                "attributes" : {
                  "Lucene50StoredFieldsFormat.mode" : "BEST_SPEED"
                }
              }
            }
          }
        ],
        "3" : [
          {
            "routing" : {
              "state" : "STARTED",
              "primary" : true,
              "node" : "uCyuJNg-TVu3E7g7nzRAZw"
            },
            "num_committed_segments" : 0,
            "num_search_segments" : 0,
            "segments" : { }
          }
        ],
        "4" : [
          {
            "routing" : {
              "state" : "STARTED",
              "primary" : true,
              "node" : "uCyuJNg-TVu3E7g7nzRAZw"
            },
            "num_committed_segments" : 0,
            "num_search_segments" : 0,
            "segments" : { }
          }
        ]
      }
    }
  }
}

주변 세차장 찾기 (2)

현재 위치를 중심으로 쿼리 만들기

현재 위치는 다양한맵의 주소에서 찾을수 있다.

구글 맵에서 검색을 하게 되면 쿼리 파라미터에 위도경도가 나오는것을 알수 있다.

https://www.google.co.kr/maps/place/%EC%8B%A0%EB%8F%84%EB%A6%BC%EC%97%AD/@37.5088099,126.8890174,17z/data=!3m1!4b1!4m5!3m4!1s0x357c9e5dbb76b179:0x2f88a2d1152886e2!8m2!3d37.5088099!4d126.8912061?hl=ko

위도 경도를 찾아서 다음과 같은 정보를 얻을수 있다.

이제 쿼리를 만들어보자

위치를 사용한 쿼리는 아래와같이 4가지의 종류가 있다.

  • Geo_shape
    • Intersects, disjoint, within, contains 등의 모양에 맞게 검색
    • Geoshape document
  • Geo_bounding_box
    • 사각형의 범위를 가지며 왼쪽 상단, 오른쪽 하단의 위도 경도를 사용하여 범위안의 값을 찾음
      bounding_box docuemnt
  • Geo_distance
    • 특정 위도 경도와의 거리를 사용하여 특정 거리안의 범위값을 찾음
      distance document
  • Geo_polygon
    • 특정 지점들을 연결연결 하여 다각형을 그리고 그 구역내의 범위를 찾음
      polygon document

스터디를 하는곳인 신도림역 37.5088099,126.8890174 을 중심으로 10Km 안에 주차장이 어디에 있는지 검색을 해봤다.

GET car_wash/_search
{
  "query": {
        "bool" : {
            "must" : {
                "match_all" : {}
            },
            "filter" : {
                "geo_distance" : {
                    "distance" : "10km",
                    "location": {
                        "lat" : 37.5088099,
                        "lon" : 126.8890174 
                    }
                }
            }
        }
    },"size":5
}

결과값

{
  "took" : 8,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 584,
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "car_wash",
        "_type" : "_doc",
        "_id" : "I_HOcW4BGQw9X5k0OQxe",
        "_score" : 1.0,
        "_source" : {
          "privider_name" : "3170000",
          "column12" : "서울특별시 금천구",
          "name" : "화성자동차정비공업사",
          "state" : "서울특별시",
          "location" : "37.460736,126.898655",
          "water_quality" : "433",
          "sector" : "정비업소",
          "address" : "서울특별시 금천구 시흥대로 304 (독산동)",
          "phone" : "02-3286-8572",
          "city" : "금천구",
          "date,provider_id" : "2019.9.10"
        }
      },
      {
        "_index" : "car_wash",
        "_type" : "_doc",
        "_id" : "J_HOcW4BGQw9X5k0OQxe",
        "_score" : 1.0,
        "_source" : {
          "privider_name" : "3170000",
          "column12" : "서울특별시 금천구",
          "name" : "금천자동차검사정비센타",
          "state" : "서울특별시",
          "location" : "37.441008,126.902064",
          "water_quality" : "559",
          "sector" : "정비업소",
          "address" : "서울특별시 금천구 시흥대로27길 26 (시흥동)",
          "phone" : "02-893-5500",
          "city" : "금천구",
          "date,provider_id" : "2019.9.10"
        }
      },
      {
        "_index" : "car_wash",
        "_type" : "_doc",
        "_id" : "n_HOcW4BGQw9X5k0OwxT",
        "_score" : 1.0,
        "_source" : {
          "privider_name" : "3170000",
          "column12" : "서울특별시 금천구",
          "name" : "동해자동차정비",
          "state" : "서울특별시",
          "location" : "37.470815,126.891905",
          "water_quality" : "273",
          "sector" : "정비업소",
          "address" : "서울특별시 금천구 두산로7길 10 (독산동)",
          "phone" : "02-851-4972",
          "city" : "금천구",
          "date,provider_id" : "2019.9.10"
        }
      },
      {
        "_index" : "car_wash",
        "_type" : "_doc",
        "_id" : "pPHOcW4BGQw9X5k0OwxT",
        "_score" : 1.0,
        "_source" : {
          "privider_name" : "3170000",
          "column12" : "서울특별시 금천구",
          "name" : "SK에너지(주)이가주유소",
          "state" : "서울특별시",
          "location" : "37.472984,126.897897",
          "water_quality" : "50",
          "sector" : "주유소",
          "address" : "서울특별시 금천구 시흥대로 441 (독산동)",
          "phone" : "02-861-2241",
          "city" : "금천구",
          "date,provider_id" : "2019.9.10"
        }
      },
      {
        "_index" : "car_wash",
        "_type" : "_doc",
        "_id" : "HfHOcW4BGQw9X5k0PQ0y",
        "_score" : 1.0,
        "_source" : {
          "privider_name" : "3170000",
          "column12" : "서울특별시 금천구",
          "name" : "대하운수(주)",
          "state" : "서울특별시",
          "location" : "37.466048,126.894731",
          "water_quality" : "53",
          "sector" : "운수업 등",
          "address" : "서울특별시 금천구 범안로16길 8 (독산동)",
          "phone" : "02-805-0487",
          "city" : "금천구",
          "date,provider_id" : "2019.9.10"
        }
      }
    ]
  }
}

전국 세차장 정보 검색

자신의 위치에서 가까운 전국의 세차장 을 검색하기 위해서 새로운 주제를 시작했다.

실제로는 geo_point를 써보고싶어서 맞춰서 작업!!

https://www.data.go.kr/dataset/15013193/standard.do

수정을 한 세차장 정보는 다음과 같다 .

image-20191116010342675

이제 데이터를 넣어보자.

잠깐 데이터를 넣기 전에 geopoint 형식으로 데이터를 삽입할것이기때문에 location의 데이터 타입을 geo-point 로 변경하자 .

injest pipeline 을 사용해보는것도 추천한다.

PUT car_wash
{
  "mappings": {
    "_doc": {
      "properties": {
        "location": {
          "type": "geo_point"
        }
      }
    }
  }
}

다양한 방법이 있지만 logstash를 통해서 쉽게 데이터를 넣어보자 !

logstash 를 사용해서 편리하게 넣을수 있지만, 다음에는 java를 REST API를 사용해서 넣어보도록 하기로함

logstash의 파일은 다음 과 같다.

input{
    file{
        path => "/Users/daeyunkim/Documents/07.ELK_v6/car_wash/cleanCar_edit.csv"
        start_position => "beginning"
        sincedb_path => "/dev/null"
    }
}

filter{
    csv{
        separator => ","
        columns => [ "name","state","city","sector","address","phone","water_quality" ,"latitude","longitude","date,provider_id","privider_name"]
    }
    mutate {
    add_field => { "location"=> "%{latitude},%{longitude}"}
    remove_field => ["@version","@timestamp","path","host","tags","message","latitude", "longitude"]
    }


}

output{
    stdout{}
    elasticsearch {
        hosts => "http://localhost:9200"
        index => "car_wash"
        document_type=>"_doc"
    }
}

logstash add field

https://www.elastic.co/guide/en/logstash/6.2/plugins-filters-mutate.html#plugins-filters-mutate-add_field

$ ./bin/logstash -f ../car_wash/car_wash.conf

위에 대로 하면 그대로들어갈것이라 생각했건만 현실은 한글 깨짐 !!

{
    "date,provider_id" => "2019.5.8",
               "phone" => "02-507-5100",
               "state" => "\\xB0\\xE6\\xB1\\u2D75",
             "address" => "\\xB0\\xE6\\xB1\\u2D75 \\xB0\\xFAõ\\xBD\\xC3 \\xC1\\u07FEӷ\\xCE 417",
                "name" => "\\xB0渶\\xC0\\xE5\\xC1\\xD6\\xC0\\xAF\\xBC\\xD2",
       "privider_name" => "3970000",
                "city" => "\\xB0\\xFAõ\\xBD\\xC3",
            "column12" => "\\xB0\\xE6\\xB1\\u2D75 \\xB0\\xFAõ\\xBD\\xC3\\r",
       "water_quality" => "2000-010",
            "location" => "37.450708,126.999478",
              "sector" => "\\xC1\\xD6\\xC0\\xAF\\xBC\\xD2"
}

Excel에서는 한글로 열리지만 실제로 vi 를 사용하여 열어보니 한글이 깨져있었다.

인코딩방법을 해결하고 다시 삽입!

es-data

Kibana 를 통해 시각화 한번 해보자

새로 넣은 데이터를 사용해서 시각화를 해봤다.

잘 넣은 것같다.
carwash_map

이전 kafka-spark streaming 을 사용하여 인기검색어에 대한 로그를 수집하려고 하였지만, 마땅히 테스트 할 서버가 없어서 우선은 logstash를 사용해서 바로 로그를 elasticsearch에 저장하기로 했다.그래서 기존에 kafka 카테고리에 연재를 하였는데 elasticsearch로 옮기게 되었다. 이전의 글들은 kafka 카테고리에 있습니다.

로그 저장 방법 변경

컴퓨터의 리소스부족으로 kafka-sparkstreaming을 사용하지 않고 logstash를 사용해서 바로 elasticsearch로 변경하기로 했다.ㅠㅠ 다음에 돈 많이 모아서 aws 서버하나 구성해야겠네..요...

할일 : 기존에는 search: 검색단어 에서 search: 검색단어 timestamp: 201910293847 로 변경 하기

grok filter를 사용해서 로그에서 searchword와 timestamp를 추가하기로 했다.

Apache 로그는 아래와 같다.

timestamp는 apache 로그 앞에 있어서 바로 사용하면 되겠다고 생각했다.

2019-10-05 14:13:19,069[http-nio-8080-exec-28]INFO  c.k.k.c.HomeKDYController - 가위 Sat Oct 05 14:13:19 KST 2019

그럼 파일을 logstash 를 사용해서 각 필드에 넣기 위해서. Grok 필터를 사용하기로 함.

grok 필터에 대해 알아보는데 스터디 시간(1시간)동안 시간을 소요한것같은데

이제서야 해답을 찾았다.

우선 grok 필터는 다음과 같이 설정 하면될 것 같다.

%{TIMESTAMP_ISO8601:timestamp},%{NUMBER:num}\[%{DATA:httpio}\]%{LOGLEVEL:log-level}  %{DATA:class} \- (?<searchWd>[a-힣].)

로그의 구성을 보면 timestamp,number,[httpio]loglevel class - searchword time 으로 동일했다.

이것을 각각에 맞게 다 작성해주고, searchWd만 개별로 customizing 해서 만들어줬다.

그럼 결과는 아래로 나온다. 이제 logstash의 conf에 추가를 해주면될것같다.

{
  "timestamp": [
    [
      "2019-10-05 14:13:19"
    ]
  ],
  "YEAR": [
    [
      "2019"
    ]
  ],
  "MONTHNUM": [
    [
      "10"
    ]
  ],
  "MONTHDAY": [
    [
      "05"
    ]
  ],
  "HOUR": [
    [
      "14",
      null
    ]
  ],
  "MINUTE": [
    [
      "13",
      null
    ]
  ],
  "SECOND": [
    [
      "19"
    ]
  ],
  "ISO8601_TIMEZONE": [
    [
      null
    ]
  ],
  "num": [
    [
      "069"
    ]
  ],
  "BASE10NUM": [
    [
      "069"
    ]
  ],
  "httpio": [
    [
      "http-nio-8080-exec-28"
    ]
  ],
  "log": [
    [
      "INFO"
    ]
  ],
  "class": [
    [
      "c.k.k.c.HomeKDYController"
    ]
  ],
  "searchWd": [
    [
      "가위"
    ]
  ]
}

Log stash 내부 grok 필터

filter {
        grok {
                match => {"message" => "%{TIMESTAMP_ISO8601:timestamp},%{NUMBER:num}\[%{DATA:httpio}\]%{LOGLEVEL:log-level}  %{DATA:class} \- (?<searchWd>[a-힣].)"}
        }
}

https://github.com/DaeyunKim/elasticsearchStudy/blob/master/koreanDictionary/(8)%20log_structure_change.md

Spark를 활용해서 Elasticsearch에 데이터 삽입하기

Excel 파일을 조작하기 위해서는 Java에서 poi를 사용하면 되지만, spark를 사용하다보니 dataframe을 사용하는 것이 더 좋을것같다라는 생각과 해보고싶다는 생각이 들었다. 그래서 spark를 활용해서 elasticsearch에 원하는 데이터를 넣어보자!!

Elasticsearch - Spark 연동은 아래에서 참고 하였다.

https://www.elastic.co/guide/en/elasticsearch/hadoop/6.2/spark.html

// reusing the example from Spark SQL documentation

import org.apache.spark.sql.SQLContext    
import org.apache.spark.sql.SQLContext._

import org.elasticsearch.spark.sql._      

...

// sc = existing SparkContext
val sqlContext = new SQLContext(sc)

// case class used to define the DataFrame
case class Person(name: String, surname: String, age: Int)

//  create DataFrame
val people = sc.textFile("people.txt")    
        .map(_.split(","))
        .map(p => Person(p(0), p(1), p(2).trim.toInt))
        .toDF()

people.saveToEs("spark/people")     

gradle Dependency 추가

사용한 의존되는 라이브러리는 다음과 같다.

dependencies {
  testCompile group: 'junit', name: 'junit', version: '4.12'
  implementation 'org.apache.spark:spark-core_2.11:2.4.3'
  implementation 'org.elasticsearch:elasticsearch-spark-20_2.11:6.5.3'
  implementation 'com.crealytics:spark-excel_2.11:0.12.0'
  implementation 'org.apache.spark:spark-sql_2.11:2.4.3'
}

crealytics 라이브러리를 사용하여 Spark-excel을 불러왔다.

SparkSession을 통하여 excel 데이터를 dataframe 으로 불러오기

excel 파일 dataframe으로 불러오기

import org.apache.spark.SparkConf
import org.apache.spark.sql.SparkSession
import org.elasticsearch.spark.sql._
object main {

  def main(args:Array[String]): Unit ={
    println("hello ES-SPARK")
    val spark = SparkSession.builder().appName("ESSpark").config("spark.master","local").getOrCreate()
    spark.conf.set("es.index.auto.create","true")
    spark.conf.set("es.nodes","127.0.0.1")
    spark.conf.set("es.port","9200")
    //load xls file
    val exceldf = spark.read
      .format("com.crealytics.spark.excel")
      .option("sheetName","sheet0")
      .option("useHeader","true")
      .load("/Users/daeyunkim/Documents/07.ELK_v6/data/korean_dic_origin/559806_60000.xls")

    exceldf.show()
    exceldf.printSchema()
    val filterData = exceldf.select("어휘","고유어 여부","품사","뜻풀이","범주")
      .withColumnRenamed("어휘","voca")
        .withColumnRenamed("고유 여부", "is_identify")
        .withColumnRenamed("품사","partOfSpeech")
        .withColumnRenamed("뜻풀이","meaning")
        .withColumnRenamed("범주","category")

    println("------")
    filterData.saveToEs("dic_sample/_doc") //("indexname/type")
  }

}

https://stackoverflow.com/questions/44196741/how-to-construct-dataframe-from-a-excel-xls-xlsx-file-in-scala-spark

elasticsearch에서는 index의 이름은 항상 소문자를 적어야한다. 그리고 elasticsearch 6버전 부터는 type을 하나만 지원하기 때문에 주의해야한다.

결과는 Elasticsearch에 잘 추가 된것을 알 수 있다.

{
"_index": "dic_sample",
"_type": "_doc",
"_id": "I1DvcWwB7eXwunPlzEi0",
"_version": 1,
"_score": 1,
"_source": {
"voca": "곡지통",
"고유어 여부": "한자어",
"partOfSpeech": "「명사」 ",
"meaning": "목을 놓아 매우 슬프게 욺. ",
"category": "일반어 "
}
}

Error(java.lang.NoSuchMethodError)

컴파일을 돌리고 나면 아래와 같이 에러가 날수도 있다.

Caused by: java.lang.NoSuchMethodError: 
scala.Predef$.refArrayOps([Ljava/lang/Object;)Lscala/collection/mutable/ArrayOps;   

이유는 프로젝트에서는 2.12 버전의 scala를 사용하고 있는데, 실행되는건 2.11의 버전을 실행하고있기 때문이다.

참고

https://alvinalexander.com/source-code/scala-java-lang-nosuchmethoderror-compiler-message


spark 2+ 부터는

sparkSession 에 SparkContext와 SqlContext를 합쳤다고 하는데 그건 추후에 알아보자!

https://databricks.com/blog/2016/08/15/how-to-use-sparksession-in-apache-spark-2-0.html

ElasticSearch 모니터링툴 설치하기

두가지 종류가 있음

apache elasiticHQ

파이썬 설치 

https://linuxize.com/post/how-to-install-python-3-on-centos-7/

Apache elasticsearch HQ

http://www.elastichq.org/gettingstarted.html



$sudo yum install centos-release-scl


$sudo yum install rh-python36

$python --version

$scl enable rh-python36 bash

$python --version

install HQ

1. Download or clone the repository https://github.com/ElasticHQ/elasticsearch-HQ

2. Navigate to the root of the repository: pip install -r requirements.txt

3. Start the server: python3 application.py

4. Point your browser to: http://localhost:5000

Kibana monitoring

두 번째는 elasticsearch의 모니터링툴

6.5 부터는 모니터링툴이 자동으로 포함되어잇지만 이전버전들은 설치 해야함 .

현재 사용중인 6.2.2 에서는 kibana monitoring 을 사용하려면 xpack 설치가 필요

elasticsearch와 kibana 에 x-pack 설치 (sudo 권한)

[user@localhost bin]$ ./elasticsearch-plugin install x-pack
[sudo] pduser의 암호:
-> Downloading x-pack from elastic
[=================================================] 100%  
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@     WARNING: plugin requires additional permissions     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
* java.io.FilePermission \\.\pipe\* read,write
* java.lang.RuntimePermission accessClassInPackage.com.sun.activation.registries
* java.lang.RuntimePermission getClassLoader
* java.lang.RuntimePermission setContextClassLoader
* java.lang.RuntimePermission setFactory
* java.net.SocketPermission * connect,accept,resolve
* java.security.SecurityPermission createPolicy.JavaPolicy
* java.security.SecurityPermission getPolicy
* java.security.SecurityPermission putProviderProperty.BC
* java.security.SecurityPermission setPolicy
* java.util.PropertyPermission * read,write
See http://docs.oracle.com/javase/8/docs/technotes/guides/security/permissions.html
for descriptions of what these permissions allow and the associated risks.

Continue with installation? [y/N]y
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@        WARNING: plugin forks a native controller        @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
This plugin launches a native controller that is not subject to the Java
security manager nor to system call filters.

Continue with installation? [y/N]y
Elasticsearch keystore is required by plugin [x-pack-security], creating...
-> Installed x-pack with: x-pack-core,x-pack-deprecation,x-pack-graph,x-pack-logstash,x-pack-ml,x-pack-monitoring,x-pack-security,x-pack-upgrade,x-pack-watcher

설치를 하면 bin 디렉토리안에 x-pack 폴더가 생긴다 폴더에 들어가서 비밀번호 설정을 할것이다.

[user@localhost x-pack]$ ./setup-passwords interactive
Initiating the setup of passwords for reserved users elastic,kibana,logstash_system.
You will be prompted to enter passwords as the process progresses.
Please confirm that you would like to continue [y/N]y


Enter password for [elastic]:
Reenter password for [elastic]:
Enter password for [kibana]:
Reenter password for [kibana]:
Enter password for [logstash_system]:
Reenter password for [logstash_system]:
Changed password for user [kibana]
Changed password for user [logstash_system]
Changed password for user [elastic]
[user@localhost x-pack]$

kibana xpack 설치

kibana 홈디렉토리의 bin디렉토리로 이동

[user@localhost bin]$ ./kibana-plugin install x-pack
Attempting to transfer from x-pack
Attempting to transfer from https://artifacts.elastic.co/downloads/kibana-plugins/x-pack/x-pack-6.2.2.zip
Transferring 269704442 bytes....................
Transfer complete
Retrieving metadata from plugin archive
Extracting plugin archive
Extraction complete
Optimizing and caching browser bundles...
Plugin installation complete

kiabana 비밀번호 설정

xpack 을 설치하면 보안이 복잡하다 그렇기 때문에 나는 사용하지 않기로 함 !!

elasticsearch.yml 파일과 kibana.yml 파일에 다음과 같이 추가해줘야함

xpack.security.enabled: false

설정 후에 elasticsearch 를 종료하고 다시 시작해야한다.

참고자료

https://www.elastic.co/guide/en/elasticsearch/reference/6.2/installing-xpack-es.html

http://www.kwangsiklee.com/2017/02/%EC%97%98%EB%9D%BC%EC%8A%A4%ED%8B%B1%EC%84%9C%EC%B9%98%EC%97%90%EC%84%9C-x-pack%EC%9C%BC%EB%A1%9C-%EC%9D%B8%EC%A6%9Dauthentication-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0/

logstash를 사용해서 데이터를 삽입했다

korean_dic.conf

input {
  file {
    path => "//Users/daeyunkim/Documents/07.ELK_v6/data/korean_dic/filter_korean5.txt"
    start_position => "beginning"
    sincedb_path => "/dev/null"
  }
}
filter {
  csv {
    separator => "|"
    columns => ["voca","isNative","partOfSpeech","meaning","category"]
  }

  mutate {
    remove_field => ["@version","@timestamp","path","host", "tags", "message"]
  } 
}
output {
  elasticsearch {
    hosts => "http://localhost:9200"
    index => "dic_korean"
    document_type => "_doc"
  }
  stdout {}
}




$ $LOGSTASH_HOME/bin/logstash -f korean_dic.conf

위와 같이 실행을 하게 되면

데이터가 들어가는 것을 확인할수 있다.

확인을 해보자!!

GET dic_korean/_search
{
  "query":{
    "match_all":{

    }
  },"size":3
}

3개만 불러와보자

{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 30002,
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "dic_korean",
        "_type" : "_doc",
        "_id" : "KDPEoGsBaqdVh3ql3lt9",
        "_score" : 1.0,
        "_source" : {
          "partOfSpeech" : "「명사」",
          "voca" : "가가-성자",
          "category" : "일반어",
          "meaning" : "성문 사과(聲聞四果)에서, 일래과를 얻기 위하여 수행하는 성인.",
          "isNative" : "한자어"
        }
      },
      {
        "_index" : "dic_korean",
        "_type" : "_doc",
        "_id" : "LTPEoGsBaqdVh3ql3lt9",
        "_score" : 1.0,
        "_source" : {
          "partOfSpeech" : "「부사」",
          "voca" : "가강-히",
          "category" : "일반어",
          "meaning" : "더욱 강력하고 완강하게.",
          "isNative" : "혼종어"
        }
      },
      {
        "_index" : "dic_korean",
        "_type" : "_doc",
        "_id" : "MDPEoGsBaqdVh3ql3lt9",
        "_score" : 1.0,
        "_source" : {
          "partOfSpeech" : "「명사」",
          "voca" : "가겟-세",
          "category" : "일반어",
          "meaning" : "가게를 하는 자리를 빌려 쓰는 대가로 주는 돈.",
          "isNative" : "혼종어"
        }
      }
    ]
  }
}

데이터가 제대로 들어간것을 알수 있다.

이번주 목표 끝~~!!

  1. excel 파일로 되어있던것을 tab으로 구분지어서 변경하기

  2. 탭으로 구분된 파일의 형태의 인코딩변경하기

    $iconv -f cp949 -t utf-8 korean_dic.txt > korean_utf_dic.txt

    참고 : iconv -l 을 사용하면 인코딩에 지원하는 모든 언어를 볼수 있다.
    한글 파일들으 대부분 ECU_KR 또는 cp949 인것같지만, 인코딩이 어떤것으로 되어있는지 알수 있는 방법이 있을까???

  3. awk를 사용해서 필요한 데이터 파일 만들기

    $awk -F '\t' '{print $1"\t"$3"\t"$11"\t"$16"\t"$18}' korean_utf_dic.txt

천천히 다시 해보자

1번까지를 진행하고 난후 \t를 | 로 변경하게 되면 아래와 같다.

어휘|구성 단위|고유어 여부|원어|어원|주표제어|부표제어|발음|활용|검색용 이형태|품사|공통 문형|의미 문형|공통 문법|의미 문법|뜻풀이|용례|범주|전문 분
야|속담|관용구|대역어|생물 분류군 정보|멀티미디어|관련 어휘^M
가(01)|단어|고유어||<_<_<용가>|||[가ː]|||"「명사」
"||"「1」
「2」
「3」
「4」
"||"「1」
「2」
「3」
「4」((일부 명사 뒤에 붙어))
"|"「1」경계에 가까운 바깥쪽 부분.
「2」어떤 중심 되는 곳에서 가까운 부분.
「3」그릇 따위의 아가리의 주변.
「4」‘주변’의 뜻을 나타내는 말.
"|"「3」참기름을 따를 때 가에 흘리지 않도록 조심해라.
「4」강가.
「4」냇가.
「4」우물가.
"|"「1」일반어
「2」일반어
「3」일반어
「4」일반어
"|||||||^M
가(02)|단어|고유어|||||[가]|||"「명사」

그래서 탭을 |로 변경

:%s /\t/|/g

\n을 없애고 ^M 이라는 구부자를 \n으로 변경해주면되는데 ..음 ...

우선 \n을 없앤다.

휘|구성 단위|고유어 여부|원어|어원|주표제어|부표제어|발음|활용|검색용 이형태|품사|공통 문형|의미 문형|공통 문법|의미 문법|뜻풀이|용례|범주|전문 분
야|속담|관용구|대역어|생물 분류군 정보|멀티미디어|관련 어휘^M가(01)|단어|고유어||<_<_<용가>|||[가ː]|||"「명사」"||"「1」「2」「3」「4」"||"「1」
「2」「3」「4」((일부 명사 뒤에 붙어))"|"「1」경계에 가까운 바깥쪽 부분.「2」어떤 중심 되는 곳에서 가까운 부분.「3」그릇 따위의 아가리의 주변.「4」‘주변’의 뜻을 나타내는 말."|"「3」참기름을 따를 때 가에 흘리지 않도록 조심해라.「4」강가.「4」냇가.「4」우물가."|"「1」일반어「2」일반어「3」일반어「
4」일반어"|||||||^M가(02)|단어|고유어|||||[가]|||"「명사」"||""||""|"서양 음악의 칠음 체계에서, 여섯 번째 음이름. 계이름 ‘라’와 같다."|""|"일반어"|"『음악』"||||||"의미동의어(2) :  아08(A),  에이05(A / a)"^M가(03)|단어|한자어|加|||가-하다||||"「품사 없음」"||""||""|"‘가하다01’의 어근."||"일반어"|||||||^M가(04)|단어|한자어|加||||[가]|||"「명사」"||""||""|"부여와 고구려에서, 족장이나 고관을 이르던 말. 본디 씨족이나 부족의 우두머리를 뜻하는 말
이었으나 국가의 발달과 함께 직명(職名)이 된 것으로, 부여의 마가ㆍ우가ㆍ구가ㆍ저가, 고구려의 대가ㆍ상가 따위가 있다."||"일반어"|"『역사』"||||||^M가(05)|단어|한자어|可|||가-하다|[가ː]|||"「명사」"||"「1」「2」「3」「4」"||"「1」「2」「3」「4」"|"「1」옳거나 좋음.「2」회의 따위에서, 어떤 안건에 대
하여 표결을 할 때 찬성하는 의사 표시.「3」성적이나 등급을 ‘수, 우, 미, 양, 가’의 다섯 단계로 나눌 때 가장 낮은 단계.「4」어떤 행위가 허용되거나 가능
함 또는 좋음을 이르는 말."|"「1」이 사람 말도 가요, 저 사람 말도 가요 하면 도대체 어떤 사람 말을 따라야 합니까?「2」의원 여러분께서는 본 안건에 대해
 가인지 부인지를 결정해 주시기 바랍니다.「3」다른 과목들은 성적이 괜찮은 편인데, 체육만 가를 받았다.「4」연소자 관람 가.「4」분할 상환 가."|"「1」일
반어「2」일반어「3」일반어「4」일반어"|"「3」『교육』"||||||"의미반대말(2) : 「2」 부07「2」(否), 「4」 불가01「2」(不可)"^M가(06)|단어|한자어|枷||||[가]|||"「명사」"||""||""|"죄인에게 씌우던 형틀. 두껍고 긴 널빤지의 한끝에 구멍을 뚫어 죄인의 목을 끼우고 비녀장을 질렀다."||"일반어"|"『역사』"||||||"의미동의어(1) :  칼02"^M가(07)|단어|한자어|家||||[가]|||"「명사」"||""||""|"예전에, 같은 호적에 들어 있는 친족 집단을 이르던 말."|"성구는 그 대>목에서 묘하게 처절해지는 버릇이 있었다. 외할머니 교하댁의 집에 대한 소문난 집착을 가를 잇고자 하는 맹목적 집념과 동일시하려는 그 나름의 시각 때문이>었다."|"일반어"|"『사회 일반』"||||||^M

없애면 ^M만 이제 새로운 라인으로 변경해주면된다.

:%s/<Ctrl-V><Ctrl-M>/\r/g

을 사용해서 ^M을 변경해주었다

\n으로 행을 변경하는줄알았는데, \r 이 줄을 변경해주는것이다.

결과는 다음과 같다.

1 어휘|구성 단위|고유어 여부|원어|어원|주표제어|부표제어|발음|활용|검색용 이형태|품사|공통 문형|의미 문형|공통 문법|의미 문법|뜻풀이|용례|범주 >
      전문 분야|속담|관용구|대역어|생물 분류군 정보|멀티미디어|관련 어휘
    2 가(01)|단어|고유어||<_<_<용가>|||[가ː]|||"「명사」"||"「1」「2」「3」「4」"||"「1」「2」「3」「4」((일부 명사 뒤에 붙어))"|"「1」경계에 가      까운 바깥쪽 부분.「2」어떤 중심 되는 곳에서 가까운 부분.「3」그릇 따위의 아가리의 주변.「4」‘주변’의 뜻을 나타내는 말."|"「3」참기름을 따를 때       가에 흘리지 않도록 조심해라.「4」강가.「4」냇가.「4」우물가."|"「1」일반어「2」일반어「3」일반어「4」일반어"|||||||
    3 가(02)|단어|고유어|||||[가]|||"「명사」"||""||""|"서양 음악의 칠음 체계에서, 여섯 번째 음이름. 계이름 ‘라’와 같다."|""|"일반어"|"『음악』"||||      ||"의미동의어(2) :  아08(A),  에이05(A / a)"
    4 가(03)|단어|한자어|加|||가-하다||||"「품사 없음」"||""||""|"‘가하다01’의 어근."||"일반어"|||||||
    5 가(04)|단어|한자어|加||||[가]|||"「명사」"||""||""|"부여와 고구려에서, 족장이나 고관을 이르던 말. 본디 씨족이나 부족의 우두머리를 뜻하는 말이>      었으나 국가의 발달과 함께 직명(職名)이 된 것으로, 부여의 마가ㆍ우가ㆍ구가ㆍ저가, 고구려의 대가ㆍ상가 따위가 있다."||"일반어"|"『역사』"||||||
    6 가(05)|단어|한자어|可|||가-하다|[가ː]|||"「명사」"||"「1」「2」「3」「4」"||"「1」「2」「3」「4」"|"「1」옳거나 좋음.「2」회의 따위에서, 어떤        안건에 대하여 표결을 할 때 찬성하는 의사 표시.「3」성적이나 등급을 ‘수, 우, 미, 양, 가’의 다섯 단계로 나눌 때 가장 낮은 단계.「4」어떤 행위가       허용되거나 가능함 또는 좋음을 이르는 말."|"「1」이 사람 말도 가요, 저 사람 말도 가요 하면 도대체 어떤 사람 말을 따라야 합니까?「2」의원 여러분      께서는 본 안건에 대해 가인지 부인지를 결정해 주시기 바랍니다.「3」다른 과목들은 성적이 괜찮은 편인데, 체육만 가를 받았다.「4」연소자 관람 가.>      「4」분할 상환 가."|"「1」일반어「2」일반어「3」일반어「4」일반어"|"「3」『교육』"||||||"의미반대말(2) : 「2」 부07「2」(否), 「4」 불가01「2>
      」(不可)"

이제 구분자 | 를 사용하여 필요한 정보만 가지고 와보자

$ awk -F '|' '{print $1"|"$3"|"$11"|"$16"|"$18}' korean4.txt > filter_korean5.txt

마지막 전처리한 결과는 다음과 같다.

1 어휘|고유어 여부|품사|뜻풀이|범주
    2 가(01)|고유어|"「명사」"|"「1」경계에 가까운 바깥쪽 부분.「2」어떤 중심 되는 곳에서 가까운 부분.「3」그릇 따위의 아가리의 주변.「4」‘주변’의 >
      뜻을 나타내는 말."|"「1」일반어「2」일반어「3」일반어「4」일반어"
    3 가(02)|고유어|"「명사」"|"서양 음악의 칠음 체계에서, 여섯 번째 음이름. 계이름 ‘라’와 같다."|"일반어"
    4 가(03)|한자어|"「품사 없음」"|"‘가하다01’의 어근."|"일반어"
    5 가(04)|한자어|"「명사」"|"부여와 고구려에서, 족장이나 고관을 이르던 말. 본디 씨족이나 부족의 우두머리를 뜻하는 말이었으나 국가의 발달과 함께 >
      직명(職名)이 된 것으로, 부여의 마가ㆍ우가ㆍ구가ㆍ저가, 고구려의 대가ㆍ상가 따위가 있다."|"일반어"
    6 가(05)|한자어|"「명사」"|"「1」옳거나 좋음.「2」회의 따위에서, 어떤 안건에 대하여 표결을 할 때 찬성하는 의사 표시.「3」성적이나 등급을 ‘수, 우      , 미, 양, 가’의 다섯 단계로 나눌 때 가장 낮은 단계.「4」어떤 행위가 허용되거나 가능함 또는 좋음을 이르는 말."|"「1」일반어「2」일반어「3」일반      어「4」일반어"
    7 가(06)|한자어|"「명사」"|"죄인에게 씌우던 형틀. 두껍고 긴 널빤지의 한끝에 구멍을 뚫어 죄인의 목을 끼우고 비녀장을 질렀다."|"일반어"
    8 가(07)|한자어|"「명사」"|"예전에, 같은 호적에 들어 있는 친족 집단을 이르던 말."|"일반어"

국어사전검색을 구현하는 프로젝트를 만들어보자!!

sample Dataset

{
  "voca":"가위",//[0]
  "isNative":"한자어",//[2]
  "partOfSpeech": "명사",//[10]
  "meaning":"옷감, 종이, 머리털 따위를 자르는 기구...", //[15]
  "category":"일반어", //[17]3
}

출처 : 표준 국어 대사전

PUT _template/dic_template
{
  "index_patterns": ["dic*"],
  "settings": {
    "index": {
      "number_of_shards": 5,
      "number_of_replicas": 0
    },
    "analysis": {
      "analyzer": {
        "ngram_analyzer": {
          "type": "custom",
          "tokenizer": "ngram_tokenizer",
          "filter": [
            "lowercase",
            "trim"
          ]
        },
        "edge_ngram_analyzer": {
          "type": "custom",
          "tokenizer": "edge_ngram_tokenizer",
          "filter": [
            "lowercase",
            "trim",
            "edge_ngram_filter_front"
          ]
        },
        "edge_ngram_analyzer_back": {
          "type": "custom",
          "tokenizer": "edge_ngram_tokenizer",
          "filter": [
            "lowercase",
            "trim",
            "edge_ngram_filter_back"
          ]
        }
      },
      "tokenizer": {
        "ngram_tokenizer": {
          "type": "nGram",
          "min_gram": "1",
          "max_gram": "50",
          "token_chars": [
            "letter",
            "digit",
            "punctuation",
            "symbol"
          ]
        },
        "edge_ngram_tokenizer": {
          "type": "edgeNGram",
          "min_gram": "1",
          "max_gram": "50",
          "token_chars": [
            "letter",
            "digit",
            "punctuation",
            "symbol"
          ]
        }
      },
      "filter": {
        "edge_ngram_filter_front": {
          "type": "edgeNGram",
          "min_gram": "1",
          "max_gram": "50",
          "side": "front"
        },
        "edge_ngram_filter_back": {
          "type": "edgeNGram",
          "min_gram": "1",
          "max_gram": "50",
          "side": "back"
        }
      }
    }
  },
    "mappings": {
      "_doc": {
        "properties": {
          "voca": {
            "type": "text",
            "analyzer": "ngram_analyzer",
            "search_analyzer": "ngram_analyzer",
            "boost": 3
          },
          "isNative": {
            "type": "keyword"
          },
          "partOfSpeech": {
            "type": "keyword"
          },
          "meaning": {
            "type": "text",
            "analyzer": "edge_ngram_analyzer",
            "search_analyzer": "edge_ngram_analyzer"
          },
          "category": {
            "type": "keyword"
          }
        }
      }
    }
}

인덱스 생성 및 예제 데이터 삽입

PUT dic_korean

POST dic_korean/_doc/1
{
  "voca":"가위",
  "isNative":"한자어",
  "partOfSpeech": "명사",
  "meaning":"옷감, 종이, 머리털 따위를 자르는 기구...", 
  "category":"일반어"
}

GET dic_korean/_mapping

데이터 검색 확인

GET dic_korean/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "term": {
            "voca": "가위"
          }
        }
      ]
    }
  }
}

GET dic_korean/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "term": {
            "meaning": "자르"
          }
        }
      ]
    }
  }
}

+ Recent posts