2019년 4월 4일 목요일

Elasticsearch - 한번에 데이터를 index로 추가하기 (Bulk Index)

이번에는 한 번에 문서를 Elasticsearch로 2개 이상을 등록하는 방법인 Bulk Index API 예제를 작성한다.

ID를 지정하는 경우

bulk.json
{ "index" : {"_id" : "1" } }
{ "field1": "노코드1", "field2": "노코드2", "field3": "노코드3" }
{ "index" : {"_id" : "2" } }
{ "field1": "노코드1", "field2": "노코드2", "field3": "노코드3" }

위와 같은 파일을 저장한 후 콘솔화면에서 아래의 명령어를 실행한다.
curl -H "Content-Type: application/json" -X POST http://localhost:9200/{index 이름}/{type 이름}/_bulk?pretty --data-binary @bulk.json

ID를 지정하지 않는 경우

자동으로 ID가 생성된다.
bulk.json
{ "index" : {} }
{ "field1": "노코드1", "field2": "노코드2", "field3": "노코드3" }
{ "index" : {} }
{ "field1": "노코드1", "field2": "노코드2", "field3": "노코드3" }

위와 같은 파일을 저장한 후 콘솔화면에서 아래의 명령어를 실행한다.
curl -H "Content-Type: application/json" -X POST http://localhost:9200/{index 이름}/{type 이름}/_bulk?pretty --data-binary @bulk.json

index / type / id를 json 내에 작성하는 경우

bulk.json
{ "index" : { "_index" : "index 이름", "_type" : "type 이름", "_id" : "1" } }
{ "field1": "노코드1", "field2": "노코드2", "field3": "노코드3" }

위와 같은 파일을 저장한 후 콘솔화면에서 아래의 명령어를 실행한다.
curl -H "Content-Type: application/json" -X POST http://localhost:9200/{index 이름}/{type 이름}/_bulk?pretty --data-binary @bulk.json

2019년 3월 30일 토요일

[영상] 구글 스태디아: 게임판 접수 계획 발표 [STADIA revolution]

내년에 이 서비스가 어떻게 되고 있고 어떤 평가를 받고 있을지 궁금해서 일단 스크랩해본다.



Elasticsearch 플러그인(Plugin) 만들기

❝Elasticsearch 플러그인❞을 만들기 위해서 여러가지 찾아보고 내용을 정리해본다.

* Elasticsearch 7.x로 업데이트 되면서 BaseRestHandler 인터페이스를 상속받아서 구현하는 방법이 바뀌어서 내용을 업데이트 합니다.

환경


  • open jdk 11
  • maven
  • elasticsearch 7.15.1


참고

여기의 코드를 참고함.


구현


우선 plugin으로 호출되는 클래스 부터 구현한다.
package org.elasticsearch.plugin.example;

import java.util.Arrays;
import java.util.List;
import java.util.function.Supplier;

import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.cluster.node.DiscoveryNodes;
import org.elasticsearch.common.settings.ClusterSettings;
import org.elasticsearch.common.settings.IndexScopedSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.settings.SettingsFilter;
import org.elasticsearch.plugins.ActionPlugin;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.rest.RestHandler;

public class ExamplePlugin extends Plugin implements ActionPlugin {
@Override
public List<RestHandler> getRestHandlers(final Settings settings,
final RestController restController,
final ClusterSettings clusterSettings,
final IndexScopedSettings indexScopedSettings,
final SettingsFilter settingsFilter,
final IndexNameExpressionResolver indexNameExpressionResolver,
final Supplier<DiscoveryNodes> nodesInCluster) {
return Arrays.asList(
new ExampleNocodeAction(settings, restController));
}
}

이 plugin으로 호출되는 클래스는
src / main / resources / plugin-descriptor.properties 에 정의한다.
version=${project.version}
description=${project.description}
name=example-plugin
classname=org.elasticsearch.plugin.example.ExamplePlugin
java.version=11
elasticsearch.version=${elasticsearch.version}


그리고, 위 ExamplePlugin에서 실제로 호출되고 처리를하는 클래스는 ExampleNocodeAction 클래스이다.

package org.elasticsearch.plugin.example;

import static org.elasticsearch.rest.RestRequest.Method.GET;
import static org.elasticsearch.rest.RestStatus.OK;

import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;

import org.elasticsearch.client.node.NodeClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.rest.BaseRestHandler;
import org.elasticsearch.rest.BytesRestResponse;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.rest.RestRequest;

/**
* Example action with a plugin.
*/
public class ExampleNocodeAction extends BaseRestHandler {

public ExampleNocodeAction(final Settings settings,
final RestController controller) {
// nothing
}

@Override
public String getName() {
return "nocode_example_action.";
}

@Override
public List<Route> routes() {
return Collections.unmodifiableList(Arrays.asList(
new Route(GET, "/{index}/_nocode"),
new Route(GET, "/_nocode")));
}


@Override
protected RestChannelConsumer prepareRequest(final RestRequest request,
final NodeClient client) throws IOException {
final boolean isPretty = request.hasParam("pretty");
final String index = request.param("index");
return channel -> {
final XContentBuilder builder = JsonXContent.contentBuilder();
if (isPretty) {
builder.prettyPrint().lfAtEnd();
}
builder.startObject();
if (index != null) {
builder.field("index", index);
}
builder.field("description",
"This is a example response: " + new Date().toString());
builder.endObject();
channel.sendResponse(new BytesRestResponse(OK, builder));
};
}


}

구현은 여기까지 하면 완성이다.
이제 mvn install을 실행하여 elasticsearch-example-plugin-7.15.1.zip 플러그인 파일을 생성하자.

마지막으로 Elasticsearch에 elasticsearch-example-plugin-7.15.1.zip plugin을 install 해본다.
./bin/elasticsearch-plugin install file:///elasticsearch/elasticsearch-example-plugin-7.15.1.zip

그리고, 이제 Elasticsearch을 시작한다.
시작 로그에서 다음과 같이
"loaded plugin :[example-plugin]"가 표시되면 성공적으로 install이 된 것이다.

[2021-10-15T08:16:36,445][INFO ][o.e.p.PluginsService ] [local] loaded module [x-pack-watcher] [2021-10-15T08:16:36,445][INFO ][o.e.p.PluginsService ] [local] loaded plugin [example-plugin] [2021-10-15T08:16:36,482][INFO ][o.e.e.NodeEnvironment ] [local] using [1] data paths, mounts [[/ (/dev/disk1s1)]], net usable_space [35gb], net total_space [233.4gb], types [apfs] [2021-10-15T08:16:36,482][INFO ][o.e.e.NodeEnvironment ] [local] heap size [989.8mb], compressed ordinary object pointers [true] [2021-10-15T08:16:36,555][INFO ][o.e.n.Node ] [local] node name [local], node ID [6pG_WeHkSEKR3FLLjResXg], cluster name [elasticsearch], roles [transform, data_frozen, master, remote_cluster_client, data, ml, data_content, data_hot, data_warm, data_cold, ingest] [2021-10-15T08:16:40,472][INFO ][o.e.x.m.p.l.CppLogMessageHandler] [local] [controller/67091] [Main.cc@122] controller (64 bit): Version 7.15.1 (Build 96c59930f1bbe9) Copyright (c) 2021 Elasticsearch BV




테스트로 플러그인 API를 호출해 본다.
http://localhost:9200/_nocode




Translate in Google Sheets

Great!!

2019년 3월 29일 금요일

나는 아마존에서 미래를 다녔다

"나는 아마존에서 미래를 다녔다" 블로그 포스트

책소개
나는 아마존에서 12년간 이렇게 일하고, 배우고, 독립했다!

평균 근속 1년 아마존에서 한국인으로서 아마존에서 가장 오래 근무한 박정준의 이야기 『나는 아마존에서 미래를 다녔다』. 아마존에서 12년 동안 근무하며 아마존이 세상에서 가장 미래적인 기업으로 성장하는 과정을 가장 가까이에서 목격한 저자가 아마존에서 배운 일과 삶의 설계를 담은 책이다. 전공에 맞춰 아마존에 입사한 저자는 치열하기로 악명 높은 업무 환경과 익숙지 않은 언어 및 문화 속에서 과연 계속 버틸 수나 있을지 막막해졌다. 그러던 어느 순간 그는 한 회사에 취업하여 일하는 것이 인생의 궁극적인 목표가 될 수는 없다고, 회사는 인생의 목표가 아니라 과정이라고 생각을 전환하게 됐다. 직장 안의 나에서 세상 속의 나로 줌아웃하여 자신의 직장생활을 더 넓은 관점에서 바라보게 된 것이다.

자신의 인생을 좀 더 큰 그림으로 보게 되면서 그의 아마존 생활은 180도 달라졌다. 점점 좁아지는 피라미드에 목숨을 걸기보다는 회사 그 이후의 삶을 주도적으로 계획하며 아마존에서 다양한 직종에 도전하여 많은 것을 배웠다. 저자는 원칙을 지키고, 본질을 보고, 시간을 자신의 편으로 만들고, 실패를 두려워하지 않고, 낭비하지 않고, 머뭇거리지 않고 행동하며, 끊임없이 혁신하는 아마존의 모든 성장 원리들을 고스란히 일과 삶의 가르침으로 받아들이고 자신의 삶에 적용했다. 이처럼 저자가 아마존 12년을 훈련과 배움의 과정, 곧 도제의 시간으로 여기며 깨달은 이야기들을 만나볼 수 있다.

2019.4.4 박정준님 스타트업 토크쇼




2019년 3월 28일 목요일

Elasticsearch - 자주 사용하는 명령어(CMD) 목록


검색 운영 업무중에 자주 사용하는 명령어(CMD) 목록을 정리해 본다.

목록

  1. Elasticsearch 관련
  2. Index 관련
  3. Alias 관련
  4. Document 유형 관련
  5. ReIndex API

1. Elasticsearch 관련

시작
 sudo /etc/init.d/elasticsearch start 

재시작
 sudo /etc/init.d/elasticsearch restart 

상태확인
 curl -XGET 'localhost:9200/' 


2. Index 관련

인덱스 확인
 curl -XGET 'localhost:9200/_cat/indices?v' 
설정 확인
 curl -XGET 'localhost:9200/{indexName}/_settings?pretty' 
맵핑 확인
 curl -XGET 'localhost:9200/{indexName}/_mapping?pretty' 
삭제
 curl -XDELETE 'localhost:9200/{indexName}?pretty' 


3. Alias 관련

Alias 확인
 curl -XGET 'localhost:9200/_aliases?pretty' 

4. Document 관련

문서 수 확인
 curl -sS -XGET 'localhost:9200/{indexName}/{typeName}/_count?pretty' 

필드추가
curl -XPUT 'localhost:9200/{indexName}/_mappings/{typeName}?pretty' -d '
{
  "properties" : {
    "field1" : {
      "type" : "long"
    },
    "field2" : {
      "type" : "date"
    }
  }
}'

벌크색인
curl -XPOST 'localhost:9200/{indexName}/{typeName}/_bulk?pretty' --data-binary @xxx.json


전체 데이터 삭제
curl -XPOST 'localhost:9200/{indexName}/{typeName}/_delete_by_query' --d '
{
  "query":{
    "match_all":{
    }
  }
}'

데이터 검색
curl -sS -XGET 'localhost:9200/{indexName}/{typeName}/_search?pretty'


데이터 검색 ((From-To 지정)
curl -sS -XGET 'localhost:9200/{indexName}/{typeName}/_search?pretty' -d '
{
  "query":{
    "match_all":{
    }
  },
  "from":0,
  "size":400
}'


데이터 갱신 (ID 지정)
curl -XPOST 'localhost:9200/{indexName}/{typeName}/{id}/_update' -d '
{
  "doc":{
    "field1": "test123"
  }
}'

데이터 일괄 업데이트
curl -XPOST 'localhost:9200/{indexName}/{typeName}/_update_by_query?conflicts=proceed&pretty' -d '
{
  "query":{
    "match_all": { }
  },
  "script":{
    "inline": "ctx._source.field1 = 'test123'"
  }
}'


5. ReIndex API

reindex
curl -XPOST 'localhost:9200/_reindex?pretty' -d '
{
  "source": {
    "index": "fromIndex"
  },
  "dest": {
    "index": "toIndex"
  }
}'


reindex (remote)
curl -XPOST 'localhost:9200/_reindex?pretty' -d '
{
  "source": {
    "remote": {
      "host": "http://otherhost:9200",
      "username": "user",
      "password": "pass"
    },
    "index": "fromIndex"
    }
  },
  "dest": {
    "index": "toIndex"
  }
}