Elasticsearchのマッピング

実際に取得した四季報データを投入するにあたり、Elasticsearch側で必要な設定をしていきます。

まずElasticsearchですが、以下のような仕組みとなっているそうです。

  • RDB: Databases => Tables => Rows => Columns
  • Elasticsearch: Indices => Types => Documents => Fields

Developers.IOのブログが非常に参考になりました。

第1回 Elastisearch 入門 インデックスを設計する際に知っておくべき事 | Developers.IO

今回投入するデータは四季報データの1種類だけなので、特段に考慮する部分はないのですが、マッピング定義は手動で実施する必要がありました。マッピング定義とは、RDBにおけるテーブル定義にあたる部分で、投入するデータのフィールドの型や利用するアナライザー、またどのようにElasticsearchへインデクシングさせるか、といった点を定義したものとなります。Elasticsearchはデータを取り込むと自動でマッピングを作成してくれますが、原則手動でマッピング定義は作成すべきみたいです。

以下が今回取り込む四季報のデータです。

“1301”,“作成日:2016年09月16日”,“1301 (株)極洋 きょくよう [水産・農林業]”,“【URL】 http://www.kyokuyo.co.jp/”,“【決算】3月”,“【設立】1937.9”,“【上場】1949.5”,“【特色】水産品の貿易、加工、買い付け主力。すしネタに強み。加工食品は業務用が軸。海外加工比率高い”,“【連結事業】水産商事50(1)、冷凍食品30(0)、常温食品8(2)、物流サービス1(5)、鰹・鮪11(1)、他0(8) <16・3>”,“”,“”,“”,“【大幅増益】冷凍・常温食品は拡大、塩釜新工場本格稼働が奇与。水産商事はサケ・マスなど魚価が安定、加工品が拡大、米国市場も堅調。冷蔵運搬船から撤退し物流効率化。新工場償却負担増でも営業増益。”,“【養殖】完全養殖のクロマグロは18年初に初出荷へ、天然種苗の確保にも全力。業務用冷食では、すし向けなど商品群の拡充を図る。9月末基準日で10株を1株に併合。”,“【業種】 食品 時価総額順位 56/172社”,“【仕入先】KAMEC”,“【販売先】三菱食品”,“【比較会社】1333 マルハニチ,2875 東洋水産,1332 日本水産”,“【本社】107-0052東京都港区赤坂3-3-5TEL03-5545-0701”,“【支社】大阪TEL06-6315-1251,東京,福岡,他”,“【従業員】<16.3>連2,249名 単599名(39.8歳)[年]661万円”,“【証券】[上]東京[幹]日興,野村,大和[名]三菱U信[監]井上”,“【銀行】りそな,農中,三井住友信,三菱U信,三菱U”,“【連結】極洋水産,極洋商事,極洋食品”,“”,“【四半期進捗率】3期平均19.6% 今期13.3%(-6.3%)”,“33702” <

それに合致したマッピング定義として以下を準備しました。

{
  "settings": {
    "index": {
      "number_of_shards": "1",
      "number_of_replicas": "0"
    },
    "analysis": {
      "tokenizer": {
        "kuromoji": {
          "type": "kuromoji_tokenizer",
          "mode": "search"
        }
      },
      "analyzer": {
        "kuromoji": {
          "type": "custom",
          "tokenizer": "kuromoji",
          "filter": [
            "kuromoji_baseform",
            "kuromoji_part_of_speech"
          ]
        }
      }
    }
  },
  "mappings": {
    "shikiho": {
      "properties": {
        "code": {
          "type": "short",
          "store": "true"
        },
        "creadtedDate": {
          "type": "string",
          "analyzer": "kuromoji",
          "store": "true"
        },
        "meigaraName": {
          "type": "string",
          "analyzer": "kuromoji",
          "store": "true"
        },
        "url": {
          "type": "string",
          "analyzer": "kuromoji"
        },
        "accountingPeriod": {
          "type": "string",
          "analyzer": "kuromoji"
        },
        "establishmentDate": {
          "type": "string",
          "analyzer": "kuromoji"
        },
        "listingDate": {
          "type": "string",
          "analyzer": "kuromoji"
        },
        "feature": {
          "type": "string",
          "analyzer": "kuromoji"
        },
        "business": {
          "type": "string",
          "analyzer": "kuromoji"
        },
        "assets": {
          "type": "string",
          "analyzer": "kuromoji"
        },
        "finance": {
          "type": "string",
          "analyzer": "kuromoji"
        },
        "nullColumn1": {
          "type": "string",
          "analyzer": "kuromoji"
        },
        "comment1": {
          "type": "string",
          "analyzer": "kuromoji",
          "store": "true"
        },
        "comment2": {
          "type": "string",
          "analyzer": "kuromoji",
          "store": "true"
        },
        "industryType": {
          "type": "string",
          "analyzer": "kuromoji"
        },
        "vendor": {
          "type": "string",
          "analyzer": "kuromoji"
        },
        "customer": {
          "type": "string",
          "analyzer": "kuromoji"
        },
        "rival": {
          "type": "string",
          "analyzer": "kuromoji",
          "store": "true"
        },
        "headOffice": {
          "type": "string",
          "analyzer": "kuromoji"
        },
        "branchOffice": {
          "type": "string",
          "analyzer": "kuromoji"
        },
        "workerNumber": {
          "type": "string",
          "analyzer": "kuromoji"
        },
        "security": {
          "type": "string",
          "analyzer": "kuromoji"
        },
        "bank": {
          "type": "string",
          "analyzer": "kuromoji"
        },
        "affiliate": {
          "type": "string",
          "analyzer": "kuromoji"
        },
        "nullColumn2": {
          "type": "string",
          "analyzer": "kuromoji"
        },
        "progress": {
          "type": "string",
          "analyzer": "kuromoji"
        },
        "holderNumber": {
          "type": "integer",
          "store": "true"
        }
      }
    }
  }
}

mappingの定義と併せて、indexの作成とanalyzerの設定の設定値についても記載しています。

indexの定義ではshardの数を1に、replicaの数を0に設定しています。shardとはElasticsearchサーバ上の、物理的なデータの固まり。デフォルトでは5つに設定されており、通常はこの5つの固まりにindexのデータが分散して格納されて、shard数の単位で平行で処理されることになる、のかな。indexを作成後はshardの数を変更できないのが運用する上では注意点でしょうか。今回は大したデータ量を投入しないので1で設定。replicaについては、その名の通り複製を作成する設定。複数ノードでクラスタを組んでいる場合、自動的に複製を作成してくれるらしいです。なおデフォルトでは1に設定されるため、シングル構成のElasticsearchを構成すると、indexのステータスがyellowになって作成されてしまいます。そのため0に設定。

analyzerについては、以前の投稿を参考。

ElasticsearchのAnalyzerについて - goodbyegangsterのブログ

mappingに関するパラメータは多数あり、正直かなり奥深いです。「どういったデータを取り込むのか」という点と、「どういった検索を実施するのか」という点の双方を考慮して定義を検討する必要があり、ある程度の経験が必要な部分だなと感じました。今回はかなりざっくり設定しています。

パラメータに関しては以下公式を参考。たくさんあります。。。

Mapping | Elasticsearch Reference [2.4] | Elastic

定義情報をmapping.jsonというファイルに記載して、以下コマンドを実行。

# curl -XPOST 'http://localhost:9200/shikiho' -d @mapping.json
{"acknowledged":true}

以下のコマンドで確認できます。

# curl 'http://localhost:9200/_cat/indices?v'
health status index   pri rep docs.count docs.deleted store.size pri.store.size
green  open   shikiho   1   0          0            0       130b           130b
# curl 'http://localhost:9200/shikiho?'
{
  "shikiho": {
    "aliases": {},
    "mappings": {
      "shikiho": {
        "properties": {
          "accountingPeriod": {
            "type": "string",
            "analyzer": "kuromoji"
          },
          "affiliate": {
            "type": "string",
            "analyzer": "kuromoji"
          },
          "assets": {
            "type": "string",
            "analyzer": "kuromoji"
          },
          "bank": {
            "type": "string",
            "analyzer": "kuromoji"
          },
          "branchOffice": {
            "type": "string",
            "analyzer": "kuromoji"
          },
          "business": {
            "type": "string",
            "analyzer": "kuromoji"
          },
          "code": {
            "type": "short",
            "store": true
          },
          "comment1": {
            "type": "string",
            "store": true,
            "analyzer": "kuromoji"
          },
          "comment2": {
            "type": "string",
            "store": true,
            "analyzer": "kuromoji"
          },
          "creadtedDate": {
            "type": "string",
            "store": true,
            "analyzer": "kuromoji"
          },
          "customer": {
            "type": "string",
            "analyzer": "kuromoji"
          },
          "establishmentDate": {
            "type": "string",
            "analyzer": "kuromoji"
          },
          "feature": {
            "type": "string",
            "analyzer": "kuromoji"
          },
          "finance": {
            "type": "string",
            "analyzer": "kuromoji"
          },
          "headOffice": {
            "type": "string",
            "analyzer": "kuromoji"
          },
          "holderNumber": {
            "type": "integer",
            "store": true
          },
          "industryType": {
            "type": "string",
            "analyzer": "kuromoji"
          },
          "listingDate": {
            "type": "string",
            "analyzer": "kuromoji"
          },
          "meigaraName": {
            "type": "string",
            "store": true,
            "analyzer": "kuromoji"
          },
          "nullColumn1": {
            "type": "string",
            "analyzer": "kuromoji"
          },
          "nullColumn2": {
            "type": "string",
            "analyzer": "kuromoji"
          },
          "progress": {
            "type": "string",
            "analyzer": "kuromoji"
          },
          "rival": {
            "type": "string",
            "store": true,
            "analyzer": "kuromoji"
          },
          "security": {
            "type": "string",
            "analyzer": "kuromoji"
          },
          "url": {
            "type": "string",
            "analyzer": "kuromoji"
          },
          "vendor": {
            "type": "string",
            "analyzer": "kuromoji"
          },
          "workerNumber": {
            "type": "string",
            "analyzer": "kuromoji"
          }
        }
      }
    },
    "settings": {
      "index": {
        "creation_date": "1476773695407",
        "analysis": {
          "analyzer": {
            "kuromoji": {
              "filter": [
                "kuromoji_baseform",
                "kuromoji_part_of_speech"
              ],
              "type": "custom",
              "tokenizer": "kuromoji"
            }
          },
          "tokenizer": {
            "kuromoji": {
              "mode": "search",
              "type": "kuromoji_tokenizer"
            }
          }
        },
        "number_of_shards": "1",
        "number_of_replicas": "0",
        "uuid": "-aDPzQlGS_O5OsbthcMDLA",
        "version": {
          "created": "2040199"
        }
      }
    },
    "warmers": {}
  }
}