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'