Elasitcsearchとkuromojiを用いての全文検索

Elasticsearchの学習用に、livedoorニュースコーパスデータのIndex登録を行い、登録された日本語ニュース記事に対して検索機能を試してみます。

バージョン情報

macOSバージョン

$ sw_ver
ProductName:    Mac OS X
ProductVersion: 10.15.7
BuildVersion:   19H2

elasticsearchバージョン

  • 7.9.2 ※ 2020年10月時点での最新バージョン

elasticsearch起動

最初にelasticsearchを起動します。
起動するにあたって、docker-composeを用いて進めていきます。

docker-composeの内容はこちら。
今回は1ノードのみ起動するようにしています。(単一ノードクラスタ
また、kibana dev toolsを利用したく、kibanaもあわせて起動します。

docker/elasticsearch/Dockerfile

FROM docker.elastic.co/elasticsearch/elasticsearch:7.9.2

# install elasticsearch plugins
RUN elasticsearch-plugin install analysis-kuromoji
RUN elasticsearch-plugin install analysis-icu
  • kuromojiプラグインもこのタイミングでインストールします
  • インストールするとtext型フィールドに対して、defaultでkuromoji analyzerが適用されるよう。

docker-compose.yaml

version: "3.7"

services:
  elasticsearch:
    build: ./docker/elasticsearch/
    container_name: es01
    environment:
      - cluster.name=docker-cluster
      - bootstrap.memory_lock=true
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
      - "discovery.type=single-node"
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      - esdata01:/usr/share/elasticsearch/data
    ports:
      - 9200:9200
      - 9300:9300

  kibana:
    image: docker.elastic.co/kibana/kibana:7.9.2
    container_name: kibana01
    environment:
      ELASTICSEARCH_URL: http://elasticsearch:9200
    ports:
      - 5601:5601
    depends_on:
      - elasticsearch

volumes:
  esdata01:
    driver: local

elasticsearchとkibana用コンテナを起動します。

$ docker-compose up -d
Creating network "es-livedoor-news-search_default" with the default driver
Building elasticsearch
Step 1/3 : FROM docker.elastic.co/elasticsearch/elasticsearch:7.9.2
 ---> caa7a21ca06e
Step 2/3 : RUN elasticsearch-plugin install analysis-kuromoji
 ---> Running in 97b65d75348b
-> Installing analysis-kuromoji
-> Downloading analysis-kuromoji from elastic
:
:
$ docker-compose ps
  Name                Command               State                       Ports
--------------------------------------------------------------------------------------------------
es01       /tini -- /usr/local/bin/do ...   Up      0.0.0.0:9200->9200/tcp, 0.0.0.0:9300->9300/tcp
kibana01   /usr/local/bin/dumb-init - ...   Up      0.0.0.0:5601->5601/tcp

elasticsearch起動確認

GET /

{
  "name" : "fdfbd5f82bd6",
  "cluster_name" : "docker-cluster",
  "cluster_uuid" : "brwR4DgsTPywnbHpKbciGQ",
  "version" : {
    "number" : "7.9.2",
    "build_flavor" : "default",
    "build_type" : "docker",
    "build_hash" : "d34da0ea4a966c4e49417f2da2f244e3e97b4e6e",
    "build_date" : "2020-09-23T00:45:33.626720Z",
    "build_snapshot" : false,
    "lucene_version" : "8.6.2",
    "minimum_wire_compatibility_version" : "6.8.0",
    "minimum_index_compatibility_version" : "6.0.0-beta1"
  },
  "tagline" : "You Know, for Search"
}

プラグイン確認

GET /_cat/plugins

efd4f7468364 analysis-icu      7.9.2
efd4f7468364 analysis-kuromoji 7.9.2

Indexデータ

Indexデータとして、Livedoorニュースコーパスデータを利用します。
Livedoorニュースコーパスデータは株式会社ロンウイットのダウンロードページよりダウンロード・解凍して利用できる状態にします。

$ curl -OL https://www.rondhuit.com/download/ldcc-20140209.tar.gz
$ ls -lh ldcc-20140209.tar.gz | awk '{print $5}'
30M
$ tar xvzf ldcc-20140209.tar.gz

解凍するとtextディレクトリが作成されます。
textディレクトリの中には以下9カテゴリのディレクトリが存在してます。

$ tree -L 1 text/
text/
├── CHANGES.txt
├── README.txt
├── dokujo-tsushin
├── it-life-hack
├── kaden-channel
├── livedoor-homme
├── movie-enter
├── peachy
├── smax
├── sports-watch
└── topic-news

9 directories, 2 files

Indexing

elasitcsearchにデータ投入するためのコードを作成します。今回はpythonで実装します。 pythonからelasticseachを利用するため、Python Elasticsearch Clientをインストールします。

$ pip install elasticsearch
Collecting elasticsearch
  Downloading elasticsearch-7.9.1-py2.py3-none-any.whl (219 kB)
     |████████████████████████████████| 219 kB 2.8 MB/s
Requirement already satisfied: certifi in ./py3/lib/python3.8/site-packages (from elasticsearch) (2020.6.20)
Requirement already satisfied: urllib3>=1.21.1 in ./py3/lib/python3.8/site-packages (from elasticsearch) (1.25.10)
Installing collected packages: elasticsearch
Successfully installed elasticsearch-7.9.1
$ pip show elasticsearch
Name: elasticsearch
Version: 7.9.1
Summary: Python client for Elasticsearch
Home-page: https://github.com/elastic/elasticsearch-py
Author: Honza Král, Nick Lang
Author-email: honza.kral@gmail.com, nick@nicklang.com
License: Apache-2.0
Location: /Users/kimai/local/python/py3/lib/python3.8/site-packages
Requires: urllib3, certifi
Required-by:

7.9.2は存在していないよう。

$ pip install 'elasticsearch==7.9.2'
ERROR: Could not find a version that satisfies the requirement elasticsearch==7.9.2 (from versions: 0.4.1, 0.4.2, 0.4.3, 0.4.4, 0.4.5, 1.0.0, 1.1.0, 1.1.1, 1.2.0, 1.3.0, 1.4.0, 1.5.0, 1.6.0, 1.7.0, 1.8.0, 1.9.0, 2.0.0, 2.1.0, 2.2.0, 2.3.0, 2.4.0, 2.4.1, 5.0.0, 5.0.1, 5.1.0, 5.2.0, 5.3.0, 5.4.0, 5.5.0, 5.5.1, 5.5.2, 5.5.3, 6.0.0, 6.1.1, 6.2.0, 6.3.0, 6.3.1, 6.4.0, 6.8.0, 6.8.1, 7.0.0, 7.0.1, 7.0.2, 7.0.3, 7.0.4, 7.0.5, 7.1.0, 7.5.1, 7.6.0a1, 7.6.0, 7.7.0a1, 7.7.0a2, 7.7.0, 7.7.1, 7.8.0a1, 7.8.0, 7.8.1, 7.9.0a1, 7.9.0, 7.9.1, 7.10.0a1)
ERROR: No matching distribution found for elasticsearch==7.9.2

elasticsearchへのデータ投入はpythonコードを実行して行います。

$ python src/index.py

正常に実行完了したあとは、kibana dev toolsを用いてインデックスデータの確認を行っていきます。

作成されたインデックスの確認

GET /ldnews

{
  "ldnews": {
    "aliases": {},
    "mappings": {
      "properties": {
        "category": {
          "type": "text",
          "fields": {
            "keyword": {
              "type": "keyword",
              "ignore_above": 256
            }
          }
        },
        "contents": {
          "type": "text",
          "fields": {
            "keyword": {
              "type": "keyword",
              "ignore_above": 256
            }
          }
        },
        "title": {
          "type": "text",
          "fields": {
            "keyword": {
              "type": "keyword",
              "ignore_above": 256
            }
          }
        }
      }
    },
    "settings": {
      "index": {
        "creation_date": "1602655963825",
        "number_of_shards": "1",
        "number_of_replicas": "1",
        "uuid": "mlGuWK47R1SAlrf0S9Cwew",
        "version": {
          "created": "7090299"
        },
        "provided_name": "ldnews"
      }
    }
  }
}

インデックス登録済みの文書数取得

GET /ldnews/_count

{
  "count" : 7367,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  }
}

Search

1件取得してみます。

GET /ldnews/_search?size=1

{
  "took" : 730,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 7367,
      "relation" : "eq"
    },
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "ldnews",
        "_type" : "_doc",
        "_id" : "7e29JXUBiDTGyEjqZ8tv",
        "_score" : 1.0,
        "_source" : {
          "category" : "movie-enter",
          "title" : "movie-enter-6858134.txt",
          "contents" : """http://news.livedoor.com/article/detail/6858134/
2012-08-16T10:00:00+0900
号泣ラブストーリー『100回泣くこと』が関ジャニ∞大倉&桐谷美玲で映画化
 2002年にデビュー作「リレキショ」で第39回文藝賞を受賞し、2004年には「ぐるぐるまわるすべり台」で第26回野間文芸新人賞を受賞するなど話題の絶えない中村航。その彼の55万部発行の号泣ラブストーリー『100回泣くこと』が実写映画化されることが決定した。

 主人公、藤井を演じるのは、満を持して映画単独初主演を務める大倉忠義。今年デビュー8周年を迎える関ジャニ∞主演映画『エイトレンジャー』や秋にはコンサートツアーが控えるなど、今最も勢いのあるグループのメンバーとして活躍するだけでなく、映画デビュー作『大奥』、ドラマ「ヤスコとケンジ」、「三毛猫ホームズの推理」に出演するなど俳優としての実力も高く評価されている。

 ヒロイン、佳美には映画『荒川アンダーザブリッジ』や『逆転裁判』でヒロインを務め、今年10月に『ツナグ』や『新しい靴を買わなくちゃ』の公開を控える若手実力派女優の桐谷美玲。その他、主演舞台「新・幕末純情伝」や情報番組「NEWS ZERO」でキャスターを務めるなど、近年活躍の場を広げることで幅広い世代から支持を受けている。

 監督は、2003年に『ヴァイブレータ』で第25回ヨコハマ映画祭にて監督賞を始め5部門を受賞したほか、40以上の国際映画祭で数々の賞を受賞し、一大センセーションを巻き起こした廣木隆一。『余命一ヶ月の花嫁』『雷桜』『軽蔑』そして、2013年には『きいろいゾウ』の公開が控えるなど、これまで男女の愛の様々な形、心の機微を見つめ続けてきた廣木隆一が、お互いが相手を想うがゆえに揺れ動く、男女の姿を繊細な描写で映し出す。

 脚本は、『ソラニン』などを手がけた高橋泉。本作では、原作にはない設定へとアレンジすることで、より感動的なストーリーを作り上げている。『100回泣くこと』は2013年、全国ロードショー。

藤井役・大倉忠義 コメント
 僕自身、これ程深い恋愛ストーリーは初めての挑戦です。そして、その作品にスクリーンで初主演という形で務めさせていただくことになりました。素直に嬉しいという気持ちとともに、原作・台本を読ませていただき"本当の愛”ということについて、改めて考えさせられました。主人公の繊細な心の移り変わりや葛藤を表現できればと思います。廣木監督、共演者の方々、スタッフの皆さんにお力を借りながら、観に来て下さった方に何か”大切なメッセージ”を伝えられる素敵な作品になるように頑張ります。

佳美役・桐谷美玲 コメント
 はじめて本を読ませて頂いた時、何気ない日常の中にある幸せをとても感じました。私が演じる佳美という役は、病気と必死に闘う姿、彼を一途に思う健気な姿、彼と一緒にすごしているときの可愛らしい姿…どの姿も魅力的なキャラクターだなと感じました。私が感じた魅力をみなさんにもスクリーンを通して伝えられればと思います。また、以前から、廣木監督の作品は好きでよく観ていて、この作品の素敵な世界観をこれから一緒に創り上げていけることが本当に嬉しいし、楽しみです。

監督・廣木隆一 コメント
 悲しい題材ですが永遠になれるように主演の二人の新鮮な組み合わせに期待してます

原作者・中村航 コメント
 真摯でひたむきな大倉さんと桐谷さんに以前から注目していました。お二人を通した『100回泣くこと』を心から楽しみにしています。

『100回泣くこと』ストーリー
4年前のバイク事故で記憶の一部を失った藤井(大倉忠義)は、友人の結婚式で佳美(桐谷美玲)に出会う。佳美に運命を感じた藤井は付き合いだしてまだ日が浅いうちに、「結婚しよう」と告げる。佳美は、1年間(結婚の)練習をしようと応えた。幸せがこのままずっと続くと思っていた藤井と佳美…。しかし、佳美に病魔が忍び寄る。なぜ、佳美は1年と伝えたのか?二人の本当の出会いとは?藤井の失われた記憶が明らかになったとき、私たちはあまりにも切なく、そして美しいラブストーリーを目撃する。

・100回泣くこと - 公式サイト
"""
        }
      }
    ]
  }
}

「Sports Watch」カテゴリの「キャスター」という文字列を含んだ記事を検索すべく、And検索を試してみます。

GET /ldnews/_search?size=1
{
  "query": {
    "bool": {
      "must": [
        { "match": { "contents": "キャスター" } },
        { "match": { "category": "sports-watch" } }
      ]
    }
  }
}


{
  "took" : 26,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 416,
      "relation" : "eq"
    },
    "max_score" : 14.871006,
    "hits" : [
      {
        "_index" : "ldnews",
        "_type" : "_doc",
        "_id" : "he2-JXUBiDTGyEjqFN7R",
        "_score" : 14.871006,
        "_source" : {
          "category" : "sports-watch",
          "title" : "sports-watch-6816328.txt",
          "contents" : """http://news.livedoor.com/article/detail/6816328/
2012-08-02T12:30:00+0900
小倉智昭キャスター、柔道に苦言「試合がつまらない」
2日、フジテレビ「とくダネ!」では、イギリスの地でロンドン五輪の現地取材を行ってる小倉智昭キャスターが、ここまで行われてた柔道の試合について苦言を述べた。

解説を務める、アテネ五輪柔道金メダリスト・鈴木桂治氏に対し、「アテネの頃と比べて、柔道場に朝から晩までいると試合がつまらないんですよ。本当に一本が数えるほどしかないんだよね。あれって、そのままでいいんだろうか、講道館柔道は?」と訴えかけた小倉キャスター。

鈴木氏は「海外の選手が日本人に対して、マークが厳しくなったり、柔道スタイルを変えてでも勝ちにこだわるっていうスタイルが増えていますので、日本は日本のスタイルを貫くのと同時に勝つことも考えなきゃいけないと思います」と返答したが、小倉キャスターは「指導2本で有効で、それだけで勝ち負けが決まっちゃうというのも解せないですよね」と続け、不満を抱いている様子をうかがわせた。
"""
        }
      }
    ]
  }
}

「キャス」だと検索にヒットしないことを確認。

GET /ldnews/_search?size=1
{
  "query": {
    "bool": {
      "must": [
        { "match": { "contents": "キャス" } },
        { "match": { "category": "sports-watch" } }
      ]
    }
  }
}

{
  "took" : 3,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 0,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [ ]
  }
}

Aggregation

Aggregation機能を用いて、9カテゴリの各カテゴリの件数を出してみます。

GET /ldnews/_search?size=0
{
  "aggs": {
    "category_aggs": {
      "terms": {
        "field": "category.keyword"
      } 
    }
  }
}

{
  "took" : 6,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 10000,
      "relation" : "gte"
    },
    "max_score" : null,
    "hits" : [ ]
  },
  "aggregations" : {
    "category_aggs" : {
      "doc_count_error_upper_bound" : 0,
      "sum_other_doc_count" : 0,
      "buckets" : [
        {
          "key" : "sports-watch",
          "doc_count" : 1800
        },
        {
          "key" : "dokujo-tsushin",
          "doc_count" : 1740
        },
        {
          "key" : "it-life-hack",
          "doc_count" : 1740
        },
        {
          "key" : "movie-enter",
          "doc_count" : 1740
        },
        {
          "key" : "smax",
          "doc_count" : 1740
        },
        {
          "key" : "kaden-channel",
          "doc_count" : 1728
        },
        {
          "key" : "peachy",
          "doc_count" : 1684
        },
        {
          "key" : "topic-news",
          "doc_count" : 1540
        },
        {
          "key" : "livedoor-homme",
          "doc_count" : 1022
        }
      ]
    }
  }
}

まとめ

日本語記事に対しての基本的な検索機能を試すまでの流れを試すことが出来ました。 この後は、他の機能を試したり、↑の機能を深ぼっりしていきます。