elasticsearch(6.3.0)+kibana(6.3.0)をwindows環境で動作させる


# はじめに<a id="sec-1" name="sec-1"></a>

俺も世の中のイケてるエンジニアみたいにビッグデータを可視化してドヤ顔したい！  
…ので友人から教えてもらったelasticsearchとkibanaを
へっぽこエンジニアがローカル環境で動作させるまでのメモ  

# 環境<a id="sec-2" name="sec-2"></a>

-   Windows 10 Home 1803
-   elasticsearch 6.3.0
-   kibana 6.3.0
-   curl 7.61.0

# 今回テストに使用するデータ<a id="sec-3" name="sec-3"></a>

各家庭や工場等の電力消費量を集めたデータセット  
<https://archive.ics.uci.edu/ml/datasets/ElectricityLoadDiagrams20112014>  

## 特徴<a id="sec-3-1" name="sec-3-1"></a>

-   データ取得期間: [2011/1/1, 2015/1/1]
-   測定欠測値は存在しない
-   期間中に利用開始・終了がある場合、測定値は0と表示される

## データ形式<a id="sec-3-2" name="sec-3-2"></a>

-   CSV形式
-   カラム区切り文字: semicolon (;)
-   小数点区切り文字: colon (,) (ポルトガルではこれが標準らしい)
-   1番目のカラム: データ取得時間 (15分間隔)
-   2番目のカラムから: 消費電力 (kW単位)

# インストール<a id="sec-4" name="sec-4"></a>

## Java<a id="sec-4-1" name="sec-4-1"></a>

elasticsearchはJava製なのでJREのインストール  

    java version "1.8.0_171"
    Java(TM) SE Runtime Environment (build 1.8.0_171-b11)
    Java HotSpot(TM) 64-Bit Server VM (build 25.171-b11, mixed mode)

設定してなければJAVA\_HOMEを環境変数に設定  

    C:\Program Files\Java\jre1.8.0_171(インストールした場所を指定)

## elasticsearchとkibana<a id="sec-4-2" name="sec-4-2"></a>

### elasticsearch<a id="sec-4-2-1" name="sec-4-2-1"></a>
<https://www.elastic.co/downloads/elasticsearch>
windows環境なのでzipを選択  

### kibana<a id="sec-4-2-2" name="sec-4-2-2"></a>

<https://www.elastic.co/downloads/kibana>
windowsのリンクをクリックしてzipをダウンロード  

### 起動<a id="sec-4-2-3" name="sec-4-2-3"></a>

-   elasticsearchは以下のバッチファイルを起動  
    `elasticsearch-6.3.0\bin\elasticsearch.bat`
-   kibanaは以下のバッチファイルを起動  
    `kibana-6.3.0-windows\bin\kibana`

今回は以下のようなバッチファイルを作成して1つにまとめる  

    start elasticsearch-6.3.0\bin\elasticsearch
    start kibana-6.3.0-windows\bin\kibana

### 起動しているか確認<a id="sec-4-2-4" name="sec-4-2-4"></a>

-   elasticsearch  
    <http://localhost:9200/>  
    以上のURLにアクセスしてversion情報とかが入ったJSONが出力されればOK
-   kibana  
    <http://localhost:5601/>  
    以上のURLにアクセスして画面が表示されればOK

## curl<a id="sec-4-3" name="sec-4-3"></a>

今回はすでに入っていたものを使用(買ったばっかのWindows機なのになんで？)  

    curl 7.55.1 (Windows) libcurl/7.55.1 WinSSL
    Release-Date: [unreleased]
    Protocols: dict file ftp ftps http https imap imaps pop3 pop3s smtp smtps telnet tftp
    Features: AsynchDNS IPv6 Largefile SSPI Kerberos SPNEGO NTLM SSL

# データ挿入<a id="sec-5" name="sec-5"></a>

## index作成<a id="sec-5-1" name="sec-5-1"></a>

まずはindexを作成(MySQLで言うところのデータベース)
`?pretty` をつけると改行付きのjsonが帰ってくる

    curl -XPUT "localhost:9200/electric_power?pretty"

indexが作られているか確認  

    curl -XGET "localhost:9200/_cat/indices?v"

## json作成<a id="sec-5-2" name="sec-5-2"></a>

elasticsearchのBulkAPIで読み込むには以下のようにデータを2行1組のjsonへ変換する  
IDを指定しない場合は適当な一意のIDが付与される。  

    { "index" : { "_index" : "(挿入したいindex)", "_type" : "(挿入したいtype)", "_id" : "(一意のid)" } }
    { (実データのjson) }

今回はpythonを使用してjsonに変換  

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    
    import csv
    import json
    
    PATH = './dataset.txt'
    JSON_BASE_NAME = './dataset'
    
    
    def main():
        with open(PATH, mode='r', encoding='utf-8', newline='') as f:
            dialect = csv.Sniffer().sniff(f.readline(), delimiters=';')
            f.seek(0)
            reader = csv.reader(f, dialect)
            header = reader.__next__()
            header[0] = "datetime"
    
            file_num = 0
            writer = open(JSON_BASE_NAME + '_' + str(file_num) + '.json', mode='w', encoding='utf-8')
            line_num = 0
            for line in reader:
                line_num = line_num + 1
                writer.writelines(json.dumps({'index': {'_index': 'electric_power', '_type': 'portugal'}}))
                writer.writelines('\n')
                line_dict = make_line_dict(header, line)
                writer.writelines(json.dumps(line_dict))
                writer.writelines('\n')
    
                # でかいのでファイル分割
                if line_num >= 1000:
                    line_num = 0
                    file_num = file_num + 1
                    writer.close()
                    writer = open(JSON_BASE_NAME + '_' + str(file_num) + '.json', mode='w', encoding='utf-8')
    
        writer.writelines('\n')
        writer.close()
    
    
    def make_line_dict(header, line):
        line = list(map(lambda l: l.replace(',', '.'), line))
        line[1:] = list(map(lambda val: float(val), line[1:]))
        line_dict = dict(zip(header, line))
        return line_dict
    
    
    if __name__ == '__main__':
        main()

## elasticsearchに投入<a id="sec-5-3" name="sec-5-3"></a>

変換したjsonをBulkAPIで投入。(データが大きすぎてcurlが動かなかったので分割)

ちなみにElasticsearch6系からContent-Typeヘッダが必須のようです。
私はここでハマりました。 <del>(半ギレ)</del>  

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    
    import os
    
    # ファイルの個数
    FILE_NUM = 141
    
    
    def main():
        for i in range(FILE_NUM):
            cmd = 'curl --noproxy localhost -H "Content-Type: application/json" ' \
                  '-XPUT localhost:9200/_bulk --data-binary @dataset_' + str(i) + '.json'
            print(cmd)
            os.system(cmd)
    
    
    if __name__ == '__main__':
        main()

以下にアクセスすると入れたデータが確認できる。
<http://localhost:9200/electric_power/_search?pretty>  

# データ可視化<a id="sec-6" name="sec-6"></a>

データはelasticsearchに入れたので表示ができるはず
<http://localhost:5601/> にアクセスするとkibanaのホーム画面が表示されるので
左側メニューの `visualize` メニューをクリックして
index patternに `electric_power` と入力し `Next Step` ボタンを押せば
なんかグラフができるはず
…と思っていたがなんかうまく行かない。 <del>(キレそう)</del>  

## マッピングの確認<a id="sec-6-1" name="sec-6-1"></a>

elasticsearchは何もしなくてもフィールドの型を定義してくれるらしいので
以下のURLにアクセスして、デフォルトの型を確認する。
<http://localhost:9200/electric_power/_mapping?pretty>  

    ～ 略 ～
    
    "datetime" : {
                "type" : "text",
                "fields" : {
                  "keyword" : {
                    "type" : "keyword",
                    "ignore_above" : 256
                  }
                }
              }
    
    ～ 略 ～

どうも、デフォルトだと時刻フィールドがtextとして扱われているのが原因のよう  

## マッピングの変更<a id="sec-6-2" name="sec-6-2"></a>

時刻として扱って欲しいので、自動生成されたマッピングのdatetimeの定義を以下のように修正  

    {
        "mappings" : {
            "portugal" : {
                "properties" : {
                    "MT_001" : {
                        "type" : "float"
                    },
                    "MT_002" : {
                        "type" : "float"
                    },
    
    ～ 略 ～
    
                    "datetime" : {
                        "type" : "date",
                        "format" : "YYYY-MM-dd HH:mm:ss"
                    }
                }
            }
        }
    }

以下のコマンドで定義したマッピングを登録  

    curl -XDELETE "localhost:9200/*"
    curl -H "Content-Type: application/json" -XPUT localhost:9200/electric_power --data-binary @mapping.json

再びアクセスして意図した定義になっているか確認
<http://localhost:9200/electric_power/_mapping?pretty>  

## 今度こそデータの可視化<a id="sec-6-3" name="sec-6-3"></a>

もう一度データをelasticsearchへ投入
<http://localhost:5601/> にアクセスするとkibanaのホーム画面が表示されるので
左側メニューの `Discover` メニューをクリックして
`index pattern` に `electric_power` と入力し
`Time Filter field name` に `datetime` を選択して `Next Step` ボタンをクリック
再び `Discover` メニューをクリックで…

![グラフ.jpg](https://qiita-image-store.s3.amazonaws.com/0/175446/73e66494-14c8-1f62-0eca-db073cfdab9d.jpeg)

やった～～～～:sushi::sushi::sushi:

# おわり<a id="sec-7" name="sec-7"></a>

-   とりあえず可視化はできたので一旦終わり
-   どんなグラフがあるかはまたあとで覚える

# おまけ<a id="sec-8" name="sec-8"></a>

-   indexを削除するときはDELETEすればOK  
    
        curl -XDELETE "localhost:9200/*"
-   proxyサーバーがある環境の場合は `--noproxy localhost` で動作するかも

# 参考<a id="sec-9" name="sec-9"></a>

以下の記事を見ながら作業してました。記事ありがとう！  

<https://qiita.com/kws9/items/7695262be0befb94897f>
<https://qiita.com/hiyuzawa/items/bad1a7e29fc8d1820bea>