ようへいの日々精進XP

よかろうもん

Elasticsearch 検索周りTutorial (1)

tl;dr

Elasticsearch にログを放り込むだけであれば何度かやってきたけど, 検索周りは殆ど取り組めていなかったので, ちょっとずつだけど時間を設けてチュートリアルしていこうと思います.

今回はとりあえず docker-compose を使って Elasticsearch と Kibana を起動して検索するデータを放り込むところまで.

各種コードやメモ等は以下のリポジトリに放り込んでいきたいと思います.

github.com

Elasticsearch と Kibana を起動する

なんの変哲もない docker-compose.yml

version: '3.7'

services:
  elasticsearch:
    build:
      context: .
      dockerfile: Dockerfile.elasticsearch
    container_name: elasticsearch
    environment:
      cluster.name: docker-cluster
      ES_JAVA_OPTS: "-Xms1024m -Xmx1024m"
      bootstrap.system_call_filter: "false"
      transport.host: localhost
    ulimits:
      memlock:
        soft: -1
        hard: -1
    ports:
      - 9200:9200
  kibana:
    build:
      context: .
      dockerfile: Dockerfile.kibana
    container_name: kibana
    ports:
      - 5601:5601

以下のように実行するだけです. 簡単ですね.

$ docker-compose up -d

気をつけないといけないのは, JVM のヒープサイズ. 起動や検索だけであれば, 1024m も確保する必要はありませんが, 後述のデータを放り込む際に小さいヒープサイズだとエラーになってしまいます.

データを放り込む

何か良い題材はないのかな

検索を試すためには何かしらの題材がほしいところですが, 今回は Abema TV の番組表が JSON で取得出来ることが判ったので, 番組表 JSON データを利用させていただくことにしました. JSON データの取得については, 以下の記事を参考にさせて頂きました.

nsdev.jp

ちなみに, なぜ, Abema TV 番組表にたどり着いたのか. それは, 今, 話題のドラマである「M 愛すべき人がいて」を見ていたら, Abema TV にたどりついて, Abema TV の番組表が良い感じで提供されていることから, これはもしや... と思い調査したところ, 番組表 JSON データにたどり着いた次第です.

www.tv-asahi.co.jp

abema.tv

bulk API で雑に放り込む

以下のような Ruby スクリプトを利用して番組表 JSON を Elasticsearch に放り込みました.

require "net/http"
require "uri"
require "json"

class AbemaTvSchedule
  ENDPOINT = "http://localhost:9200"

  def initialize(date, slots = [])
    @date = date
    @slots = slots
  end

  def post_index
    uri = URI.parse("#{ENDPOINT}/_bulk")
    http = Net::HTTP.new(uri.host, uri.port)
    req = Net::HTTP::Post.new(uri.request_uri)
    req["Content-Type"] = "application/json"
    req.body = bulk_data
    res = http.request(req)
    puts "#{res.code} #{res.body}"
  end

  private

  def bulk_data
    docs = ''
    @slots.each do |slot|
      doc_id = slot['id']
      doc = <<~EOS
      {"index":{"_id":"#{doc_id}","_index":"abema-channel-#{@date}","_type":"_doc"}}
      #{JSON.dump(slot)}
      EOS
      docs << doc
    end
    docs
  end
end

hash = JSON.parse File.read('abema-tv1.json')
hash.each do |c|
  next if c['slots'].empty?
  AbemaTvSchedule.new(c['date'], c['slots']).post_index
end

番組表 JSON ファイルは以下のような内容で構成されています (抜粋).

[
  {
    "channelId": "abema-news",
    "date": "20200621",
    "slots": [
      {
        "id": "CwYrZm89oahExf",
        "title": "ABEMA NEWS",
        "startAt": 1592661600,
        "endAt": 1592668800,
        "programs": [
          {
            "id": "89-107_s0_p328",
            "episode": {
              "sequence": 328
            },
            "credit": {
              "casts": [
                "ABEMAキャスター"
              ],
...

以下のようにスクリプトを実行すれば, Elasticsearch にデータのインポートが完了します.

$ ruby import-json.rb

当初は 1 件ずつ登録するような実装にしていましたが, 時間も掛かるし, ヒープサイズのエラーに悩まされました. Bulk API を利用することで, 処理時間は一気に短縮されました.

試しに検索

あの「M 愛すべき人がいて」を検索してみます. みんな大好き Dev Tools

f:id:inokara:20200621225232p:plain

更に, 「オンライン音楽祭【LIVE HUMAN 2020】Day 2」を検索してみます.

abema.tv

Kibana の Discover で検索してみます.

f:id:inokara:20200621225937p:plain

いい感じですが, 特にマッピングやアナライザ等の設定を行っていないため, より高度な検索を行おうとすると, 意図しない結果がヒットしたりするのではと考えています.

以上

ここから各種設定を行いながら, 番組タイトルと出演者をキーにして検索したり, より精度の高い検索結果が得られるように取り組んでいきたいと思います.

参考

www.elastic.co

qiita.com