'Elastic/Elasticsearch'에 해당되는 글 385건

  1. 2017.12.21 [Elasticsearch] 실행 시 option arguments
  2. 2017.12.20 [Elasticsearch] simple query 내 synonym graph 사용
  3. 2017.12.18 [Elasticsearch] Java Security Manager 관련
  4. 2017.12.06 [Elasticsearch] Refresh interval while bulk request 2
  5. 2017.11.15 [Elasticsearch] elasticsearch-arirang-analyzer-6.0.0 릴리즈
  6. 2017.11.14 [Elasticsearch] _id mapping 시 path 설정
  7. 2017.10.19 [Elasticsearch] Arirang Analyzer + Elasticsearch Analyzer Plugin 사용자 관점 개발리뷰
  8. 2017.08.03 [Elasticsearch] Snapshot and Restore 알아보기
  9. 2017.07.17 [Elasticsearch] Multi Search API 사용
  10. 2017.07.13 [Elasticsearch] post_filter

[Elasticsearch] 실행 시 option arguments

Elastic/Elasticsearch 2017. 12. 21. 14:13

Elasticsearch 클래스 보시면 잘 나와 있습니다.

저는 그냥 코드만 발췌했습니다.

Elasticsearch() {
super("starts elasticsearch");
versionOption = parser.acceptsAll(Arrays.asList("V", "version"),
"Prints elasticsearch version information and exits");
daemonizeOption = parser.acceptsAll(Arrays.asList("d", "daemonize"),
"Starts Elasticsearch in the background")
.availableUnless(versionOption);
pidfileOption = parser.acceptsAll(Arrays.asList("p", "pidfile"),
"Creates a pid file in the specified path on start")
.availableUnless(versionOption)
.withRequiredArg()
.withValuesConvertedBy(new PathConverter());
quietOption = parser.acceptsAll(Arrays.asList("q", "quiet"),
"Turns off standard output/error streams logging in console")
.availableUnless(versionOption)
.availableUnless(daemonizeOption);
}


PID 저장)

$ bin/elasticsearch -d -p ./bin/es.pid

- 이와 같이 하시면 daemon 으로 실행 되면서 process id 는 bin/ 디렉토리 아래 es.pid 파일로 생성이 됩니다.

:

[Elasticsearch] simple query 내 synonym graph 사용

Elastic/Elasticsearch 2017. 12. 20. 10:11

일단 나중에 잊을 수도 있어서 keep 합니다.


Ref.

https://www.elastic.co/guide/en/elasticsearch/reference/6.1/query-dsl-simple-query-string-query.html#_synonyms_2


Simple Query 사용 시 추가 되는 parameter 인데, 이 기능을 잘 활용하면  query expansion (query rewrite) 기능을 대체 할 수도 있겠다는 생각이 듭니다.

그래서 일단 기록!

Synonyms

The simple_query_string query supports multi-terms synonym expansion with the synonym_graph token filter. When this filter is used, the parser creates a phrase query for each multi-terms synonyms. For example, the following synonym: "ny, new york" would produce:


(ny OR ("new york"))


It is also possible to match multi terms synonyms with conjunctions instead:

GET /_search

{

   "query": {

       "simple_query_string" : {

           "query" : "ny city",

           "auto_generate_synonyms_phrase_query" : false

       }

   }

}

약간의 부연 설명을 하자면, 보통 사용자가 입력한 검색어만 가지고 검색을 하는 경우는 이커머스에서는 거의 없습니다.

대부분 사용자가 입력한 검색어 + 확장검색어 형태로 질의를 하게 되는데요.

일반적으로 가장 많이 사용하는 방식이 색인 시점에 동의어를 통한 검색어 확장입니다.

이건 색인 시점이고 위 기능을 잘 활용하게 되면 질의 시점에 검색어 확장을 통한 상품 매칭을 할 수 있습니다.

저는 보통 Query Expansion 기능이라고 부르는데요. 이 작업은 Query Rewriter 라고 불리는 영역에서도 수행이 되기도 합니다.


간단한 예를 들자면)

"나이키" 라는 검색어가 들어 왔을 때 이를 개인화 query expansion 기능을 적용 한다면 저 키워드를 입력한 사용자가 선호 하는게 "운동화" 였다면, 실제 검색 매칭에 사용되는 검색어는 "나이키" + "운동화" 가 되는 것입니다.

이건 단순 예시 입니다.

:

[Elasticsearch] Java Security Manager 관련

Elastic/Elasticsearch 2017. 12. 18. 15:45

elasticsearch plugin 을 만들다 보면 보안정책 강화에 따라 permission 에러가 발생 하는 경우가 있습니다.

이럴 경우 아래 내용을 통해서 문제를 해결해 보시기 바랍니다.


[Elasticsearch Java Security Manager]

$ export JAVA_OPTS="${JAVA_OPTS} -Djava.security.policy=file:///path/to/my.policy` ./bin/elasticsearch

# or config/jvm.options

$ vi config/jvm.options

-Djava.security.policy=file:///path/to/my.policy

$ export JAVA_OPTS="${JAVA_OPTS} -Dsecurity.manager.enabled=false` ./bin/elasticsearch

# or config/jvm.options

$ vi config/jvm.options 

-Dsecurity.manager.enabled=false


Example) my.policy

# Ref. https://docs.oracle.com/javase/7/docs/technotes/guides/security/PolicyFiles.html


grant {

permission org.elasticsearch.script.ClassPermission "java.util.Base64"; // allow class

permission org.elasticsearch.script.ClassPermission "java.util.*"; // allow package

permission org.elasticsearch.script.ClassPermission "*"; // allow all (disables filtering basically)

permission java.io.FilePermission "$ES_HOME/config/resource/*", "read"; // allow all files

};


Example) plugin-metadata/plugin-security.policy (recommended)

# Ref. https://www.elastic.co/guide/en/elasticsearch/plugins/current/plugin-authors.html#plugin-authors-jsm

# Ref. https://github.com/elastic/elasticsearch/blob/master/plugins/discovery-gce/src/main/plugin-metadata/plugin-security.policy

:

[Elasticsearch] Refresh interval while bulk request

Elastic/Elasticsearch 2017. 12. 6. 14:00

작업 하면서 이상한 현상이 발생을 해서 분석 하다 보니 누구나 경험 할 수 있는 것 같아 올려 봅니다.


참고문서)

https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-update-settings.html

https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-refresh.html


보통 bulk request 하기 전에 아래 설정을 적용하게 됩니다.

index.refresh_interval: "-1"

이 설정은 해당 index 의 settings 에서 설정 합니다.


이 설정을 하게 되면 bulk request 시 refresh action 을 수행 하지 않게 되는데요.

제가 경험한 현상은 disable 했음에도 불구하고 refresh thread 수가 증가 한다는 것이였습니다.


문제는 역시 elasticsearch 에 있었던 것이 아닌 저의 잘 못 이였습니다.

이유는 제가 정의한 mapping 정보에서 dynamic field 에 따른 template 구성이 영향을 주는 것이였습니다.

결과적으로 dynamic field 설정으로 색인 시 mapping 정보가 바뀌게 되고 이를 반영 하기위해 IndexService 가 updateMetaData() 를 수행 하게 됩니다. 이 과정에서 자동으로 refresh 가 발생을 하기 때문에 bulk request 시 왜 성능이 안나오지 하지 마시고 어떤 구성을 하셨는지 부터 분석해 보시면 더 좋을 것 같습니다.


:

[Elasticsearch] elasticsearch-arirang-analyzer-6.0.0 릴리즈

Elastic/Elasticsearch 2017. 11. 15. 23:49

페북에 올렸더니 스팸 이라고 삭제 당했내요. ㅡ.ㅡ;

https://github.com/HowookJeong/elasticsearch-analysis-arirang/tree/6.0.0

https://github.com/HowookJeong/elasticsearch-analysis-arirang/releases/download/6.0.0/elasticsearch-analysis-arirang-6.0.0.zip


설치 방법은 잘 아시겠지만 두 가지 입니다.

$ bin/elasticsearch-plugin install file:///elasticsearch-analysis-arirang-6.0.0.zip

$ bin/elasticsearch-plugin install https://github.com/HowookJeong/elasticsearch-analysis-arirang/releases/download/6.0.0/elasticsearch-analysis-arirang-6.0.0.zip


적용된 version 은 아래와 같습니다.

elasticsearch-6.0.0

lucene-7.0.1

arirang.lucene-analyzer-7.0.1

arirang.morph-1.1.0


혹시 arirang plugin 을 어떻게 만드는지 궁금하신 분들은 아래 글 참고하세요.

[Elasticsearch] Arirang Analyzer + Elasticsearch Analyzer Plugin 사용자 관점 개발리뷰


:

[Elasticsearch] _id mapping 시 path 설정

Elastic/Elasticsearch 2017. 11. 14. 11:13

_id 에 사용하시는 데이터의 primary key 값을 지정 하고 싶을때가 많이 있습니다.

기억이 가물가물해서 잠시 찾아 봤는데요.

2.4 까지는 path 설정 기능이 살아 있었는데 5.X 들어 가면서 삭제 되었습니다.


2.4)

private String path = Defaults.PATH;


public Builder() {

    super(Defaults.NAME, new FieldType(Defaults.FIELD_TYPE));

    indexName = Defaults.INDEX_NAME;

}


public Builder path(String path) {

    this.path = path;

    return builder;

}


그래서 _id field 에 primary key 를 넣고 싶으실 경우  IndexRequestBuilder.setId() 를 이용하시거나 JSON 파일 만드실 때 _id field 에 primary key 값을 넣어 주시면 됩니다.


:

[Elasticsearch] Arirang Analyzer + Elasticsearch Analyzer Plugin 사용자 관점 개발리뷰

Elastic/Elasticsearch 2017. 10. 19. 14:25

사용자 관점에서 어떻게 개발 하는지 정리해 보았습니다.


Elasticsearch를 서비스에 사용하면서 한글 처리를 위해 어떤 analyzer를 사용해야 할지 고민해 보신적이 있을 것입니다.

오늘은 제가 사용하고 있는 Lucene Korean Analyzer와 이를 Elasticsearch에 plugin으로 설치하고 사용하는 방법을 알아 보도록 하겠습니다.


들어 가기에 앞서 lucene에서 제공하는 analyzer의 기본 구성과 동작에 대해서 살펴 보겠습니다.

Lucene에서 제공하는 analyzer 는 하나의 tokenizer와 다수의 filter로 구성이 됩니다.

Filter 는 CharFilter와 TokenFilter 두 가지가 있습니다.

CharFilter는 입력된 문자열에서 불필요한 문자를 normalization 하기 위해 사용되며 TokenFilter는 tokenizer에 의해 분해된 token에 대한 filter 처리를 하게 됩니다.

결과적으로 아래와 같은 순서로 analysis 된다고 이해 하면 됩니다.


Input Text 

Character Filter    Filtered Text        

Tokenizer              Tokens                 →

Token Filter          Filtered Tokens   

Output Tokens


이제 본론으로 들어 가겠습니다.

Lucene Korean Analyzer는 현재 이수명님에 의해 개발 및 유지보수가 되고 있으며 오픈소스로 등록이 되어 있습니다.

관련 소스코드는 아래 두 가지 repository를 통해서 제공 되고 있습니다.


[svn 주소]

https://lucenekorean.svn.sourceforge.net/svnroot/lucenekorean


[github 주소]

https://github.com/korlucene


 Lucene Korean Analyzer 는 지금 Arirang 이라고 부르고 있습니다.


Arirang의 프로젝트 구성은 크게 두 부분으로 나뉩니다.

  • arirang analyzer
  • arirang morph


1. arirang morph

  이 프로젝트는 한글 형태소에 대한 기본 분석과 사전 정보로 구성이 되어 있습니다.

  한글 처리와 사전 정보를 변경 하고 싶을 경우 본 프로젝트의 코드를 분석하고 수정 해서 활용을 하실 수 있습니다.


2. arirang analyzer

  이 프로젝트는 lucene의 analyzer를 상속받아 lucene에서 사용 할 수 있도록 구성이 되어 있습니다.

  Lucene의 analyzer pipeline에 필요한 

    - KoreanAnalyzer

    - KoreanFilter

    - KoreanFilterFactory

    - KoreanToken

    - KoreanTokenizer

    - KoreanTokenizerFactory

  등이 주요 클래스로 구현이 되어 있습니다.


한글 형태소 분석에서 중요한 역할을 하는 부분으로 사전 이라는 것이 있으며, 이를 알아 보도록 하겠습니다.

arirang.morph 프로젝트에 포함이 되어 있으며 언급 한것과 같이 지속적인 업데이트 및 변경이 가능 합니다.


1. Dictionary classpath

  org/apache/lucene/analysis/ko/dic


2. Dictionary files

  org/apache/lucene/analysis/ko

    korean.properties

  org/apache/lucene/analysis/ko/dic

    abbreviation.dic

    cj.dic

    compounds.dic

    eomi.dic

    extension.dic

    josa.dic

    mapHanja.dic

    occurrence.dic

    prefix.dic

    suffix.dic

    syllable.dic

    total.dic

    uncompounds.dic


3. 주요 사전 설명

주요 사전 설명 이라고는 했지만 쉽고 빠르게 활용할 수 있는 사전이라고 이해 하시면 좋을 것 같습니다.

  • total.dic
    이 사전 파일은 arirang analyzer 에서 사용하는 기본 사전으로 그대로 사용을 하시면 됩니다.
    다만, 수정이 필요 하실 경우 아래 extension.dic 파일을 활용 하시면 됩니다.
  • extension.dic
    확장사전이라고 부르며, 사전 데이터를 추가 해야 할 경우 이 파일에 추가해서 운영 및 관리를 하시면 됩니다.
  • compounds.dic
    복합명사 사전으로 하나의 단어가 여러개의 단어로 구성이 되어 있을 경우 이를 분해하기 위한 사전 정보를 관리 하는 파일 입니다.


4. total.dic / extension.dic 파일 구조

체언 용언 기타품사 하여(다)동사 되어(다)동사 '내'가붙을수있는체언 NA NA NA 불규칙변경


예)

  # 엘사는 명사이고 동사, 기타품사, 불규칙이 아니다, 라고 가정하면 아래와 같이 표현이 됩니다.

  엘사,100000000X


  # 노래는 명사이고 하여(다) 동사가 됩니다.

  노래,100100000X


  # 소리는 명사이고 소리내다와 같이 내가 붙을 수 있는 명사 입니다.

  소리,100001000X


불규칙 정보는 아래와 같으며 원문을 참고 하시기 바랍니다.

  B : ㅂ 불규칙

  H : ㅎ 불규칙

  L : 르 불규칙

  U : ㄹ 불규칙

  S : ㅅ 불규칙

  D : ㄷ 불규칙

  R : 러 불규칙

  X : 규칙

 원문 : http://cafe.naver.com/korlucene/135


5. compound.dic 파일 구조

분해전단어:분해후단어1,분해후단어2,...,분해후단어N:DBXX


분해전단어에 하여(다)동사(D), 되어(다)동사(B) 가 붙을 수 있는지 확인 하셔야 합니다.


예)

  객관화:객관,화:1100


  이와 같이 된 이유는

    객관화하다

    객관화되다

  가 되기 때문입니다.


  참고)

  http://krdic.naver.com/search.nhn?query=%EA%B0%9D%EA%B4%80%ED%99%94&kind=all


이제 부터는 소스 코드를 내려 받아서 빌드 후 Elasticsearch plugin을 만드는 방법을 알아 보겠습니다.


1. 프로젝트 clone

기본적으로 master branch 를 받습니다.


$ git clone https://github.com/korlucene/arirang.morph.git

$ git clone https://github.com/korlucene/arirang-analyzer-6.git


2. Maven build

  • 두 프로젝트 모드 maven project로 빌드 장비에 maven 이 설치가 되어 있어야 합니다.
    maven 설치 참고 - https://maven.apache.org/
  • arirang-analyzer-6 프로젝트에 기본적으로 arirang.morph 패키지가 등록이 되어 있기 때문에 별도 arirang.morph를 수정 하지 않았다면 arirang-analyzer-6 만 빌드하시면 됩니다.


arirang.morph $ mvn clean package

arirang-analyzer-6 $ mvn clean package


3. 기능 테스트

  • 기능 테스트는 arirang-analyzer-6 프로젝트에 포함된 test code를 이용해서 확인해 보시면 됩니다.
  • src/test 아래 TestKoreanAnalyzer1 클래스를 참고하시면 됩니다.

 아래는 이해를 돕기 위해 원본 테스트 코드를 추가 하였습니다.

/**

 * Created by SooMyung(soomyung.lee@gmail.com) on 2014. 7. 30.

 */

public class TestKoreanAnalyzer1 extends TestCase {


