'lucene'에 해당되는 글 71건

  1. 2021.08.04 [Elasticsearch] force merge 에 대한 정리
  2. 2019.08.28 [Elasticsearch] Node Topology 7.x
  3. 2019.08.07 [Elasticsearch] elasticsearch-analysis-arirang 7.3.0 출시(?)
  4. 2019.01.15 [Elasticsearch] 문서 모델링 펼치기
  5. 2018.10.04 [Elasticsearch] _analyze 예제 - 특수문자 제거
  6. 2018.05.09 [Elasticsearch] Adaptive Replica Selection 기능
  7. 2018.05.09 [Elasticsearch] GET API 에 대해서
  8. 2018.04.24 [Elasticsearch] Synonym filter position 문제 개선
  9. 2018.04.19 [Elasticsearch] Synonym filter 테스트
  10. 2018.04.18 [Elasticsearch] elasticsearch-docker plugin 설치는 어떻게?

[Elasticsearch] force merge 에 대한 정리

Elastic/Elasticsearch 2021. 8. 4. 08:12

공식 문서 참고)

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

 

API)

POST /my-index-000001/_forcemerge

 

force merge 는 너무 많이 생성된 Segments 파일을 병합 하거나 삭제 된 문서를 물리적으로 병합 하면서 제거 하거나 하는 작업을 수행 하게 됩니다.

 

보통은 merge policy 에 의해서 자동으로 수행이 되긴 하지만 간혹 수동으로 실행을 해야 할 때도 발생을 합니다.

그럼 Segments 파일을 어느 정도 수준으로 유지 하는게 좋을까요?

 

그 전에 확인할 사항이 있습니다.

 

1. Segment 파일에 문서 수가 많나요?

2. Segment 파일에 문서의 크기가 큰 것일까요?

 

결국 이 Segments 파일은 IndexWriter 즉, 색인 보다는 검색에 영향을 많이 주는 요소라고 보면 됩니다.

 

짧은 용량의 Segments 파일이 많이 있다고 가정 하면 파일 수 만큼의 IndexeReader 가 준비 되어야 하고 그에 맞는 thread 가 일을 해야 하기 때문에 다소 성능이 떨어 질 수도 있습니다.

반대로, 용량이 큰 Segments 파일이 있다고 하면 Single thread 로 동작 하기 때문에 역시 성능이 떨어 질 수 있기도 합니다.

 

그래서 가장 최적의 Segment file 의 크기와 수를 구해야 검색 성능을 확보 할 수 있습니다.

보통은 Primary shard 와 Replica shard 를 가지고 접근을 하게 됩니다. 그러다 Node 수준까지 접근 하게 되고 더 나아가 Application 수준까지 ...

 

Segment 파일이 가질 수 있는

- 최대 문서의 수는 Integer.MAX_VALUE (2,147,483,519) 만큼 가질 수 있습니다.

- 문서 하나의 최대 크기는 2GB 입니다.

 

제가 제안 하는 범용적인 Segment 파일은

- 최대 문서 수는 1천만 ~ 5천만

- Segment file 크기는 2GB ~ 5GB

- Segment file 수는 Core 크기와 같거나 1/2 만큼

정도 인것 같습니다.

 

Segment 는 Lucene 기준으로 검토를 하셔야 하고 Elasticsearch 기준으로 확장 한다고 하면 Shard 까지 검토를 하셔야 합니다.

 

제가 제안한 정보를 기준으로 단순 예를 들어 보면)

- 코어가 10개 라고 하고

- 1/2 만큼의 Segments file : 5개

- Shard 1개의 크기는 5 개 Segments file x 2GB : 10GB

 

대략 Node와 Shard size estimation 하는 방법에서 제시 한 것과 어느 정도 부합 한다는 것을 볼 수 있습니다.

 

:

[Elasticsearch] Node Topology 7.x

Elastic/Elasticsearch 2019. 8. 28. 10:54

사용성에서 정확한 정의가 없어서 많이들 헷갈려 하셨던 Node 가 정리가 된 것 같아 기록해 봅니다.

 

원문 링크)

https://www.elastic.co/guide/en/elasticsearch/reference/7.3/modules-node.html

 

Master Eligible Node

- node.master

마스터 노드는 전용으로 구성 하는 것을 추천 하며, 최소한의 작업을 수행 하도록 하는 것이 좋습니다.

 

Data Node

- node.data

CPU-, Memory-, I/O 성능 영향을 많이 받기 때문에 좋은 장비로 구성 하시길 추천 드립니다.

또한 Network 사용량에 대한 고려도 해야 합니다.

 

Ingest Node

- node.ingest

 

Machine Learning Node (x-pack)

- node.ml

 

Coordinating Node

node.master: false

node.data: false

node.ingest: false

node.ml: false

이 노드의 수를 너무 많이 늘리지 않도록 주의 하는게 좋습니다.

이유는 마스터 노드가 선출 되었을 때 모든 노드의 승인을 기다리게 되어 오히려 성능적으로 손해를 볼 수도 있습니다.

 

Voting Only Node (x-pack)

- node.voting_only

:

[Elasticsearch] elasticsearch-analysis-arirang 7.3.0 출시(?)

Elastic/Elasticsearch 2019. 8. 7. 10:26

Elasticsearch 7.3.0 출시가 되어 아리랑 플러그인도 버전을 올려봤습니다.

 

https://github.com/HowookJeong/elasticsearch-analysis-arirang/releases/tag/7.3.0

 

HowookJeong/elasticsearch-analysis-arirang

korean analyzer (lucene analyzer kr arirang). Contribute to HowookJeong/elasticsearch-analysis-arirang development by creating an account on GitHub.

github.com

 

설치 방법은 아래와 같습니다.

 

로컬 빌드 후 파일로 설치)

elasticsearch-7.3.0$ bin/elasticsearch-plugin install file:///git/elasticsearch-analysis-arirang/target/elasticsearch-analysis-arirang-7.3.0.zip

불러오는 중입니다...

 

github 에 올라간 release 파일로 설치)

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

불러오는 중입니다...

 

:

[Elasticsearch] 문서 모델링 펼치기

Elastic/Elasticsearch 2019. 1. 15. 19:13

상품 모델링을 하다 보니 부득이 flat 하게 펼쳐야 하는 이슈가 생겼습니다.

field 수만 수천개 ㅡ.ㅡ;

사실 C사에 있을 때 봤던 field 수가 1만개 이상 되었던 걸로 기억 하는데 어쩔 수 없이 저도 다르지만 비슷한 구조를 만들게 되었내요.


[원문]

https://www.elastic.co/guide/en/elasticsearch/reference/master/mapping.html#mapping-limit-settings


Elasticsearch 에서 기본 제약 사항은 아래와 같습니다.


index.mapping.total_fields.limit

The maximum number of fields in an index. Field and object mappings, as well as field aliases count towards this limit. The default value is 1000.


index.mapping.depth.limit

The maximum depth for a field, which is measured as the number of inner objects. For instance, if all fields are defined at the root object level, then the depth is 1. If there is one object mapping, then the depth is 2, etc. The default is 20.


index.mapping.nested_fields.limit

The maximum number of nested fields in an index, defaults to 50. Indexing 1 document with 100 nested fields actually indexes 101 documents as each nested document is indexed as a separate hidden document.


index.mapping.nested_objects.limit

The maximum number of nested json objects within a single document across all nested fields, defaults to 10000. Indexing one document with an array of 100 objects within a nested field, will actually create 101 documents, as each nested object will be indexed as a separate hidden document.


혹시라도 문서 모델링 하시다가 field limit 에 대한 정보가 궁금 할 것 같아 일단 북마킹 합니다.

모델링을 flat 하게 펼치게 된 이유는 시간에 대한 다양한 요구사항이 발생을 하다 보니 부득이 이와 같이 펼치게 되었습니다.

:

[Elasticsearch] _analyze 예제 - 특수문자 제거

Elastic/Elasticsearch 2018. 10. 4. 08:02

색인 시점에 text 에 포함된 특수 문자를 제거 하기 위한 예시 입니다.


[실행]

curl -X POST \

  http://localhost:9200/_analyze \

  -H 'cache-control: no-cache' \

  -H 'content-type: application/json' \

  -d '{

  "tokenizer": "arirang_tokenizer",

  "filter":[

              "lowercase",

              "trim",

              "arirang_filter"

            ],

  "char_filter" : [{

          "type": "pattern_replace",

          "pattern": "\\p{Punct}|\\d",

          "replacement": " "

        }],

  "text": "애플(&<>,./^!@+=;:%)파이"

}'



[결과]

{

    "tokens": [

        {

            "token": "애플",

            "start_offset": 0,

            "end_offset": 2,

            "type": "korean",

            "position": 0

        },

        {

            "token": "파이",

            "start_offset": 18,

            "end_offset": 20,

            "type": "korean",

            "position": 1

        }

    ]

}


:

[Elasticsearch] Adaptive Replica Selection 기능

Elastic/Elasticsearch 2018. 5. 9. 17:06

6.x 에 추가된 API 중 맘에 드는 것이 있어서 글을 퍼왔습니다.


Reference)

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


Adaptive Replica Selection

As an alternative to requests being sent to copies of the data in a round robin fashion, you may enable adaptive replica selection. This allows the coordinating node to send the request to the copy deemed "best" based on a number of criteria:


  • Response time of past requests between the coordinating node and the node containing the copy of the data
  • Time past search requests took to execute on the node containing the data
  • The queue size of the search threadpool on the node containing the data

This can be turned on by changing the dynamic cluster setting cluster.routing.use_adaptive_replica_selection from false to true:


PUT /_cluster/settings

{

    "transient": {

        "cluster.routing.use_adaptive_replica_selection": true

    }

}



위에 기술 되어 있는 것 처럼,

- 기본적으로는 Round Robin 방식으로 동작 합니다.

- 하지만 3가지 기준을 가지고 이 기능은 동작을 합니다.

1. Coordinating node 와 Data node 간의 응답 시간

2. Data node 에서 수행된 시간

3. Data node 의 threadpool queue 크기


이 중에서 가장 좋은 기준을 바탕으로 동작 하게 되는 것입니다.


:

[Elasticsearch] GET API 에 대해서

Elastic/Elasticsearch 2018. 5. 9. 14:22

평소 Get API 에 대해서 설명을 할 때 document id 로 lookup 을 하기 때문에 모든 shard 로 request 를 보내지 않아 빠르다고 설명을 했습니다.

이 과정에서 놓치기 쉬운 부분이 있는데 기본적으로 Get API 는 realtime  동작을 합니다.

즉, refresh 동작과 상관 없이 기본적으로 최신 정보를 가져오게 되는 것입니다.


다시 말해, 내부적으로 refresh 동작을 수행한다는 의미가 됩니다.

그래서 Get API 에서는 realtime 이라는 변수를 통해서 realtime Get 을 사용 할 것인지 말 것인지를 정의 할 수 있습니다.


아래는 위 설명을 이해 하는데 도움을 주기 위해서 소스 코드의 일부를 발췌 하였습니다.


[GetRequest.java]

private boolean refresh = false;

boolean realtime = true;
/**
* Should a refresh be executed before this get operation causing the operation to
* return the latest value. Note, heavy get should not set this to {@code true}. Defaults
* to {@code false}.
*/
public GetRequest refresh(boolean refresh) {
this.refresh = refresh;
return this;
}

public boolean refresh() {
return this.refresh;
}

public boolean realtime() {
return this.realtime;
}

@Override
public GetRequest realtime(boolean realtime) {
this.realtime = realtime;
return this;
}


[RestGetAction.java]

@Override
public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException {
final boolean includeTypeName = request.paramAsBoolean("include_type_name", true);
final String type = request.param("type");
if (includeTypeName == false && MapperService.SINGLE_MAPPING_NAME.equals(type) == false) {
throw new IllegalArgumentException("You may only use the [include_type_name=false] option with the get APIs with the " +
"[{index}/_doc/{id}] endpoint.");
}
final GetRequest getRequest = new GetRequest(request.param("index"), type, request.param("id"));
getRequest.refresh(request.paramAsBoolean("refresh", getRequest.refresh()));
getRequest.routing(request.param("routing"));
getRequest.preference(request.param("preference"));
getRequest.realtime(request.paramAsBoolean("realtime", getRequest.realtime()));
if (request.param("fields") != null) {
throw new IllegalArgumentException("the parameter [fields] is no longer supported, " +
"please use [stored_fields] to retrieve stored fields or [_source] to load the field from _source");
}
final String fieldsParam = request.param("stored_fields");
if (fieldsParam != null) {
final String[] fields = Strings.splitStringByCommaToArray(fieldsParam);
if (fields != null) {
getRequest.storedFields(fields);
}
}

getRequest.version(RestActions.parseVersion(request));
getRequest.versionType(VersionType.fromString(request.param("version_type"), getRequest.versionType()));

getRequest.fetchSourceContext(FetchSourceContext.parseFromRestRequest(request));

return channel -> client.get(getRequest, new RestToXContentListener<GetResponse>(channel) {
@Override
protected RestStatus getStatus(final GetResponse response) {
return response.isExists() ? OK : NOT_FOUND;
}
});
}


[TransportGetAction.java]

@Override
protected void asyncShardOperation(GetRequest request, ShardId shardId, ActionListener<GetResponse> listener) throws IOException {
IndexService indexService = indicesService.indexServiceSafe(shardId.getIndex());
IndexShard indexShard = indexService.getShard(shardId.id());
if (request.realtime()) { // we are not tied to a refresh cycle here anyway
listener.onResponse(shardOperation(request, shardId));
} else {
indexShard.awaitShardSearchActive(b -> {
try {
super.asyncShardOperation(request, shardId, listener);
} catch (Exception ex) {
listener.onFailure(ex);
}
});
}
}

@Override
protected GetResponse shardOperation(GetRequest request, ShardId shardId) {
IndexService indexService = indicesService.indexServiceSafe(shardId.getIndex());
IndexShard indexShard = indexService.getShard(shardId.id());

if (request.refresh() && !request.realtime()) {
indexShard.refresh("refresh_flag_get");
}

GetResult result = indexShard.getService().get(request.type(), request.id(), request.storedFields(),
request.realtime(), request.version(), request.versionType(), request.fetchSourceContext());
return new GetResponse(result);
}

보시면 아시겠지만 refresh 와 realtime 의 동작에는 차이가 있습니다.

이해 하기 쉽게 정리 하면,

- refresh 는 get 수행 전에 relevant shard 를 대상으로 refresh 동작을 먼저 하기 때문에 성능 저하가 있을 수 있습니다.

- realtime 은 수행 시점에 refresh 동작을 수행 하게 됩니다. (이 경우 refresh searcher 의 대상은 internal searcher 가 됩니다.)

- 여기서 translog 에서 데이터를 읽어야 하는 경우가 있는데 이 경우는 update API 에서 사용 되게 됩니다.


[InternalEngine.java]

@Override
public GetResult get(Get get, BiFunction<String, SearcherScope, Searcher> searcherFactory) throws EngineException {
assert Objects.equals(get.uid().field(), IdFieldMapper.NAME) : get.uid().field();
try (ReleasableLock ignored = readLock.acquire()) {
ensureOpen();
SearcherScope scope;
if (get.realtime()) {
VersionValue versionValue = null;
try (Releasable ignore = versionMap.acquireLock(get.uid().bytes())) {
// we need to lock here to access the version map to do this truly in RT
versionValue = getVersionFromMap(get.uid().bytes());
}
if (versionValue != null) {
if (versionValue.isDelete()) {
return GetResult.NOT_EXISTS;
}
if (get.versionType().isVersionConflictForReads(versionValue.version, get.version())) {
throw new VersionConflictEngineException(shardId, get.type(), get.id(),
get.versionType().explainConflictForReads(versionValue.version, get.version()));
}
if (get.isReadFromTranslog()) {
// this is only used for updates - API _GET calls will always read form a reader for consistency
// the update call doesn't need the consistency since it's source only + _parent but parent can go away in 7.0
if (versionValue.getLocation() != null) {
try {
Translog.Operation operation = translog.readOperation(versionValue.getLocation());
if (operation != null) {
// in the case of a already pruned translog generation we might get null here - yet very unlikely
TranslogLeafReader reader = new TranslogLeafReader((Translog.Index) operation, engineConfig
.getIndexSettings().getIndexVersionCreated());
return new GetResult(new Searcher("realtime_get", new IndexSearcher(reader)),
new VersionsAndSeqNoResolver.DocIdAndVersion(0, ((Translog.Index) operation).version(), reader, 0));
}
} catch (IOException e) {
maybeFailEngine("realtime_get", e); // lets check if the translog has failed with a tragic event
throw new EngineException(shardId, "failed to read operation from translog", e);
}
} else {
trackTranslogLocation.set(true);
}
}
refresh("realtime_get", SearcherScope.INTERNAL);
}
scope = SearcherScope.INTERNAL;
} else {
// we expose what has been externally expose in a point in time snapshot via an explicit refresh
scope = SearcherScope.EXTERNAL;
}

// no version, get the version from the index, we know that we refresh on flush
return getFromSearcher(get, searcherFactory, scope);
}
}


[코드의 이해]

RestGetAction -> TransportGetAction (TransportSingleShardAction) -> IndexShard ->

ShardGetService -> InternalEngine (Engine) -> SearcherManager(ReferenceManager)


:

[Elasticsearch] Synonym filter position 문제 개선

Elastic/Elasticsearch 2018. 4. 24. 15:52

이미 synonym filter 테스트 관련 글을 공유 했었습니다.

- [Elasticsearch] Synonym filter 테스트


이 테스트에서 발생한 문제는 동의어에 대한 position 정보가 잘 못되는 것입니다.

테스트 환경은 기본 Elasticsearch 6.x 에서 진행 되었습니다. 


아래는 SynonymFilter 코드 내 주석 입니다.


[SynonymFilter.java]

 Matches single or multi word synonyms in a token stream.

 This token stream cannot properly handle position

 increments != 1, ie, you should place this filter before

 filtering out stop words.


그리고 아래는 동의어 처리 시 문제가 발생 하는 부분의 코드 입니다.


[SynonymMap.java]

 /** Sugar: analyzes the text with the analyzer and

 *  separates by {@link SynonymMap#WORD_SEPARATOR}.

 *  reuse and its chars must not be null. */

public CharsRef analyze(String text, CharsRefBuilder reuse) throws IOException {

  try (TokenStream ts = analyzer.tokenStream("", text)) {

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

    PositionIncrementAttribute posIncAtt = ts.addAttribute(PositionIncrementAttribute.class);

    ts.reset();

    reuse.clear();

    while (ts.incrementToken()) {

      int length = termAtt.length();

      if (length == 0) {

        throw new IllegalArgumentException("term: " + text + " analyzed to a zero-length token");

      }

      if (posIncAtt.getPositionIncrement() != 1) {

        throw new IllegalArgumentException("term: " + text + " analyzed to a token (" + termAtt +

                                           ") with position increment != 1 (got: " + posIncAtt.getPositionIncrement() + ")");

      }

      reuse.grow(reuse.length() + length + 1); /* current + word + separator */

      int end = reuse.length();

      if (reuse.length() > 0) {

        reuse.setCharAt(end++, SynonymMap.WORD_SEPARATOR);

        reuse.setLength(reuse.length() + 1);

      }

      System.arraycopy(termAtt.buffer(), 0, reuse.chars(), end, length);

      reuse.setLength(reuse.length() + length);

    }

    ts.end();

  }

  if (reuse.length() == 0) {

    throw new IllegalArgumentException("term: " + text + " was completely eliminated by analyzer");

  }

  return reuse.get();

}


기본적으로 동의어 처리에 대한 문제는 이미 lucene 레벨에서 개선이 되었습니다.

관련 참고 링크는 아래와 같습니다.


[Reference links]

https://issues.apache.org/jira/browse/LUCENE-6664

http://blog.mikemccandless.com/2012/04/lucenes-tokenstreams-are-actually.html


이와 같이 개선된 synonym filter 를 elasticsearch 에서는 아래와 같이 사용 할 수 있습니다.


[Reference links]

https://www.elastic.co/guide/en/elasticsearch/reference/master/analysis-synonym-graph-tokenfilter.html



[Create index]

PUT /syngtest

{

  "settings": {

    "index.number_of_shards": 1,

    "index.number_of_replicas": 0,

    "index": {

      "analysis": {

        "analyzer": {

          "arirang_custom": {

            "tokenizer": "arirang_tokenizer",

            "filter": [

              "lowercase",

              "trim",

              "arirang_filter",

              "custom_synonym"

            ]

          }

        },

        "filter": {

          "custom_synonym": {

            "type": "synonym_graph",

            "synonyms": [

              "henry,헨리,앙리",

              "신해철,마왕"

            ]

          }

        }

      }

    }

  }

}


[Request analyze]

GET /syngtest/_analyze

{

  "tokenizer": "arirang_tokenizer",

  "filter": [

    "lowercase",

    "trim",

    "arirang_filter",

    "custom_synonym"

  ],

  "text": "신해철은 henry"

}


[Analyzed result]

{

  "tokens": [

    {

      "token": "마왕",

      "start_offset": 0,

      "end_offset": 3,

      "type": "SYNONYM",

      "position": 0

    },

    {

      "token": "신해철",

      "start_offset": 0,

      "end_offset": 3,

      "type": "korean",

      "position": 0

    },

    {

      "token": "헨리",

      "start_offset": 5,

      "end_offset": 10,

      "type": "SYNONYM",

      "position": 1

    },

    {

      "token": "앙리",

      "start_offset": 5,

      "end_offset": 10,

      "type": "SYNONYM",

      "position": 1

    },

    {

      "token": "henry",

      "start_offset": 5,

      "end_offset": 10,

      "type": "word",

      "position": 1

    }

  ]

}


특별히 코드를 수정 하거나 하지 않고 문제가 해결 된 것을 확인 하실 수 있습니다.

왜 해결 되었는지는 위 synonym graph filter 에 대해서 문서를 보시면 되겠습니다.


:

[Elasticsearch] Synonym filter 테스트

Elastic/Elasticsearch 2018. 4. 19. 16:19

커뮤니티에 질문 주신 내용이 있어서 바쁘지만 테스트 결과 공유 드립니다.

커뮤니티에 질문 올려 주신 분이 계셔서 직접 테스트 진행 했습니다.

제가 position 관련 에러 수정을 위한 테스트 시간이 없어서 인명 사전 등록 방법으로 처리 했는데요.

수정이 필요 하시면 KoreanFilter 코드를 수정 하시면 됩니다.


설치 및 테스트 Elasticsearch Version)

6.0.0


Arirang plugin 설치)

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


Index 삭제)

DELETE syntest


{

  "error": {

    "root_cause": [

      {

        "type": "index_not_found_exception",

        "reason": "no such index",

        "resource.type": "index_or_alias",

        "resource.id": "syntest",

        "index_uuid": "_na_",

        "index": "syntest"

      }

    ],

    "type": "index_not_found_exception",

    "reason": "no such index",

    "resource.type": "index_or_alias",

    "resource.id": "syntest",

    "index_uuid": "_na_",

    "index": "syntest"

  },

  "status": 404

}


Index 생성)

PUT /syntest

{

  "settings": {

    "index.number_of_shards": 1,

    "index.number_of_replicas": 0,

    "index": {

      "analysis": {

        "analyzer": {

          "arirang_custom": {

            "tokenizer": "arirang_tokenizer",

            "filter": [

              "lowercase",

              "trim",

              "custom_synonym",

              "arirang_filter"

            ]

          }

        },

        "filter": {

          "custom_synonym": {

            "type": "synonym",

            "synonyms": [

              "henry,헨리,앙리",

              "신해철,마왕"

            ]

          }

        }

      }

    }

  }

}


{

  "acknowledged": true,

  "shards_acknowledged": true,

  "index": "syntest"

}


Analyze 실행)

GET /syntest/_analyze

{

  "tokenizer": "arirang_tokenizer",

  "filter": [

    "lowercase",

    "trim",

    "custom_synonym",

    "arirang_filter"

  ],

  "text": "신해철"

}


{

  "tokens": [

    {

      "token": "신해철",

      "start_offset": 0,

      "end_offset": 3,

      "type": "korean",

      "position": 0

    },

    {

      "token": "신해",

      "start_offset": 0,

      "end_offset": 2,

      "type": "korean",

      "position": 0

    },

    {

      "token": "해철",

      "start_offset": 1,

      "end_offset": 3,

      "type": "korean",

      "position": 1

    },

    {

      "token": "마왕",

      "start_offset": 0,

      "end_offset": 3,

      "type": "SYNONYM",

      "position": 1

    }

  ]

}


동의어 처리가 되지 않은 이유)

GET /syntest/_analyze

{

  "tokenizer": "arirang_tokenizer",

  "text": "신해철은 henry"

}


{

  "tokens": [

    {

      "token": "신해철은",

      "start_offset": 0,

      "end_offset": 4,

      "type": "korean",

      "position": 0

    },

    {

      "token": "henry",

      "start_offset": 5,

      "end_offset": 10,

      "type": "word",

      "position": 1

    }

  ]

}


tokenizer 에서 추출한 토큰은 위와 같이 두개 입니다.

아래는 filter 를 적용한 내용입니다.


GET /syntest/_analyze

{

  "tokenizer": "arirang_tokenizer",

  "filter": [

    "lowercase",

    "trim",

    "custom_synonym",

    "arirang_filter"

  ],

  "text": "신해철은 henry"

}


{

  "tokens": [

    {

      "token": "신해철",

      "start_offset": 0,

      "end_offset": 3,

      "type": "korean",

      "position": 0

    },

    {

      "token": "신해철은",

      "start_offset": 0,

      "end_offset": 4,

      "type": "korean",

      "position": 0

    },

    {

      "token": "신해",

      "start_offset": 0,

      "end_offset": 2,

      "type": "korean",

      "position": 0

    },

    {

      "token": "해철",

      "start_offset": 1,

      "end_offset": 3,

      "type": "korean",

      "position": 1

    },

    {

      "token": "철은",

      "start_offset": 2,

      "end_offset": 4,

      "type": "korean",

      "position": 2

    },

    {

      "token": "henry",

      "start_offset": 5,

      "end_offset": 10,

      "type": "word",

      "position": 3

    },

    {

      "token": "헨리",

      "start_offset": 5,

      "end_offset": 10,

      "type": "SYNONYM",

      "position": 3

    },

    {

      "token": "앙리",

      "start_offset": 5,

      "end_offset": 10,

      "type": "SYNONYM",

      "position": 3

    }

  ]

}


위에 추출된 term 목록을 보면 "마왕" 이라는 동의어가 추가 되지 않은것을 볼 수 있습니다.

이것은 두 가지 방법으로 해결이 가능 합니다.

사전에 "신해철" 이라는 인명 사전 정보를 등록 하시면 됩니다.

기본적으로 tokenzier 과정 후 filter 처리가 되면서 사전에 등록된 정보로 term 구성이 되기 때문에 사전에 누락 된 경우는 일반적으로 KoreanFilter 에 의해서 bigram 처리가 됩니다.

다른 한 가지는 position 정보가 구성 시 오류가 나지 않도록 코드를 수정 하는 것입니다.

KoreanFilter 코드를 참고 하셔서 테스트 및 수정 하시면 됩니다.


아래는 사전에 "신해철" 추가 후 실행한 방법 입니다.


GET /syntest/_analyze

{

  "tokenizer": "arirang_tokenizer",

  "filter": [

    "lowercase",

    "trim",

    "arirang_filter",

    "custom_synonym"

  ],

  "text": "신해철은 henry"

}


{

  "tokens": [

    {

      "token": "신해철",

      "start_offset": 0,

      "end_offset": 3,

      "type": "korean",

      "position": 0

    },

    {

      "token": "마왕",

      "start_offset": 0,

      "end_offset": 3,

      "type": "SYNONYM",

      "position": 0

    },

    {

      "token": "henry",

      "start_offset": 5,

      "end_offset": 10,

      "type": "word",

      "position": 1

    },

    {

      "token": "헨리",

      "start_offset": 5,

      "end_offset": 10,

      "type": "SYNONYM",

      "position": 1

    },

    {

      "token": "앙리",

      "start_offset": 5,

      "end_offset": 10,

      "type": "SYNONYM",

      "position": 1

    }

  ]

}


:

[Elasticsearch] elasticsearch-docker plugin 설치는 어떻게?

Elastic/Elasticsearch 2018. 4. 18. 15:44

Elasticsearch docker 만들어 보기 참고하세요.


초간단 예제를 보여 드리겠습니다.

Dockerfile 에 추가해 주시면 됩니다.


# plugin 을 설치 합니다.

RUN bin/elasticsearch-plugin install --batch analysis-icu


$ docker run elasticsearch-6.2.3-ubuntu-14.04-jdk8u152 ls -al plugins/

total 12

drwxr-xr-x 1 elasticsearch elasticsearch 4096 Apr 18 06:38 .

drwxr-xr-x 1 elasticsearch elasticsearch 4096 Apr 18 06:37 ..

drwxr-xr-x 2 elasticsearch elasticsearch 4096 Apr 18 06:38 analysis-icu


참 쉽습니다.

: