ElasticsearchにWikipediaのドキュメントをインデキシングする

Elasticsearch

Wikipedia を題材に自分で Elasticsearch 用にドキュメントを用意し、インデキシングしてみました。
Elasticsearch の構築については別の記事として投稿する予定です。

スポンサーリンク

ゴール

  • Elasticsearch にインデキシングできるドキュメントを用意する
  • Elasticsearch でインデックスを作成する
  • Elasticsearch にインデキシングする

Elasticsearch にインデキシングできるドキュメントを用意する

Wikipedia からアーカイブを取得する

Wikipedia からアーカイブをダウンロードします。
https://dumps.wikimedia.org/jawiki/
jawiki-latest-pages-articles.xml.bz2 をダウンロードしました。

.xml.bz2 -> json

このままだとインデキシングできないのでアーカイブを 100M 以下の json ファイルに分割します(Elasticsearch では 100M 以上のファイルをインデキシングできない)。
https://github.com/attardi/wikiextractor

$ sudo apt install -y python
$ cd ~
$ git clone https://github.com/attardi/wikiextractor
$ wikiextractor/WikiExtractor.py -b 80M -o extracted jawiki-latest-pages-articles.xml.bz2 --json
$ du extracted/AA -h
2.8 G
# 念のため ndjson ファイルのバックアップを取っておく
$ cp extracted/AA extracted/AA.bak

これで細切れになった json ファイルができました(正確には ndjson)。
サイズは 2.8 GB となりました。

{"url": "https://ja.wikipedia.org/wiki?curid=5", "text": "アンパサンド\n\nアンパサンド (&、英語名:) とは...", "id": "5", "title": "アンパサンド"}
{"url": "https://ja.wikipedia.org/wiki?curid=10", "text": "言語\n\nこの記事では言語(げんご)、特に...", "id": "10", "title": "言語"}
{"url": "https://ja.wikipedia.org/wiki?curid=11", "text": "日本語\n\n日本語(にほんご、にっぽんご)は...", "id": "11", "title": "日本語"}

ですが、このままだと Bulk インデキシングできないのでひと手間加えます。
https://www.elastic.co/guide/en/elasticsearch/reference/7.1/docs-bulk.html
元のファイルの各行に action を指定する必要があります。

{"index":{}}
{"url": "https://ja.wikipedia.org/wiki?curid=5", "text": "アンパサンド\n\nアンパサンド (&、英語名:) とは...", "id": "5", "title": "アンパサンド"}
{"index":{}}
{"url": "https://ja.wikipedia.org/wiki?curid=10", "text": "言語\n\nこの記事では言語(げんご)、特に...", "id": "10", "title": "言語"}
{"index":{}}
{"url": "https://ja.wikipedia.org/wiki?curid=11", "text": "日本語\n\n日本語(にほんご、にっぽんご)は...", "id": "11", "title": "日本語"}
$ vi add_action_line.py
$ chmod +x ./add_action_line.py
# 並列で処理する
$ ls extracted/AA/* -d | xargs -L 1 -P 10 bash -c './add_action_line.py $0'
$ du extracted/AA -h
2.9 GElasticsearchにWikipediaのドキュメントをインデキシングする

行を追加した分サイズが若干増えました。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys
import datetime

# 引数に処理するjsonファイルのパスを指定すること
args = sys.argv
# ndjsonの各行
new_lines = []

start_datetime = datetime.datetime.now()
print('{}: start converting {}.'.format(start_datetime.strftime('%Y/%m/%d %H:%M:%S'), args[1]))

# ファイルの各行を読み込んで action を追加する
# Bulk API によると以下の形式(Newline Delimited JSON: ndjson)で用意する必要がある
#
# action_and_metadata\n
# optional_source\n
# ...
# action_and_metadata\n
# optional_source\n
with open(args[1]) as f:
    for line in f:
        if(line != '\n'):
            # 空行でなければ action を追加する
            new_lines.append('{"index":{}}\n')
            new_lines.append(line)
        else:
            new_lines.append(line)

# ndjson で上書きする
with open(args[1], mode='w') as f:
    f.writelines(new_lines)

finish_datetime = datetime.datetime.now()
processing_time = finish_datetime - start_datetime
print('{}: {} is converted({} sec).'.format(finish_datetime.strftime('%Y/%m/%d %H:%M:%S'), args[1], processing_time.seconds))

これでようやく Elasticsearch に投入できるドキュメントができました。

Elasticsearch でインデックスを作成する

今回は wiki というインデックスを作成しました。
スキーマ定義は settings.json に記述します。
analyzer や mapping 情報を定義します。

$ curl -H 'Content-Type: application/json' -X PUT 'http://localhost:9200/wiki?pretty' -d @settings.json

{
    "settings": {
        "number_of_shards": 3,
        "number_of_replicas": 0,      
        "analysis": {
            "analyzer": {
                "ngram": {
                    "filter": [
                        "cjk_width",
                        "lowercase"
                    ],
                    "char_filter": [
                        "html_strip"
                    ],
                    "type": "custom",
                    "tokenizer": "ngram"
                }
            },
            "tokenizer": {
                "ngram": {
                    "token_chars": [
                        "letter",
                        "digit"
                    ],
                    "min_gram": "1",
                    "type": "nGram",
                    "max_gram": "2"
                }
            }
        }
    },
    "mappings": {
        "properties": {
            "text": {
                "analyzer": "ngram",
                "type": "text"
            },
            "url": {
                "type": "keyword"
            },
            "id": {
                "type": "keyword"
            },
            "title": {
                "analyzer": "ngram",
                "type": "text"
            }
        }
    }
}

Elasticsearch にインデキシングする

作成したインデックスに Bulk インデキシングします。

$ ls ./extracted/AA/* -d | xargs -L 1 -P 10 bash -c 'echo $0 ; cat $0 | curl -s -X POST -H '\''Content-Type: application/x-ndjson'\'' '\''http://localhost:9200/wiki/_bulk?pretty'\'' --data-binary @-;'

インデックス情報

117 万件のドキュメントが登録されました。
インデックスのサイズは 5.84 GB になりました。
加工したドキュメントのサイズは 2.9 GB だったのでおよそ 2 倍に増えました(この増分は Elasticsearch で転置インデックスを作成したことによるものです)。

$ curl -X GET 'http://localhost:9200/wiki/_stats?pretty' -s | jq '{ count: .indices.wiki.primaries.docs.count, size: .indices.wiki.primaries.store.size_in_bytes }'
{
  "count": 1175109,
  "size": 6272333867
}

検索

「全文検索エンジン Elasticsearch」で検索してみるとたった 40 ms でそれらしきドキュメントがヒットしました。
以上で自分でドキュメントを用意して Elasticsearch にインデキシングできるようになりました。

$ curl -s -XGET 'http://localhost:9200/wiki/_search?pretty' -d '{"query":{"match":{"text": "全文検索エンジン Elasticsearch"}},"size":1}' -H 'Content-Type: application/json'
{
  "took" : 40,
  "timed_out" : false,
  "_shards" : {
    "total" : 3,
    "successful" : 3,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 10000,
      "relation" : "gte"
    },
    "max_score" : 171.43716,
    "hits" : [
      {
        "_index" : "wiki",
        "_type" : "_doc",
        "_id" : "mcWiYm4BvfCPgCEA1xL2",
        "_score" : 171.43716,
        "_source" : {
          "url" : "https://ja.wikipedia.org/wiki?curid=3479729",
          "text" : "Elasticsearch\n\nElasticsearchはLucene基盤の分散処理マルチテナント対応検索エンジンである。オープンソースソフトウェアだが、現在はオランダ・アムステルダムに本社を置くElastic社が中心になって開発が進められている。なお「Elas
tic Search」といったように間に空白を入れる・「search」の頭を大文字にするといった表記は誤り(ただしVer.1.0.0リリース前にはそのような表記も混在していた)。\n\n全文検索に特化しており、他のソリューションと比較しても圧倒的な全文検索スピードと利
便性を誇る。Elasticsearchの内部ではApache Luceneが提供する超高速全文検索をフル活用しており、スケーラブル,スキーマレス,マルチテナントを特長とする。\n\nJavaで組まれたApacheライセンスのオープンソースソフトウェアであり、商用を含めた検索エン
ジン業界では一番人気(2016年9月現在)とされている。著名な導入例として、 Wikimedia, Facebook, StumbleUpon, Mozilla, アマデウスITグループ, Quora, Foursquare, Etsy, SoundCloud, GitHub, FDA, 欧州原子核研究機構, Stack Exchange, Netflix, Pixabay
,Sophosなどがある。\n",
          "id" : "3479729",
          "title" : "Elasticsearch"
        }
      }
    ]
  }
}

備考

インデックス削除

$ curl -X DELETE 'http://localhost:9200/wiki?pretty'
タイトルとURLをコピーしました