  public void testKoreanAnalzer() throws Exception {


    String[] sources = new String[]{

      "고려 때 중랑장(中郞將) 이돈수(李敦守)의 12대손이며",

      "이돈수(李敦守)의",

      "K·N의 비극",

      "金靜子敎授",

      "天國의",

      "기술천이",

      "12대손이며",

      "明憲淑敬睿仁正穆弘聖章純貞徽莊昭端禧粹顯懿獻康綏裕寧慈溫恭安孝定王后",

      "홍재룡(洪在龍)의",

      "정식시호는 명헌숙경예인정목홍성장순정휘장소단희수현의헌강수유령자온공안효정왕후(明憲淑敬睿仁正穆弘聖章純貞徽莊昭端禧粹顯懿獻康綏裕寧慈溫恭安孝定王后)이며 돈령부영사(敦寧府領事) 홍재룡(洪在龍)의 딸이다. 1844년, 헌종의 정비(正妃)인 효현왕후가 승하하자 헌종의 계비로써 중궁에 책봉되었으나 5년 뒤인 1849년에 남편 헌종이 승하하고 철종이 즉위하자 19세의 어린 나이로 대비가 되었다. 1857년 시조모 대왕대비 순원왕후가 승하하자 왕대비가 되었다.",

      "노벨상을"

    };


    KoreanAnalyzer analyzer = new KoreanAnalyzer();


    for (String source : sources) {

      TokenStream stream = analyzer.tokenStream("dummy", new StringReader(source));


      CharTermAttribute termAtt = stream.addAttribute(CharTermAttribute.class);

      PositionIncrementAttribute posIncrAtt = stream.addAttribute(PositionIncrementAttribute.class);

      PositionLengthAttribute posLenAtt = stream.addAttribute(PositionLengthAttribute.class);

      TypeAttribute typeAtt = stream.addAttribute(TypeAttribute.class);

      OffsetAttribute offsetAtt = stream.addAttribute(OffsetAttribute.class);

      MorphemeAttribute morphAtt = stream.addAttribute(MorphemeAttribute.class);

      stream.reset();


      while (stream.incrementToken()) {

        System.out.println(termAtt.toString() + ":" + posIncrAtt.getPositionIncrement() + "(" + offsetAtt.startOffset() + "," + offsetAtt.endOffset() + ")");

      }

      stream.close();

    }


  }

}


이제 arirang에 대한 빌드와 기능테스트가 끝났으니 elasticsearch에 설치 하기 위한 plugin 만드는 방법을 알아 보도록 하겠습니다.

먼저, elasticsearch에서 제공하는 plugins 관련 문서를 시간이 된다면 한번 읽어 보시고 아래 내용을 보시길 추천 드립니다.


Elasticsearch Plugins and Integrations : https://www.elastic.co/guide/en/elasticsearch/plugins/5.5/index.html


Elastic에서 공식문서에서 제공해 주고 있는 예제는 아래 링크에 나와 있으니 구현 시 참고하시기 바랍니다.

 https://github.com/elastic/elasticsearch/tree/master/plugins/jvm-example


 제가 추천하는 것은 elasticsearch source code를 다운받아 official하게 작성된 plugin 코드를 참고하여 구현하는 방법 입니다.


그럼 analysis plugin의 기본 프로젝트 구조를 살펴 보겠습니다.


1. Project Directory

  src/main

  assemblies

    plugin.xml

  java

    org/elasticsearch

      index/analysis

        ${CUSTOM-ANALYZER-NAME}AnalyzerProvider

        ${CUSTOM-ANALYZER-NAME}TokenFilterFactory

        ${CUSTOM-ANALYZER-NAME}TokenizerFactory

      plugin/analysis/arirang

        Analysis${CUSTOM-ANALYZER-NAME}Plugin

  resources

    plugin-descriptor.propeties


2. Files and classes

  • plugin.xml
      maven assembly plugin을 이용한 패키징을 하기 위한 설정을 구성 합니다.
  • plugin-descriptor.propeties
      plugin authors 정보를 구성 합니다.
      elasticsearch reference) https://www.elastic.co/guide/en/elasticsearch/plugins/5.5/plugin-authors.html
  • ${CUSTOM-ANALYZER-NAME}AnalyzerProvider
      custom analyzer 생성자 제공을 위한 코드를 작성 합니다.
  • ${CUSTOM-ANALYZER-NAME}TokenFilterFactory
      custom filter 생성자 제공을 위한 코드를 작성 합니다.
  • ${CUSTOM-ANALYZER-NAME}TokenizerFactory
      custom tokenizer 생성자 제공을 위한 코드를 작성 합니다.
  • Analysis${CUSTOM-ANALYZER-NAME}Plugin
      custom analyzer plugin 등록을 위한 코드를 작성 합니다.

이와 같은 구조를 이용하여 elasticsearch-analysis-arirang plugin을 만들어 보도록 하겠습니다.

본 plugin에서는 arirang에서 제공하는 dynamic dictionary reload 기능을 사용하기 위한 Rest Handler도 추가해서 만들어 보도록 하겠습니다.


소스코드 참고)

https://github.com/HowookJeong/elasticsearch-analysis-arirang/tree/5.5.0


Step1)

Step2)

  • Plugin project structure를 구성 합니다.

Step3)

  • root path에 lib 폴더를 생성하고 arirang analyzer 관련 jar 파일을 복사해 놓습니다.
  • arirang.lucene-analyzer-VERSION.jar
  • arirang-morph-VERSION.jar

Step4)

  • pom.xml에서 local jar 파일에 대한 dependency 설정을 추가해 줍니다.

    <dependency>

      <groupId>com.argo</groupId>

      <artifactId>morph</artifactId>

      <version>${morph.version}</version>

      <scope>system</scope>

      <systemPath>${project.basedir}/lib/arirang-morph-${morph.version}.jar</systemPath>

      <optional>false</optional>

    </dependency>


    <dependency>

      <groupId>com.argo</groupId>

      <artifactId>arirang.lucene-analyzer-${lucene.version}</artifactId>

      <version>${morph.version}</version>

      <scope>system</scope>

      <systemPath>${project.basedir}/lib/arirang.lucene-analyzer-${lucene.version}-${morph.version}.jar</systemPath>

      <optional>false</optional>

    </dependency>


Step5)

  • analysis plugin 관련 코드를 작성 합니다.

    @Override

    public List<RestHandler> getRestHandlers(Settings settings, RestController restController, ClusterSettings clusterSettings,

      IndexScopedSettings indexScopedSettings, SettingsFilter settingsFilter, IndexNameExpressionResolver indexNameExpressionResolver,

      Supplier<DiscoveryNodes> nodesInCluster) {

        return singletonList(new ArirangAnalyzerRestAction(settings, restController));

    }


    @Override

    public Map<String, AnalysisProvider<TokenFilterFactory>> getTokenFilters() {

        return singletonMap("arirang_filter", ArirangTokenFilterFactory::new);

    }


    @Override

    public Map<String, AnalysisProvider<TokenizerFactory>> getTokenizers() {

        Map<String, AnalysisProvider<TokenizerFactory>> extra = new HashMap<>();

        extra.put("arirang_tokenizer", ArirangTokenizerFactory::new);


        return extra;

    }


    @Override

    public Map<String, AnalysisProvider<AnalyzerProvider<? extends Analyzer>>> getAnalyzers() {

        return singletonMap("arirang_analyzer", ArirangAnalyzerProvider::new);

    }


Step6)

  • analysis 관련 코드를 작성 합니다.

    // ArirangAnalyzerProvider

    private final KoreanAnalyzer analyzer;


    public ArirangAnalyzerProvider(IndexSettings indexSettings, Environment env, String name, Settings settings) throws IOException {

        super(indexSettings, name, settings);


        analyzer = new KoreanAnalyzer();

    }


    @Override

    public KoreanAnalyzer get() {

        return this.analyzer;

    }


    // ArirangTokenFilterFactory

    public ArirangTokenFilterFactory(IndexSettings indexSettings, Environment env, String name, Settings settings) {

        super(indexSettings, name, settings);

    }


    @Override

    public TokenStream create(TokenStream tokenStream) {

        return new KoreanFilter(tokenStream);

    }


    // ArirangTokenizerFactory

    public ArirangTokenizerFactory(IndexSettings indexSettings, Environment env, String name, Settings settings) {

        super(indexSettings, name, settings);

    }


    @Override

    public Tokenizer create() {

        return new KoreanTokenizer();

    }


Step7)

  • rest action 관련 코드를 작성 합니다.

  // ArirangAnalyzerRestAction

  @Inject

  public ArirangAnalyzerRestAction(Settings settings, RestController controller) {

    super(settings);


    controller.registerHandler(RestRequest.Method.GET, "/_arirang_dictionary_reload", this);

  }


  @Override

  protected RestChannelConsumer prepareRequest(RestRequest restRequest, NodeClient client) throws IOException {

    try {

      DictionaryUtil.loadDictionary();

    } catch (MorphException me) {

      return channel -> channel.sendResponse(new BytesRestResponse(RestStatus.NOT_ACCEPTABLE, "Failed which reload arirang analyzer dictionary!!"));

    } finally {

    }


    return channel -> channel.sendResponse(new BytesRestResponse(RestStatus.OK, "Reloaded arirang analyzer dictionary!!"));

  }


  // ArirangAnalyzerRestModule

  @Override

  protected void configure() {

    // TODO Auto-generated method stub

    bind(ArirangAnalyzerRestAction.class).asEagerSingleton();

  }


Step8)

  • plugin-descriptor.properties 관련 코드를 작성 합니다.

  classname=org.elasticsearch.plugin.analysis.arirang.AnalysisArirangPlugin

  name=analysis-arirang

  jvm=true

  java.version=1.8

  site=false

  isolated=true

  description=Arirang plugin

  version=${project.version}

  elasticsearch.version=${elasticsearch.version}

  hash=${buildNumber}

  timestamp=${timestamp}


Step9)

  • 패키징을 하기 위한 plugin.xml 관련 코드를 작성 합니다.

    <file>

      <source>lib/arirang.lucene-analyzer-6.5.1-1.1.0.jar</source>

      <outputDirectory>elasticsearch</outputDirectory>

    </file>

    <file>

      <source>lib/arirang-morph-1.1.0.jar</source>

      <outputDirectory>elasticsearch</outputDirectory>

    </file>

    <file>

      <source>target/elasticsearch-analysis-arirang-5.5.0.jar</source>

      <outputDirectory>elasticsearch</outputDirectory>

    </file>

    <file>

      <source>${basedir}/src/main/resources/plugin-descriptor.properties</source>

      <outputDirectory>elasticsearch</outputDirectory>

      <filtered>true</filtered>

    </file>


Step10)

  • 빌드를 합니다.

  $ mvn clean package -DskipTests=true


여기서는 작성된 코드는 일부만 발췌 했기 때문에 github에 올라간 소스코드를 참고하시기 바랍니다.

또한, 위 단계는 순서가 중요한 것이 아니며 구성과 어떻게 구현을 해야 하는지를 이해 하시는게 중요 합니다.


이제 빌드가 완료 되었으니 설치 및 기능 점검을 수행해 보도록 하겠습니다.


1. 설치

$ bin/elasticsearch-plugin install --verbose file:///path/elasticsearch-analysis-arirang-5.5.0.zip


2. 기능점검

  • 실행

$ bin/elasticsearch

[2017-08-22T18:56:17,223][INFO ][o.e.n.Node               ] [singlenode] initializing ...

[2017-08-22T18:56:17,289][INFO ][o.e.e.NodeEnvironment    ] [singlenode] using [1] data paths, mounts [[/ (/dev/disk1)]], net usable_space [489.3gb], net total_space [930.3gb], spins? [unknown], types [hfs]

[2017-08-22T18:56:17,289][INFO ][o.e.e.NodeEnvironment    ] [singlenode] heap size [1.9gb], compressed ordinary object pointers [true]

[2017-08-22T18:56:17,309][INFO ][o.e.n.Node               ] [singlenode] node name [singlenode], node ID [saCA_25vSxyUwF-RagteLw]

[2017-08-22T18:56:17,309][INFO ][o.e.n.Node               ] [singlenode] version[5.5.0], pid[12613], build[260387d/2017-06-30T23:16:05.735Z], OS[Mac OS X/10.12.5/x86_64], JVM[Oracle Corporation/Java HotSpot(TM) 64-Bit Server VM/1.8.0_72/25.72-b15]

[2017-08-22T18:56:17,309][INFO ][o.e.n.Node               ] [singlenode] JVM arguments [-Xms2g, -Xmx2g, -XX:+UseConcMarkSweepGC, -XX:CMSInitiatingOccupancyFraction=75, -XX:+UseCMSInitiatingOccupancyOnly, -XX:+DisableExplicitGC, -XX:+AlwaysPreTouch, -Xss1m, -Djava.awt.headless=true, -Dfile.encoding=UTF-8, -Djna.nosys=true, -Djdk.io.permissionsUseCanonicalPath=true, -Dio.netty.noUnsafe=true, -Dio.netty.noKeySetOptimization=true, -Dio.netty.recycler.maxCapacityPerThread=0, -Dlog4j.shutdownHookEnabled=false, -Dlog4j2.disable.jmx=true, -Dlog4j.skipJansi=true, -XX:+HeapDumpOnOutOfMemoryError, -Des.path.home=/Users/jeonghoug/dev/server/elastic/elasticsearch-5.5.0]

[2017-08-22T18:56:18,131][INFO ][o.e.p.PluginsService     ] [singlenode] loaded module [aggs-matrix-stats]

[2017-08-22T18:56:18,131][INFO ][o.e.p.PluginsService     ] [singlenode] loaded module [ingest-common]

[2017-08-22T18:56:18,131][INFO ][o.e.p.PluginsService     ] [singlenode] loaded module [lang-expression]

[2017-08-22T18:56:18,131][INFO ][o.e.p.PluginsService     ] [singlenode] loaded module [lang-groovy]

[2017-08-22T18:56:18,131][INFO ][o.e.p.PluginsService     ] [singlenode] loaded module [lang-mustache]

[2017-08-22T18:56:18,131][INFO ][o.e.p.PluginsService     ] [singlenode] loaded module [lang-painless]

[2017-08-22T18:56:18,131][INFO ][o.e.p.PluginsService     ] [singlenode] loaded module [parent-join]

[2017-08-22T18:56:18,131][INFO ][o.e.p.PluginsService     ] [singlenode] loaded module [percolator]

[2017-08-22T18:56:18,131][INFO ][o.e.p.PluginsService     ] [singlenode] loaded module [reindex]

[2017-08-22T18:56:18,132][INFO ][o.e.p.PluginsService     ] [singlenode] loaded module [transport-netty3]

[2017-08-22T18:56:18,132][INFO ][o.e.p.PluginsService     ] [singlenode] loaded module [transport-netty4]

[2017-08-22T18:56:18,132][INFO ][o.e.p.PluginsService     ] [singlenode] loaded plugin [analysis-arirang]

[2017-08-22T18:56:19,195][INFO ][o.e.d.DiscoveryModule    ] [singlenode] using discovery type [zen]

[2017-08-22T18:56:19,686][INFO ][o.e.n.Node               ] [singlenode] initialized

[2017-08-22T18:56:19,687][INFO ][o.e.n.Node               ] [singlenode] starting ...

[2017-08-22T18:56:24,837][INFO ][o.e.t.TransportService   ] [singlenode] publish_address {127.0.0.1:9300}, bound_addresses {[fe80::1]:9300}, {[::1]:9300}, {127.0.0.1:9300}

[2017-08-22T18:56:27,899][INFO ][o.e.c.s.ClusterService   ] [singlenode] new_master {singlenode}{saCA_25vSxyUwF-RagteLw}{_fn1si8zTT6bkZK1q6ilxQ}{127.0.0.1}{127.0.0.1:9300}, reason: zen-disco-elected-as-master ([0] nodes joined)

[2017-08-22T18:56:27,928][INFO ][o.e.h.n.Netty4HttpServerTransport] [singlenode] publish_address {127.0.0.1:9200}, bound_addresses {[fe80::1]:9200}, {[::1]:9200}, {127.0.0.1:9200}

[2017-08-22T18:56:27,928][INFO ][o.e.n.Node               ] [singlenode] started


  • 형태소분석기 확인

http://localhost:9200/_analyze?pretty&analyzer=arirang_analyzer&text=한국 엘라스틱서치 사용자 그룹의 HENRY 입니다.


  • 형태소분석기 결과 확인

{

  "tokens" : [

    {

      "token" : "한국",

      "start_offset" : 0,

      "end_offset" : 2,

      "type" : "korean",

      "position" : 0

    },

    {

      "token" : "엘라스틱서치",

      "start_offset" : 3,

      "end_offset" : 9,

      "type" : "korean",

      "position" : 1

    },

    {

      "token" : "엘라",

      "start_offset" : 3,

      "end_offset" : 5,

      "type" : "korean",

      "position" : 1

    },

    {

      "token" : "스틱",

      "start_offset" : 5,

      "end_offset" : 7,

      "type" : "korean",

      "position" : 2

    },

    {

      "token" : "서치",

      "start_offset" : 7,

      "end_offset" : 9,

      "type" : "korean",

      "position" : 3

    },

    {

      "token" : "사용자",

      "start_offset" : 10,

      "end_offset" : 13,

      "type" : "korean",

      "position" : 4

    },

    {

      "token" : "그룹",

      "start_offset" : 14,

      "end_offset" : 16,

      "type" : "korean",

      "position" : 5

    },

    {

      "token" : "henry",

      "start_offset" : 18,

      "end_offset" : 23,

      "type" : "word",

      "position" : 6

    },

    {

      "token" : "입니다",

      "start_offset" : 24,

      "end_offset" : 27,

      "type" : "korean",

      "position" : 7

    }

  ]

}


  • 형태소분석기 RESTful endpoint 실행 및 결과

  실행)

    http://localhost:9200/_arirang_dictionary_reload


  결과)

    Reloaded arirang analyzer dictionary!!


이제 기본적인 arirang analyzer와 elasticsearch용 plugin 까지 살펴 보았습니다.

마지막으로 arirang analyzer의 사전 데이터 수정과 반영을 살펴 보겠습니다.


 arirang 에서 제공하는 기본 dictionary path 변경을 하지 않고 사전 내용만 변경 하는 것으로 하겠습니다.


1. 사전 파일에 대한 classpath 설정

  • elasticsearch 실행 시 사전 파일에 대한 classpath 등록이 되어 있어야 정상적으로 로딩이 됩니다.
  • elasticsearch.in.sh 파일을 수정해 줍니다.

  ES_CLASSPATH="$ES_HOME/lib/elasticsearch-5.5.0.jar:$ES_HOME/lib/*:$ES_CONF_PATH/dictionary"


  예) 위에서 언급한 사전 관련 path와 파일들이 존재해야 합니다.

    config/dictionary/org/apache/lucene/analysis/ko

    config/dictionary/org/apache/lucene/analysis/ko/dic

  • ES_CONF_PATH는 기본 path.conf 정보와 동일해야 합니다.


2. 사전 정보 수정 및 반영

  • 1번 path에 위치한 사전 파일을 수정합니다.


3. 사전 reload

  • elasticsearch restart 없이 /_arirang_dictionary_reload API를 호출하여 반영 합니다.


여기까지 오셨으면 이제 arirang analyzerelasticseearch-analysis-arirang plugin 그리고 dictionary에 대한 기본 활용을 하실수 있게 되셨다고 생각합니다.

기술된 모든 정보는 모두 오픈소스이기 때문에 출처를 정확히 명시해 주시고 언제든지 오류와 개선에 대해서는 적극적인 참여 부탁 드립니다.


참고 사이트)

http://cafe.naver.com/korlucene

https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html

https://www.elastic.co/guide/en/elasticsearch/plugins/current/index.html

:

[Elasticsearch] Snapshot and Restore 알아보기

Elastic/Elasticsearch 2017. 8. 3. 11:23

Elasticsearch 에서 제공하는 Snapshot과 Restore 기능에 대해서 정리합니다.


Snapshot과 Restore 기능에 대한 정의와 설명은 아래 본문에 잘 나와 있습니다.


[원본 문서]

https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-snapshots.html

https://www.elastic.co/guide/en/elasticsearch/guide/current/backing-up-your-cluster.html


※ 각 API의 상세 옵션과 설명은 원본 문서 참고 하시면 됩니다.


[발췌]

The snapshot and restore module allows to create snapshots of individual indices or an entire cluster into a remote repository like shared file system, S3, or HDFS. These snapshots are great for backups because they can be restored relatively quickly but they are not archival because they can only be restored to versions of Elasticsearch that can read the index. That means that:


  • A snapshot of an index created in 2.x can be restored to 5.x.
  • A snapshot of an index created in 1.x can be restored to 2.x.
  • A snapshot of an index created in 1.x can not be restored to 5.x.

글에서도 보시는 것 처럼 snapshot type 은 아래와 같이 지원 합니다.
  • fs
  • s3
  • hdfs
  • gcs

각 type을 지정하고 사용하기 위해서는 아래와 같은 추가 작업이 필요 합니다.


fs)

클러스터 내 모든 노드에 path.repo 설정 후 재시작 합니다.

- elasticsearch.yml

반드시 shared file system 적용을 해주셔야 합니다. 


s3)

클러스터 내 모든 노드에 plugin 설치를 해주셔야 합니다.

aws의 s3를 사용하기 위해서는 repository plugin 을 설치해야 합니다.


2.x)

bin/plugin install cloud-aws


5.x)

bin/elasticsearch-pluin install repository-s3


hdfs)

https://github.com/elastic/elasticsearch-hadoop/tree/master/repository-hdfs


클러스터 내 모든 노드에 plugin 설치를 해주셔야 합니다.

hadoop file system을 사용하기 위해서는 repository plugin 을 설치해야 합니다.


2.x)

bin/plugin install elasticsearch-repository-hdfs


5.x)

bin/elasticsearch-pluin install repository-hdfs


gcs)

클러스터 내 모든 노드에 plugin 설치를 해주셔야 합니다.

google cloud storage를 사용하기 위해서는 repository plugin 을 설치해야 합니다.


5.x)

bin/elasticsearch-pluin install repository-gcs


azure)

https://github.com/elastic/elasticsearch-cloud-azure


클러스터 내 모든 노드에 plugin 설치를 해주셔야 합니다.

azure repository를 사용하기 위해서는 repository plugin 을 설치해야 합니다.


2.x)

bin/plugin install elasticsearch/elasticsearch-cloud-azure


5.x)

bin/elasticsearch-pluin install repository-azure


이 중 2.X 클러스터에서 제가 추천 하는 것은 fs, s3 입니다.

이유는 다른건 제가 경험이 없어서 좋은지 나쁜지는 모릅니다.


fs, s3 에 대한 예제를 가지고 snapshot 과정과 restore 과정을 살펴 보겠습니다.


[fs]

step 1)

snapshot 기능을 수행 하기 위한 snapshot 저장소를 생성 합니다.


$ curl -XPUT 'localhost:9200/_snapshot/fs_snapshot?pretty' -d '{

    "type": "fs",

    "settings": {

        "location": "/mount/snapshot",

        "compress": true

    }

}'


step 2)

snapshot 할 대상을 선정하고 실행 합니다.


$ curl -XPUT 'localhost:9200/_snapshot/fs_snapshot/logstash_20170803?pretty' -d '

{

  "indices": "logstash-web-20170803,logstash-app-20170803",

  "ignore_unavailable": true,

  "include_global_state": false

}'


step 3)

이제 restore 해보겠습니다.


$ curl -XPOST 'localhost:9200/_snapshot/fs_snapshot/logstash_20170803/_restore'


[s3]

step 1)

snapshot 기능을 수행 하기 위한 snapshot 저장소를 생성 합니다.


$ curl -XPUT 'localhost:9200/_snapshot/s3_snapshot?pretty' -d '{

    "type": "s3",

    "settings": {

        "bucket": "s3-bucket",

        "region": "ap-northeast-2"

    }

}'


step 2)

snapshot 할 대상을 선정하고 실행 합니다.


$ curl -XPUT 'localhost:9200/_snapshot/s3_snapshot/logstash_20170803?pretty' -d '

{

  "indices": "logstash-web-20170803,logstash-app-20170803",

  "ignore_unavailable": true,

  "include_global_state": false

}'


step 3)

이제 restore 해보겠습니다.


$ curl -XPOST 'localhost:9200/_snapshot/s3_snapshot/logstash_20170803/_restore'


하나의 클러스터 내 여러개의 snapshot repository 를 등록해서 사용 하셔도 되기 때문에 목적에 맞게 사용 하시면 좋을 것 같습니다.


이 과정들은 모두 background 로 동작 하기 때문에 실행 후 바로 acknowledged/accepted 가 전달 됩니다.

그렇기 때문에 실행한 snapshot 의 상태 점검을 하셔야 합니다.


$ curl -XGET 'localhost:9200/_snapshot/s3_snapshot/logstash_20170803/_status'


추가적으로 사용하시면서 약간의 주의점 공유 드립니다.

- 더이상 색인 작업이 발생 하지 않는 index 들에 대해서 snapshot 작업을 수행하시면 좋습니다.

- restore 시는 대상 index 가 없거나 close 되어 있어야 하기 때문에 지속적인 색인 작업이 발생 하는 index에 대한 snapshot은 추천 하지 않지만, 아직 필요한 경우를 찾지는 못했습니다.


:

[Elasticsearch] Multi Search API 사용

Elastic/Elasticsearch 2017. 7. 17. 12:48

2.X 랑 5.X 랑 크게 바뀐 부분은 없습니다.

다만 5.X 에서는 template 지원도 함께 됩니다.


참고문서)

https://www.elastic.co/guide/en/elasticsearch/reference/current/search-multi-search.html


Multi Search API를 얼마나 많은 분들이 사용하고 계신지는 잘 모르겠습니다.

이 API 를 한 줄로 정의 하면 "통합 검색 API" 라고 할 수 있습니다.

뭐 동의 하지 않으시는 분들이 계시다면 어쩔수 없구요.


설명을 풀어서 하면, 서로 다른 query 를 하나의 index 에 질의 하거나 하나의 query 를 서로 다른 index 로 질의 할 때 사용 하시면 유용 합니다.


Case 1)

Single Query + Multi Index


Case 2)

Multi Query + Single Index


Case 3)

Multi Query + Multi Index


위 참고문서 에서는 Request 에 대한 내용은 나와 있지만 실제 Response 에 대한 예시는 나와 있지 않아 실제 실행 보지 않고서는 어떻게 결과가 나올지 모르실 수도 있습니다.

(사실 상상은 되실거예요.)


아래는 제가 테스트로 하나의 클러스터에 "Case 3" 으로 실행한 결과 입니다.


Request API)

Endpoint : http://xxxx/_msearch

Method : POST (raw)


Request Query)

{"index":"service_product"}

{"query":{"match_all":{}},"from":0,"size":1}

{"index":"service_item"}

{"query":{"term":{"title":{"value":"틴트"}}},"from":0,"size":1}


Response Data)

{

    "responses": [

        {

            "took": 1,

            "timed_out": false,

            "_shards": {

                "total": 1,

                "successful": 1,

                "failed": 0

            },

            "hits": {

                "total": xxxxxx,

                "max_score": 1,

                "hits": [

                    {

                        "_index": "service_product_201707171215",

                        "_type": "deal",

                        "_id": "7510",

                        "_score": 1,

                        "_source": {

                        ... 생략 ...

                        }

                    }

                ]

            }

        },

        {

            "took": 1,

            "timed_out": false,

            "_shards": {

                "total": 1,

                "successful": 1,

                "failed": 0

            },

            "hits": {

                "total": xxxxxx,

                "max_score": 7.0183215,

                "hits": [

                    {

                        "_index": "service_item_201707171215",

                        "_type": "item",

                        "_id": "1170617",

                        "_score": 7.0183215,

                        "_source": {

                            ... 중략 ...

                            "title": "라네즈 투톤틴트바 No.3 2g 틴트민트",

                            ... 중략 ...

                        }

                    }

                ]

            }

        }

    ]

}


결과적으로 보면, 검색엔진에서 해당 연산과 실행을 하실지 아니면 별도 API Gateway 같은 WAS 에서 연산과 실행을 하실지에 대한 문제로 트래픽과 용량을 잘 산정 하셔서 사용하시면 매우 유용하리라 생각 합니다.

(너무 오래 전에 비슷한 내용을 올린 것 같아 5.5 릴리즈 기념으로 한번 더 복습해 봤습니다.)


:

[Elasticsearch] post_filter

Elastic/Elasticsearch 2017. 7. 13. 15:08

§ Elasticsearch Reference 5.5


https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-post-filter.html


한 줄 요약)

모든 action 완료 후 결과 내 재검색 수행 후 결과를 리턴 합니다.


이전 글 참고)

[Elasticsearch] aggregations 사용 시 filter 의 차이점.


: