Elasticsearch 先輩で日付を触りたい

やりたかったこと

日付の取り回しについて調べてみた。 具体的には、以下のようなデータを想定する。

  • 20170713
  • 2017/07/13
  • 2017-07-13
  • 2017年07月13日
  • 2017713
  • 2017/7/13
  • 2017-7-13
  • 2017年7月13日

これらが不特定なカラムに入っている時に、 Elasticsearch で、いい感じに受け取るにはどうすればいいか?を調べてみた。

自動的にやってくれるもの

Elasticsearch は自動的に型を判断していい感じにマッピングしてくれる機能がある。 そこで使われる日付のフォーマットはこちらに一覧されている。

format | Elasticsearch Reference [5.5] | Elastic

というわけで、以下の実験データを何もマッピングされていない index に突っ込んでみる

{"index":{"_index":"sample_index","_type":"sample_type","_id":"1"}}
{"DATE1":"2017年07月13日"}
{"index":{"_index":"sample_index","_type":"sample_type","_id":"2"}}
{"DATE2":"2017/07/13"}
{"index":{"_index":"sample_index","_type":"sample_type","_id":"3"}}
{"DATE3":"2017-07-13"}
{"index":{"_index":"sample_index","_type":"sample_type","_id":"4"}}
{"DATE4":"2017.07.13"}
{"index":{"_index":"sample_index","_type":"sample_type","_id":"5"}}
{"DATE5":"20170713"}
{"index":{"_index":"sample_index","_type":"sample_type","_id":"6"}}
{"DATE6":"2017年7月13日"}
{"index":{"_index":"sample_index","_type":"sample_type","_id":"7"}}
{"DATE7":"2017/7/13"}
{"index":{"_index":"sample_index","_type":"sample_type","_id":"8"}}
{"DATE8":"2017-7-13"}
{"index":{"_index":"sample_index","_type":"sample_type","_id":"9"}}
{"DATE9":"2017.7.13"}
{"index":{"_index":"sample_index","_type":"sample_type","_id":"10"}}
{"DATE10":"2017713"}
$ curl -s -X GET 'http://localhost:9200/sample_index/' | jq .
{
  "sample_index": {
    "aliases": {},
    "mappings": {
      "sample_type": {
        "properties": {
          "DATE10": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          },
          "DATE2": {
            "type": "date",
            "format": "yyyy/MM/dd HH:mm:ss||yyyy/MM/dd||epoch_millis"
          },
          "DATE3": {
            "type": "date"
          },
          "DATE4": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          },
          "DATE5": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          },
          "DATE6": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          },
          "DATE7": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          },
          "DATE8": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          },
          "DATE9": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          }
        }
      }
    },
    "settings": {
      "index": {
        "creation_date": "1499935832320",
        "number_of_shards": "5",
        "number_of_replicas": "1",
        "uuid": "Tf1HqJSfQzmoIPVKrMgnQQ",
        "version": {
          "created": "5040299"
        },
        "provided_name": "sample_index"
      }
    }
  }
}

結果から分かる通り、YYYY/MM/DD の形のみ受け付けている

そして、 format | Elasticsearch Reference [5.5] | Elastic にある通り、07月のように0埋めしないと自動ではマッピングしてもらえない。

なので、 DATE5~10 のフォーマットは、事前に何かをしてやるとかしないと、難しそう。

フォーマットを自分で定義してみる

次に自分で mapping を指定してデータの登録を行ってみる。

{
  "mappings": {
    "sample_type": {
      "properties": {
        "DATE1": {
          "type":   "date",
          "format": "yyyy年MM月dd日"
        },
        "DATE3": {
          "type":   "date",
          "format": "yyyy-MM-dd"
        },
        "DATE4": {
          "type":   "date",
          "format": "yyyy.MM.dd"
        },
        "DATE5": {
          "type":   "date",
          "format": "yyyy年M月dd日"
        }
      }
    }
  }
}

登録は次の用に行う。

curl -X PUT 'http://localhost:9200/sample_index' --data-binary @mapping.json | jq .

これを行った後、 DATE1~5について再度データの挿入をしてみた。

$curl -X GET 'http://localhost:9200/sample_index/' | jq .
{
  "sample_index": {
    "aliases": {},
    "mappings": {
      "sample": {
        "properties": {
          "DATE1": {
            "type": "date",
            "format": "yyyy年MM月dd日"
          },
          "DATE3": {
            "type": "date",
            "format": "yyyy-MM-dd"
          },
          "DATE4": {
            "type": "date",
            "format": "yyyy.MM.dd"
          },
          "DATE5": {
            "type": "date",
            "format": "yyyy年M月dd日"
          }
        }
      },
      "sample_type": {
        "properties": {
          "DATE1": {
            "type": "date",
            "format": "yyyy年MM月dd日"
          },
          "DATE2": {
            "type": "date",
            "format": "yyyy/MM/dd HH:mm:ss||yyyy/MM/dd||epoch_millis"
          },
          "DATE3": {
            "type": "date",
            "format": "yyyy-MM-dd"
          },
          "DATE4": {
            "type": "date",
            "format": "yyyy.MM.dd"
          }
        }
      }
    },
    "settings": {
      "index": {
        "creation_date": "1500369904641",
        "number_of_shards": "5",
        "number_of_replicas": "1",
        "uuid": "EN99Zmc7TuK-7ePO1S9T6A",
        "version": {
          "created": "5040299"
        },
        "provided_name": "sample_index"
      }
    }
  }
}

これで、データ型での登録ができるようになった。 しかし、これでは、予め決められているカラムにしかデータの取込が出来ないため、デフォルトとして登録することを考えるもやり方が分からない。。。

Dynamic template は index の正規表現に則って、決まった index に対してマッピングを当てはめるみたいなことはできるが、不特定のカラム名に対して割り当てるのは難しい。

Dynamic templates | Elasticsearch Reference [5.5] | Elastic

うーん。。。

フォーラムで質問して見るかしら。。。

(追記 7月20日) フォーラムで質問したら、大谷さんから回答が返ってきた。

不特定カラムに対して日付のフォーマットを指定する - Elastic In Your Native Tongue / 日本語による質問・議論はこちら - Discuss the Elastic Stack

6.0.0-alpha2 だと試せるそうなので、試し見た。

$ curl -X PUT -H 'Content-Type: application/json' http://localhost:9200/my_index -d '
{
  "mappings": {
    "my_type": {
      "dynamic_date_formats": ["yyyy年MM月dd日", "yyyy/MM/dd", "yyyy.MM.dd", "yyyy-MM-dd"]
    }
  }
}' | jq .
{
  "acknowledged": true,
  "shards_acknowledged": true
}
$ curl -X POST -H 'Content-Type: application/json' http://localhost:9200/_bulk --data-binary @test.json 
$ curl -X GET http://localhost:9200/my_index | jq .
{
  "my_index": {
    "aliases": {},
    "mappings": {
      "my_type": {
        "dynamic_date_formats": [
          "yyyy年MM月dd日",
          "yyyy/MM/dd",
          "yyyy.MM.dd",
          "yyyy-MM-dd"
        ],
        "properties": {
          "DATE1": {
            "type": "date",
            "format": "yyyy年MM月dd日"
          },
          "DATE2": {
            "type": "date",
            "format": "yyyy/MM/dd"
          },
          "DATE3": {
            "type": "date",
            "format": "yyyy-MM-dd"
          },
          "DATE4": {
            "type": "date",
            "format": "yyyy.MM.dd"
          }
        }
      }
    },
  以下略
  }
}

意図通り、 date 型でのデータの保存ができるようになった。 ポイントは、事前に dynamic_date_formats を指定することで、予め想定される date を確定することだった。 これを template 化しておくことで、任意の index に対して、任意のカラムの日付を割り当てることができそう。

Dynamic field mapping | Elasticsearch Reference [master] | Elastic

日付が扱えるとどんな検索ができるか?

Range Query | Elasticsearch Reference [5.5] | Elastic

  • ○日から○日の範囲内
  • 今から何日以内
  • この日から何日以内

といった検索が行えそう

まとめ

文字列をトークン化するとか、予め決められたデータを処理することに対しては、 Elasticsearch は有効打にできそうだけど、日付周りは、フォーマットの関係に一工夫必要だった。

Elasticsearch 先輩との戯れ日記(登場人物の整理)

この記事何?

お仕事で Elasticsearch 先輩を使うことになったので、そのお戯れの記録

登場人物の整理

事例を中心に調査していたが、色々混乱してきたので、 Elasticsearch の世界の登場人物を整理することにした。

雑な説明

hoge

オブジェクト 概要
Cluster Node を束ねる存在
Node Elasticsearch のインスタンスのこと
Shard Index を分割したもの
Index インデックス
Type テーブル
Document レコード

雑すぎるので1個ずつ見ていく。

Cluster

Node を束ねるもの。 Cluster Health を使って、 Cluster の状態を確認することができる。

  • green: すべての Shard が Cluster 上に配置されてる
  • yellow: PrimaryShard は Cluster 上に配置されているが、配置されていない ReplicaShard が存在している
  • red: Cluster 上に配置されていない Shard が存在している

Shard については、後述。

Node

Node | Elasticsearch Reference [5.4] | Elastic

Node には Master (eligible) Node と Data Node と Ingest node が主要な感じ(Tribe Node はよくわからなかった)

Master Eligible Node

  • クラスタ内で行う処理の分配を行う
  • もし、 Master Node が死んだら、別の Master Eligible Node が Master になってくれる(デフォルトでは全 Node)
  • Master Node は、基本的に分配だけをメインにやらせると安定運用につながる

Data Node

  • document の管理を行う
  • 主に CRUD 操作を担当
  • Master Node とは役割分離させようね
  • I/O 系の処理はメモリいっぱい使うからちゃんと監視しようね

Data Node が死んじゃったらデータは欠損する? discovery.zen.minimum_master_nodes を設定すれば、設定した値の数で最小構成として、データの書き込みとかを行うみたい(おそらく) なので、一応データの欠損は防げるらしい。

参考:Avoiding split brain with minimum_master_nodes

Ingest Node

  • Elasticsearch 5.0 から登場
  • index を貼る前にドキュメントを変換するために使う Node
  • パイプラインを作って複数の処理を組み合わせて実行もできる

Shard

Shards & Replicas

10億個の document を複数に分割して読み込めるように、 index を分割する仕組み。 これによって、オペレーションの分散と並列化ができるため、パフォーマンスの向上につながる。

Primary shard を増やせば増やすほど並列数が上がって1ノードのパフォーマンスが上がるかも? →ただ、その分スペックは要求される デフォルトは5つの Primary Shard と 各 Primary Shard 毎に 1つの Replica Shard

Replica Shard は Primary Shard コピーを複製してあるもの。 このコピーは同一 Node には作成出来ない。

elas_0401.png

出典 : How Primary and Replica Shards Interact | Elasticsearch: The Definitive Guide [2.x] | Elastic

Index

Document を纏めるもの。 インデックス 付け、検索、更新、削除全てを行う。

Reindex | Curator Reference [5.1] | Elastic

インデックスを貼り直すとかって事もできる。 ここらへんが検索周りのチューニングポイントらしい。

Type

Document を種類別に分別することができる。 データベースで言うところの table

Document

1レコード分の情報が入ってるもの。 JSON で表現される。

Index Type Document の相関図はこんな感じ el-640x374.png

出典: データ構造について – AWSで始めるElasticSearch(4) | Developers.IO

まとめ

登場人物整理したら、なんとなく事例でチューニングした話とかも分かりそうな気がした。 あくまで、気がしているだけ。 図とかは出典元を見ていただけるとうれしいです。

番外編

こいつで、docker-compose up ってするだけで、 kibana と elasticsearch がどっちも立ち上がるんだって :sugoi:

version: '2'
services:
  kibana:
    image: kibana
    links:
      - elasticsearch:elasticsearch
    ports:
      - 5601:5601

  elasticsearch:
    image: elasticsearch
    ports:
      - 9200:9200
      - 9300:9300

公式もサポートしているっぽい。 library/elasticsearch - Docker Hub

Elasticsearch 先輩との戯れ日記(データの投入)

この記事何?

お仕事で Elasticsearch 先輩を使うことになったので、そのお戯れの記録

複数ドキュメントを扱う API って何がある?

Document APIs | Elasticsearch Reference [5.4] | Elastic

Multi-document API いっぺんにデータ突っ込んだり取ってきたりするやつっぽいな。

  • Multi Get API(データの取得)
  • Bulk API(データの挿入)
  • Delete By Query API(データの削除)
  • Update By Query API(データの更新)
  • Reindex API(インデックスの貼り直し)

データの投入について

Bulk API

Bulk API | Elasticsearch Reference [5.4] | Elastic

The bulk API makes it possible to perform many index/delete operations in a single API call. This can greatly increase the indexing speed.

endpoint は /_bulk, /{index}/_bulk, {index}/{type}/_bulk の3つ。 パラメーターに JSON 形式で挿入したいデータを指定する。 もし、データを送りたい場合は、

  • データの最終行に改行(\n)を入れる
  • もし改行したい場合は、 carriage return (\r) を使ってね
  • Content-Type: application/x-ndjson をヘッダーに含めて送ってね

を守ること。

$curl -X POST -H 'Content-Type: application/x-ndjson' 'localhost:9200/test/account/_bulk?pretty&refresh' --data-binary "@accounts.json"  
   ...
    {
      "index" : {
        "_index" : "test",
        "_type" : "account",
        "_id" : "990",
        "_version" : 1,
        "result" : "created",
        "forced_refresh" : true,
        "_shards" : {
          "total" : 2,
          "successful" : 1,
          "failed" : 0
        },
        "created" : true,
        "status" : 201
      }
    },
    {
      "index" : {
        "_index" : "test",
        "_type" : "account",
        "_id" : "995",
        "_version" : 1,
        "result" : "created",
        "forced_refresh" : true,
        "_shards" : {
          "total" : 2,
          "successful" : 1,
          "failed" : 0
        },
        "created" : true,
        "status" : 201
      }
    }
  ]
}

CSV ファイルから直接いけるか?

:no_good: :no_good: デフォルトでは CSV ファイルから、直接ぶち込むことはできず、一旦 JSON 形式に変換してあげる必要がありそう。

$ curl -s -X POST -H 'Content-Type: text/csv' 'localhost:9200/test/feed/_bulk?pretty&refresh' --data-binary "@test_feed1000.csv" | jq
{
  "error": "Content-Type header [text/csv] is not supported",
  "status": 406
}

打開案

1手目はできそうだけど、そこまでして、 Elasticsearch の内部でデータを持つ必要があるのか説はあるな。。。

注意点

設計によっては、 OOM とかが頻発しちゃうのは結構怖さあるな。 OOM 発動しなくてもデータ破損とかはかなり痛いしな もうちょっとここは追加調査が必要。 場合によっては、検索/保存で仕組みを切り離しちゃうのも1つなんだろうなというお気持ちになりました。

余談

データの取得

ついでに確認のために、調べたので、データの取得も _search に query を投げてあげれば取れる。 from で指定する値が行数。 size が何行取り出すか。

以下の例は100行目から2行取り出すとなるので、100,101行目が取り出せている。

$ curl -s -X GET http://localhost:9200/test/account/_search -d '{"query":{"match_all": {}}, "from": "100", "size": "2"}' | jq .
{
  "took": 3,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "failed": 0
  },
  "hits": {
    "total": 1000,
    "max_score": 1,
    "hits": [
      {
        "_index": "test",
        "_type": "account",
        "_id": "383",
        "_score": 1,
        "_source": {
          "account_number": 383,
          "balance": 48889,
          "firstname": "Knox",
          "lastname": "Larson",
          "age": 28,
          "gender": "F",
          "address": "962 Bartlett Place",
          "employer": "Bostonic",
          "email": "knoxlarson@bostonic.com",
          "city": "Smeltertown",
          "state": "TX"
        }
      },
      {
        "_index": "test",
        "_type": "account",
        "_id": "408",
        "_score": 1,
        "_source": {
          "account_number": 408,
          "balance": 34666,
          "firstname": "Lidia",
          "lastname": "Guerrero",
          "age": 30,
          "gender": "M",
          "address": "254 Stratford Road",
          "employer": "Snowpoke",
          "email": "lidiaguerrero@snowpoke.com",
          "city": "Fairlee",
          "state": "LA"
        }
      }
    ]
  }
}

というわけで、100行単位でページングみたいな使い方はできそうということが分かった。

Elasticsearch 先輩との戯れ日記(セットアップなどなど)

この記事何?

お仕事で Elasticsearch 先輩を使うことになったので、そのお戯れの記録

今回調べたものなど

まず Elasticsearch でどんなことができるかを調べるために、公式のチュートリアルとここ載ってる記事を読み漁った。

環境準備

公式さんから最新版(5.4)を Download してきた。 Kibana も合わせてローカルで準備しておく(戯れ用)

Download Elasticsearch Free • Get Started Now | Elastic Download Kibana Free • Get Started Now | Elastic

日本語全文検索用のプラグインである analysis-kuromoji を入れてみる。

# /Users/pokotyamu/Downloads/elasticsearch-5.4.2
$ bin/elasticsearch-plugin install analysis-kuromoji
-> Downloading analysis-kuromoji from elastic
[=================================================] 100%
-> Installed analysis-kuromoji

curl で叩いて、 plugin が入っていることを確認。

$ curl -X GET 'http://localhost:9200/_nodes/plugins?pretty'
{
  "_nodes" : {
    "total" : 1,
    "successful" : 1,
    "failed" : 0
  },
  "cluster_name" : "elasticsearch",
  "nodes" : {
    "tfQ1ZsOzQ9CYZtA0jXw2ww" : {
      "name" : "tfQ1ZsO",
      "transport_address" : "127.0.0.1:9300",
      "host" : "127.0.0.1",
      "ip" : "127.0.0.1",
      "version" : "5.4.2",
      "build_hash" : "929b078",
      "roles" : [
        "master",
        "data",
        "ingest"
      ],
      "plugins" : [
        {
          "name" : "analysis-kuromoji",
          "version" : "5.4.2",
          "description" : "The Japanese (kuromoji) Analysis plugin integrates Lucene kuromoji analysis module into elasticsearch.",
          "classname" : "org.elasticsearch.plugin.analysis.kuromoji.AnalysisKuromojiPlugin",
          "has_native_controller" : false
        }
      ],
     ...
}

基本コマンド

起動は以下で行う。終了は Ctrl-C-d オプションでバックグラウンド起動可能。

# /Users/pokotyamu/Downloads/elasticsearch-5.4.2
$ bin/elasticsearch

ポートはデフォルトで 9200番が使われるみたい。 crul で叩いてみて、次のように出たらOK

$ curl localhost:9200
{
  "name" : "tfQ1ZsO",
  "cluster_name" : "elasticsearch",
  "cluster_uuid" : "Y2-U0XlwSxWX771QXbIN0w",
  "version" : {
    "number" : "5.4.2",
    "build_hash" : "929b078",
    "build_date" : "2017-06-15T02:29:28.122Z",
    "build_snapshot" : false,
    "lucene_version" : "6.5.1"
  },
  "tagline" : "You Know, for Search"
}

データ投入してみる

マッピング定義

MySQL Elasticsearch
database index
schema mapping
table type
record document

だいたいこんな感じの対応をしている。 マッピングの定義はこんな感じ↓

{
    "mappings": {
        "sample": {
            "properties": {
                "name": {
                    "type": "string",
                    "index": "not_analyzed"
                },
                "coord": {
                    "type": "geo_point"
                },
                "description": {
                    "type": "string",
                    "analyzer": "kuromoji"
                }
            }
        }
    }
}

sample の部分が type の名前になっている。 これを landmark という index に突っ込むためには PUT で書き換えれる。

# curl -X PUT http://localhost:9200/<Index Name>
$ curl -X PUT http://localhost:9200/landmark --data-binary @mapping.json
$ curl -s -X GET http://localhost:9200/landmark | jq .
{
  "landmark": {
    "aliases": {},
    "mappings": {
      "sample": {
        "properties": {
          "coord": {
            "type": "geo_point"
          },
          "description": {
            "type": "text",
            "analyzer": "kuromoji"
          },
          "name": {
            "type": "keyword"
          }
        }
      }
    },
    "settings": {
      "index": {
        "creation_date": "1498037230489",
        "number_of_shards": "5",
        "number_of_replicas": "1",
        "uuid": "9tFY4-F5S-KTyVEAa40DCg",
        "version": {
          "created": "5040299"
        },
        "provided_name": "landmark"
      }
    }
  }
}

ちなみに、このマッピング定義をしないでも、登録はできるらしい。 その時は Elasticsearch 側がよしなに値から型定義をしてくれるとか。凄い。

データ投入

今回は Bulk API でデータ投入してみる。 Bulk だと一回のリクエストで一気にデータを突っ込めるみたい。

{ "index" : {} }
{ "name": "スカイツリー", "coord": { "lat": "35.710063", "lon": "139.8107"}, "description": "東京スカイツリー(とうきょうスカイツリー、英: TOKYO SKYTREE)は東京都墨田区押上一丁目にある電波塔(送信所)である。"}
{ "index" : {} }
{ "name": "BLUE NOTE TOKYO", "coord": { "lat": "35.661198", "lon": "139.716207"}, "description": "N.Y.の「Blue Note」を本店に持つジャズクラブとして、南青山にオープンしたブルーノート東京。ジャズをはじめとする多様な音楽ジャンルのトップアーティストたちが、連夜渾身のプレイを繰り広げている。"}
{ "index" : {} }
{ "name": "東京タワー", "coord": { "lat": "35.65858", "lon": "139.745433"}, "description": "東京タワー(とうきょうタワー、英: Tokyo Tower)は、東京都港区芝公園にある総合電波塔とその愛称である。正式名称は日本電波塔(にっぽんでんぱとう)。"}
# curl -X POST http://localhost:9200/<Index Name>/<Type Name>/_bulk --data-binary @landmark.json
$ curl -X POST http://localhost:9200/landmark/sample/_bulk --data-binary @landmark.json
{
    "errors": false, 
    "items": [
        {
            "index": {
                "_id": "AVzJ-slvXdDNsyfvvuMV", 
                "_index": "landmark", 
                "_shards": {
                    "failed": 0, 
                    "successful": 1, 
                    "total": 2
                }, 
                "_type": "sample", 
                "_version": 1, 
                "created": true, 
                "result": "created", 
                "status": 201
            }
        }, 
        {
            "index": {
                "_id": "AVzJ-slwXdDNsyfvvuMW", 
                "_index": "landmark", 
                "_shards": {
                    "failed": 0, 
                    "successful": 1, 
                    "total": 2
                }, 
                "_type": "sample", 
                "_version": 1, 
                "created": true, 
                "result": "created", 
                "status": 201
            }
        }, 
        {
            "index": {
                "_id": "AVzJ-slwXdDNsyfvvuMX", 
                "_index": "landmark", 
                "_shards": {
                    "failed": 0, 
                    "successful": 1, 
                    "total": 2
                }, 
                "_type": "sample", 
                "_version": 1, 
                "created": true, 
                "result": "created", 
                "status": 201
            }
        }
    ], 
    "took": 286
}

検索

最後に検索。検索も json で query を作って実行する。 メソッドは GET を使用。 検索に使えるのは、調べ中。。。 AND 検索はもちろん、数値の比較とか範囲指定とかもできるみたい。

$ curl -s -X GET http://localhost:9200/landmark/sample/_search -d '{"query":{"match":{"description":"ジャズ"}}}' | jq .
{
  "took": 2,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "failed": 0
  },
  "hits": {
    "total": 1,
    "max_score": 0.928525,
    "hits": [
      {
        "_index": "landmark",
        "_type": "sample",
        "_id": "AVzJ-slwXdDNsyfvvuMW",
        "_score": 0.928525,
        "_source": {
          "name": "BLUE NOTE TOKYO",
          "coord": {
            "lat": "35.661198",
            "lon": "139.716207"
          },
          "description": "N.Y.の「Blue Note」を本店に持つジャズクラブとして、南青山にオープンしたブルーノート東京。ジャズをはじめとする多様な音楽ジャンルのトップアーティストたちが、連夜渾身のプレイを繰り広げている。"
        }
      }
    ]
  }
}

まとめ

ひとまず、ローカルで立ち上げる→データを突っ込む→検索するの流れは試せてた。 次はどーやったら、がつっとデータ突っ込めるかを調べてみる。

社会人2年目もチェスト!!!

ついに社会人1年目終了。

東京の生活には全然慣れませんが、健康体でいれたので、 病欠による休みはゼロでした! (5分ぐらいの遅刻1回)

ただ、オリジン弁当とズブズブの関係なので、体重は7キロぐらい太りました。適正体重まであとちょっとですw


1年目は、技術力が高い先輩や同期に圧倒されつつも、ちょっとずつ自分の出来ることが増えていくのを感じれることができました。

自分の中で、学びたかったスクラムの知識を整理できたのも非常に身になりました!

pokotyamu.hatenablog.com

ただ、プログラミングスキルに関しては、まだまだ発展途上… 結局 Rails しか満足に勉強できてないし…(その Rails もまだまだ修行中)


今の会社では、自分のエンジニアとしてのキャリアパスに対して、色々挑戦させていただいたり、サポートしてくれる環境が整っているので、それに応えれるように準備してこうな!

この一年は自分のプログラミングスキルを一から見直す。

そのための勉強をするときのコストはなるべく惜しまないようにしていきたいと考えていて、2年目の初日、 Mac Book Pro の 2016年モデル買いました。

f:id:pokotyamu:20170403003051p:plain

2年目もチェスト!!!!!(宗りんおかえり!!!!!!)

新卒が真面目にアジャイル勉強してみた

社内勉強会で、アジャイルについて発表してみたので、こちらにも残しておきます!!!

当日資料はこちら↓↓

やってみた感想

きっかけは、なぜスクラムしているの?

資料の中にも書いていますが、自分が今回アジャイルについて勉強を始めたきっかけは、1番新しくチームメンバーとして入った方の「なぜスクラムしているの?その利点は何?」という発言でした。

実際に開発メンバーとして仕事をしている上で、何も知らない中スクラムを実施していることに違和感があったので、今回社内勉強会という機会を利用して、自分で勉強して、発表することにしました。

結果として、今のチームとしてここは良い点悪い点が言えて、そこから改善案も考えれるようになりました。

新卒が開発手法について口を出す

チームの中でも1番年下になりやすい新卒だからこそ見える世界もあると思います。 ただ、意見を1番いいにくいのも新卒な気がします(経験の問題や会社の風土だからという感じに流される)。

だからこそ、意見を言うための確かな知識を勉強して、経験じゃない根拠を持って話すことが出来れば、雰囲気に押し流されない意見を言えるようになるのではないか?とやってみて感じました。

アジャイル守破離の心構え

「これが正解」「こうやるもの」というわけではなく「何を目的にしていてこういうやり方が進められている」を知るのが「守」かな

これは、発表を終えたあと頂いた弊社 CTO からの感想です。

これも先程述べたように、原理原則を知った上で行動や意見をする必要があるという気づきにつながりました。

勉強会に対するモチベーション

準備期間が長く取れる場合ほど、途中で別のことしたくなったりなどあるかと。

今回はそれ対策で、途中途中で発表内容に使う本のまとめやセミナーのまとめを社内 Qiita やこのブログにまとめることで承認欲求を満たしつつ準備するというので乗り切りましたw

何かしらのリアクションやご意見を頂きつつ勉強することで、自分の見えてない見方や考えを途中の段階でも知れるきっかけになります! これは意外とよかったです。

pokotyamu.hatenablog.com

pokotyamu.hatenablog.com

※内容がネタバレ的になっちゃう系の場合はこそこそ頑張りましょう!

まとめ

スライドメインなので、さらっと終わりますが、今までチームとしてのやり方しかしらず、アジャイルスクラムを研修や独学で知識を付けてない方はぜひ一度勉強することをおすすめします!!!!

LEGOスクラムに参加した!(レゴ編)

この記事なに?

12/14(木) に行われたLEGOスクラムのレポートです!

イベントの詳細についてはこちら↓

waicrew.doorkeeper.jp

座学編はこちら↓

pokotyamu.hatenablog.com

今回は実際のワークでもあるレゴ編です!

ユーザーストーリーを作る

各チームが1つの家族で、家族が希望する街に引っ越すという形でワークが始まりました。

私が担当したのは私立女子校に通う JK の娘↓

f:id:pokotyamu:20161214131920j:plain

要求(ユーザーストーリー)はこのテンプレートにそって書いていきます。

(役割や立場)として、
(機能や性能)が欲しい。
それは(理由や目的)のためだ。

f:id:pokotyamu:20161214135450j:plain

うん!実に最近の JK っぽいですね()

ここでポイントとなるのが、3行目で、3行目の理由や目的を解決するための手段として、2行目があるので、できるだけ3行目を具体的に書くことが大事になってきます!

もちろん、別な手段(2行目)で解決出来ることが開発側からあれば、そちらを提案できるようにしておくのが、顧客にも求められます。

プランニングポーカー

それぞれのユーザーストーリーがまとまったら、プランニングポーカーを使って、見積もりをしていきます。

あくまで絶対評価ではなく、相対評価で、作業の見積もりを行うやり方です。

写真は、10個の都道府県の人口を 1,2, 3, 5, 8 に分けるワークを行っています。 基準として与えられたのが、福岡が5、長野が2という情報だけです。

f:id:pokotyamu:20161214133326j:plain

絶対評価だと、難しいですが、相対評価になることで、見積もりの精度を上げることができます。

ただし、このやり方は声の大きい人の意見が通りやすいので、ファシリテートが大事そうに感じました。

いよいよ LEGO

ここまで終わったら、実際に LEGO を使ってまちづくりです。 今回のスプリントでやることを決める(2分) →作る(7分)→顧客が評価する→振り返り この流れで3スプリント分行いました。

f:id:pokotyamu:20161214143157j:plain

f:id:pokotyamu:20161214165811j:plain

成果

最初は獲得したベロシティは0pt だったのですが、最後は9pt 分獲得することができました。

その要因としては、

  • チームとして、最終成果物の認識を合わせることが大事
  • 作業の分担を決めた
  • 全員で1つの作業に取り掛かった
  • PO に質問を最初にまとめておこなった
  • 時間管理を行った

このような点が考えられます。

2つ目に挙げた作業分担ですが、正直これに関しては、スクラムというやり方的には真っ当ではなかったのですが、3スプリントで成果をだすことを目標にしたので、固定化しました。

もっと期間がながければ、役割を何度かシャッフルして、得意な分野に対して、サインアップしていくやり方が正しい気がします。今回はスキルなどもわからない点が多かったためにこのような形をとりました。

また、最後の時間管理については、あと何分だから、ここは諦めるなどの決定をチームで行ったことで、ミニマムな建造物を作る方にシフトしてたのもベロシティがあがった要因だと思います。

これは、品質の悪いものをとどけるという意味ではなく、成果が0よりは、何かしらの形あるものに対してフィードバックをもらって開発を進めたほうがよいという判断をチームで行ったために、このようになりました。

結果として、アジャイルな開発に沿った形で開発を行えた気がします。

難しかった点

  • 他のチームの PO を捕まえて仕様を確認する必要が度々発生したのですが、やはり PO は忙しいので、個別でいくのではなく、まとめて質問する必要があると感じました
  • 成果物のイメージが固まっていないと、各々の方向に向かって開発を進めてしまって、結果何もできないという未来がまっていた
  • カンバンに対する意識を7分間の中に入れ込むのは少し難しい
    • もっと時間のある期間であれば問題なさそう

感想

2つの記事に分けてまとめていきましたが、率直に参加して良かったと思っています。

体型的に知識を入れることができた点もありますが、実際のワークを通して、アジャイルな考え方が整理された気がします。 また、スクラム開発を行うのであれば、チームメンバーは最低限の知識として、このぐらいは経験じゃなく本や研修として学んでおくほうがよいと感じました。

もうしばらく、スクラムアジャイルについて勉強を深めていきたいと思います!!!! f:id:pokotyamu:20161214170602j:plain