言語処理100本ノック 2015の挑戦記録です。環境はUbuntu 16.04 LTS + Python 3.5.2 :: Anaconda 4.1.1 (64-bit)です。過去のノックの一覧はこちらからどうぞ。
第7章: データベース
artist.json.gzは,オープンな音楽データベースMusicBrainzの中で,アーティストに関するものをJSON形式に変換し,gzip形式で圧縮したファイルである.このファイルには,1アーティストに関する情報が1行にJSON形式で格納されている.JSON形式の概要は以下の通りである.
フィールド 型 内容 例 id ユニーク識別子 整数 20660 gid グローバル識別子 文字列 "ecf9f3a3-35e9-4c58-acaa-e707fba45060" name アーティスト名 文字列 "Oasis" sort_name アーティスト名(辞書順整列用) 文字列 "Oasis" area 活動場所 文字列 "United Kingdom" aliases 別名 辞書オブジェクトのリスト aliases[].name 別名 文字列 "オアシス" aliases[].sort_name 別名(整列用) 文字列 "オアシス" begin 活動開始日 辞書 begin.year 活動開始年 整数 1991 begin.month 活動開始月 整数 begin.date 活動開始日 整数 end 活動終了日 辞書 end.year 活動終了年 整数 2009 end.month 活動終了月 整数 8 end.date 活動終了日 整数 28 tags タグ 辞書オブジェクトのリスト tags[].count タグ付けされた回数 整数 1 tags[].value タグ内容 文字列 "rock" rating レーティング 辞書オブジェクト rating.count レーティングの投票数 整数 13 rating.value レーティングの値(平均値) 整数 86 artist.json.gzのデータをKey-Value-Store (KVS) およびドキュメント志向型データベースに格納・検索することを考える.KVSとしては,LevelDB,Redis,KyotoCabinet等を用いよ.ドキュメント志向型データベースとして,MongoDBを採用したが,CouchDBやRethinkDB等を用いてもよい.
###64. MongoDBの構築
アーティスト情報(artist.json.gz)をデータベースに登録せよ.さらに,次のフィールドでインデックスを作成せよ: name, aliases.name, tags.value, rating.value
####出来上がったコード:
# coding: utf-8
import gzip
import json
import pymongo
from pymongo import MongoClient
fname = 'artist.json.gz'
unit_bulk = 10000 # バルクインサートする単位(件)
# MongoDBのデータベースtestdbにコレクションartistを作成
client = MongoClient()
db = client.testdb
collection = db.artist
# gzファイル読み込み
with gzip.open(fname, 'rt') as data_file:
# 1行ずつパースしてbufに詰め込む
buf = []
for i, line in enumerate(data_file, 1):
data_json = json.loads(line)
buf.append(data_json)
# unit_bulk件たまったらartistへバルクインサート
if i % unit_bulk == 0:
collection.insert_many(buf)
buf = []
print('{}件追加完了'.format(i))
# 最後のunit_bulkに入らなかった半端分の追加
if len(buf) > 0:
collection.insert_many(buf)
print('{}件追加完了'.format(i))
# インデックス作成
collection.create_index([('name', pymongo.ASCENDING)])
collection.create_index([('aliases.name', pymongo.ASCENDING)])
collection.create_index([('tags.value', pymongo.ASCENDING)])
collection.create_index([('rating.value', pymongo.ASCENDING)])
####実行結果:
10000件追加完了
20000件追加完了
30000件追加完了
40000件追加完了
50000件追加完了
(中略)
890000件追加完了
900000件追加完了
910000件追加完了
920000件追加完了
921337件追加完了
###MongoDBとは
MongoDBはドキュメント指向データベースと呼ばれるもので、今回の問題で使う音楽データベースのような構造を持ったデータを放り込んで管理できるのが特徴のようです。オフィシャルサイトはこちらです。残念ながら英語ですが、MongoDBの概要だけは日本語がありました。他にもMongoDBの解説はネットにたくさんありますので、詳細はググってみてください。
なお、MongoDBには有償版もあるようですが、今回は無償のCommunity Editionを使っています。
###MongoDBのインストール
condaで一発かと思ったのですが、サービスのスクリプトを書かないといけない?だかでちょっと面倒のようなので、今回はオフィシャルサイトの手順でインストールしました。
私が使っているUbuntu 16.04 LTSへのインストール手順はInstall MongoDB Community Edition on Ubuntuにあります。英語ですがそれほど難しくはありません。Install MongoDB Community Editionの通りにやっていくとインストールできました。
segavvy@ubuntu:~$ sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 0C49F3730359A14518585931BC711F9BA15703C6
[sudo] segavvy のパスワード:
Executing: /tmp/tmp.8h93YVjQSG/gpg.1.sh --keyserver
hkp://keyserver.ubuntu.com:80
--recv
0C49F3730359A14518585931BC711F9BA15703C6
gpg: 鍵A15703C6をhkpからサーバkeyserver.ubuntu.comに要求
gpg: 鍵A15703C6: 公開鍵"MongoDB 3.4 Release Signing Key <packaging@mongodb.com>"をインポートしました
gpg: 処理数の合計: 1
gpg: インポート: 1 (RSA: 1)
segavvy@ubuntu:~$ echo "deb [ arch=amd64,arm64 ] http://repo.mongodb.org/apt/ubuntu xenial/mongodb-org/3.4 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.4.list
deb [ arch=amd64,arm64 ] http://repo.mongodb.org/apt/ubuntu xenial/mongodb-org/3.4 multiverse
segavvy@ubuntu:~$ sudo apt-get update
無視:1 http://repo.mongodb.org/apt/ubuntu xenial/mongodb-org/3.4 InRelease
取得:2 http://repo.mongodb.org/apt/ubuntu xenial/mongodb-org/3.4 Release [3,452 B]
ヒット:3 http://us.archive.ubuntu.com/ubuntu xenial InRelease
取得:4 http://security.ubuntu.com/ubuntu xenial-security InRelease [102 kB]
取得:5 http://us.archive.ubuntu.com/ubuntu xenial-updates InRelease [102 kB]
取得:6 http://repo.mongodb.org/apt/ubuntu xenial/mongodb-org/3.4 Release.gpg [801 B]
取得:7 http://repo.mongodb.org/apt/ubuntu xenial/mongodb-org/3.4/multiverse amd64 Packages [2,778 B]
取得:8 http://us.archive.ubuntu.com/ubuntu xenial-backports InRelease [102 kB]
取得:9 http://repo.mongodb.org/apt/ubuntu xenial/mongodb-org/3.4/multiverse arm64 Packages [2,784 B]
取得:10 http://security.ubuntu.com/ubuntu xenial-security/main amd64 DEP-11 Metadata [68.1 kB]
取得:11 http://us.archive.ubuntu.com/ubuntu xenial-updates/main amd64 DEP-11 Metadata [303 kB]
取得:12 http://security.ubuntu.com/ubuntu xenial-security/main DEP-11 64x64 Icons [43.0 kB]
取得:13 http://security.ubuntu.com/ubuntu xenial-security/universe amd64 DEP-11 Metadata [19.4 kB]
取得:14 http://security.ubuntu.com/ubuntu xenial-security/universe DEP-11 64x64 Icons [25.6 kB]
取得:15 http://security.ubuntu.com/ubuntu xenial-security/multiverse amd64 DEP-11 Metadata [212 B]
取得:16 http://us.archive.ubuntu.com/ubuntu xenial-updates/main DEP-11 64x64 Icons [179 kB]
取得:17 http://us.archive.ubuntu.com/ubuntu xenial-updates/universe amd64 DEP-11 Metadata [121 kB]
取得:18 http://us.archive.ubuntu.com/ubuntu xenial-updates/universe DEP-11 64x64 Icons [145 kB]
取得:19 http://us.archive.ubuntu.com/ubuntu xenial-updates/multiverse amd64 DEP-11 Metadata [2,520 B]
取得:20 http://us.archive.ubuntu.com/ubuntu xenial-backports/main amd64 DEP-11 Metadata [208 B]
取得:21 http://us.archive.ubuntu.com/ubuntu xenial-backports/universe amd64 DEP-11 Metadata [212 B]
取得:22 http://us.archive.ubuntu.com/ubuntu xenial-backports/multiverse amd64 DEP-11 Metadata [212 B]
ヒット:23 http://archive.ubuntulinux.jp/ubuntu xenial InRelease
無視:24 http://archive.ubuntulinux.jp/ubuntu-ja-non-free xenial InRelease
ヒット:25 http://archive.ubuntulinux.jp/ubuntu-ja-non-free xenial Release
1,223 kB を 15秒 で取得しました (80.2 kB/s)
AppStream cache update completed, but some metadata was ignored due to errors.
パッケージリストを読み込んでいます... 完了
segavvy@ubuntu:~$ sudo apt-get install -y mongodb-org
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています
状態情報を読み取っています... 完了
以下のパッケージが自動でインストールされましたが、もう必要とされていません:
ubuntu-core-launcher
これを削除するには 'sudo apt autoremove' を利用してください。
以下の追加パッケージがインストールされます:
mongodb-org-mongos mongodb-org-server mongodb-org-shell mongodb-org-tools
以下のパッケージが新たにインストールされます:
mongodb-org mongodb-org-mongos mongodb-org-server mongodb-org-shell
mongodb-org-tools
アップグレード: 0 個、新規インストール: 5 個、削除: 0 個、保留: 16 個。
66.6 MB のアーカイブを取得する必要があります。
この操作後に追加で 269 MB のディスク容量が消費されます。
取得:1 http://repo.mongodb.org/apt/ubuntu xenial/mongodb-org/3.4/multiverse amd64 mongodb-org-shell amd64 3.4.1 [7,955 kB]
取得:2 http://repo.mongodb.org/apt/ubuntu xenial/mongodb-org/3.4/multiverse amd64 mongodb-org-server amd64 3.4.1 [14.2 MB]
取得:3 http://repo.mongodb.org/apt/ubuntu xenial/mongodb-org/3.4/multiverse amd64 mongodb-org-mongos amd64 3.4.1 [8,093 kB]
取得:4 http://repo.mongodb.org/apt/ubuntu xenial/mongodb-org/3.4/multiverse amd64 mongodb-org-tools amd64 3.4.1 [36.3 MB]
取得:5 http://repo.mongodb.org/apt/ubuntu xenial/mongodb-org/3.4/multiverse amd64 mongodb-org amd64 3.4.1 [3,564 B]
66.6 MB を 12秒 で取得しました (5,236 kB/s)
以前に未選択のパッケージ mongodb-org-shell を選択しています。
(データベースを読み込んでいます ... 現在 209736 個のファイルとディレクトリがインストールされています。)
.../mongodb-org-shell_3.4.1_amd64.deb を展開する準備をしています ...
mongodb-org-shell (3.4.1) を展開しています...
以前に未選択のパッケージ mongodb-org-server を選択しています。
.../mongodb-org-server_3.4.1_amd64.deb を展開する準備をしています ...
mongodb-org-server (3.4.1) を展開しています...
以前に未選択のパッケージ mongodb-org-mongos を選択しています。
.../mongodb-org-mongos_3.4.1_amd64.deb を展開する準備をしています ...
mongodb-org-mongos (3.4.1) を展開しています...
以前に未選択のパッケージ mongodb-org-tools を選択しています。
.../mongodb-org-tools_3.4.1_amd64.deb を展開する準備をしています ...
mongodb-org-tools (3.4.1) を展開しています...
以前に未選択のパッケージ mongodb-org を選択しています。
.../mongodb-org_3.4.1_amd64.deb を展開する準備をしています ...
mongodb-org (3.4.1) を展開しています...
man-db (2.7.5-1) のトリガを処理しています ...
mongodb-org-shell (3.4.1) を設定しています ...
mongodb-org-server (3.4.1) を設定しています ...
システムユーザー `mongodb' (UID 121) を追加しています...
新しいユーザー `mongodb' (UID 121) をグループ `nogroup' に追加しています...
ホームディレクトリ `/home/mongodb' を作成しません。
グループ `mongodb' (GID 129) を追加しています...
完了。
ユーザー `mongodb' をグループ `mongodb' に追加しています...
ユーザ mongodb をグループ mongodb に追加
完了。
mongodb-org-mongos (3.4.1) を設定しています ...
mongodb-org-tools (3.4.1) を設定しています ...
mongodb-org (3.4.1) を設定しています ...
これでMongoDB本体のインストールができましたので、起動することを確認します。
segavvy@ubuntu:~$ sudo service mongod start
正常に起動したかどうかは、Verify that MongoDB has started successfullyの手順で確認できます。
2017-01-09T22:00:38.186+0900 I CONTROL [initandlisten] MongoDB starting : pid=4384 port=27017 dbpath=/var/lib/mongodb 64-bit host=ubuntu
2017-01-09T22:00:38.186+0900 I CONTROL [initandlisten] db version v3.4.1
2017-01-09T22:00:38.186+0900 I CONTROL [initandlisten] git version: 5e103c4f5583e2566a45d740225dc250baacfbd7
2017-01-09T22:00:38.186+0900 I CONTROL [initandlisten] OpenSSL version: OpenSSL 1.0.2g 1 Mar 2016
(中略)
2017-01-09T22:00:38.254+0900 I FTDC [initandlisten] Initializing full-time diagnostic data capture with directory '/var/lib/mongodb/diagnostic.data'
2017-01-09T22:00:38.265+0900 I INDEX [initandlisten] build index on: admin.system.version properties: { v: 2, key: { version: 1 }, name: "incompatible_with_version_32", ns: "admin.system.version" }
2017-01-09T22:00:38.265+0900 I INDEX [initandlisten] building index using bulk method; build may temporarily use up to 500 megabytes of RAM
2017-01-09T22:00:38.266+0900 I INDEX [initandlisten] build index done. scanned 0 total records. 0 secs
2017-01-09T22:00:38.266+0900 I COMMAND [initandlisten] setting featureCompatibilityVersion to 3.4
2017-01-09T22:00:38.267+0900 I NETWORK [thread1] waiting for connections on port 27017
最後の行に「waiting for connections on port 27017」とあるので、これで大丈夫そうです。
###PyMongoのインストール
続いて、PythonでMongoDBを使うためのPyMongoのインストールですが、これはconda一発です。
segavvy@ubuntu:~$ conda search pymongo
Fetching package metadata .......
pymongo 2.7.2 py26_0 defaults
2.7.2 py27_0 defaults
2.7.2 py33_0 defaults
2.7.2 py34_0 defaults
2.8 py26_0 defaults
2.8 py27_0 defaults
2.8 py33_0 defaults
2.8 py34_0 defaults
3.0.3 py27_0 defaults
3.0.3 py34_0 defaults
3.0.3 py35_0 defaults
3.3.0 py27_0 defaults
3.3.0 py34_0 defaults
3.3.0 py35_0 defaults
3.3.0 py36_0 defaults
segavvy@ubuntu:~$ conda install pymongo
Fetching package metadata .......
Solving package specifications: ..........
Package plan for installation in environment /home/segavvy/anaconda3:
The following packages will be downloaded:
package | build
---------------------------|-----------------
pymongo-3.3.0 | py35_0 376 KB
The following NEW packages will be INSTALLED:
pymongo: 3.3.0-py35_0
Proceed ([y]/n)? y
Fetching packages ...
pymongo-3.3.0- 100% |################################| Time: 0:00:00 8.30 MB/s
Extracting packages ...
[ COMPLETE ]|###################################################| 100%
Linking packages ...
[ COMPLETE ]|###################################################| 100%
Pythonで使えることを確認しておきます。
segavvy@ubuntu:~$ python
Python 3.5.2 |Anaconda 4.1.1 (64-bit)| (default, Jul 2 2016, 17:53:06)
[GCC 4.4.7 20120313 (Red Hat 4.4.7-1)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import pymongo
>>>
これで環境構築は完了です。
###MongoDBの起動、終了、再起動
起動、終了、再起動の方法は、インストール手順の後半にあるRun MongoDB Community Editionにまとまっています。
segavvy@ubuntu:~$ sudo service mongod start
segavvy@ubuntu:~$ sudo service mongod stop
segavvy@ubuntu:~$ sudo service mongod restart
###MongoDBのコマンド(インタラクティブシェル)
MongoDBの起動中はmongoコマンドでいろいろできます。svjunicさんがまとめられているMongoDB コマンドメモとか書きが便利です。
以下、DB一覧の表示例です。
segavvy@ubuntu:~$ mongo
MongoDB shell version v3.4.1
connecting to: mongodb://127.0.0.1:27017
MongoDB server version: 3.4.1
Server has startup warnings:
2017-01-14T12:51:31.792+0900 I STORAGE [initandlisten]
2017-01-14T12:51:31.792+0900 I STORAGE [initandlisten] ** WARNING: Using the XFS filesystem is strongly recommended with the WiredTiger storage engine
2017-01-14T12:51:31.792+0900 I STORAGE [initandlisten] ** See http://dochub.mongodb.org/core/prodnotes-filesystem
2017-01-14T12:51:32.332+0900 I CONTROL [initandlisten]
2017-01-14T12:51:32.332+0900 I CONTROL [initandlisten] ** WARNING: Access control is not enabled for the database.
2017-01-14T12:51:32.332+0900 I CONTROL [initandlisten] ** Read and write access to data and configuration is unrestricted.
2017-01-14T12:51:32.332+0900 I CONTROL [initandlisten]
2017-01-14T12:51:32.332+0900 I CONTROL [initandlisten]
2017-01-14T12:51:32.332+0900 I CONTROL [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is 'always'.
2017-01-14T12:51:32.332+0900 I CONTROL [initandlisten] ** We suggest setting it to 'never'
2017-01-14T12:51:32.332+0900 I CONTROL [initandlisten]
2017-01-14T12:51:32.332+0900 I CONTROL [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/defrag is 'always'.
2017-01-14T12:51:32.332+0900 I CONTROL [initandlisten] ** We suggest setting it to 'never'
2017-01-14T12:51:32.332+0900 I CONTROL [initandlisten]
> show dbs
admin 0.000GB
local 0.000GB
testdb 0.254GB
>
mongoコマンドの実行直後にワーニングがたくさん出てきています。ちゃんとしたシステムで使う時は対応した方が良さそうですね。ちなみにDB一覧の中にある「testdb」は、インストール直後にはありません。今回の問題を実行して作ったものです。
###PyMongoを使ったプログラミング
PyMongoの使い方は、Tutorial(今回condaで入った3.3.0のもの)(最新はこちら)を見るとだいたいわかります。
今回の問題では、「testdb」というデータベースを作り、そこに「artist」というコレクションを作って、jsonのデータをどんどん登録していきました。
###バルクインサート
登録はcollection.insert_one()
(最新)で1件ずつ繰り返してもいいのですが、今回は100万件近くもあるため、バルクインサートという機能を使いました。
collection.insert_one()
は、実行の度にMongoDBのサーバーへ登録依頼を投げるので、大量に繰り返すと登録依頼の度に発生するオーバーヘッドが無視できません。collection.insert_many()
(最新)なら、リストに詰め込んだ複数件のデータの登録依頼を1回で投げるため、サーバーとの往復が減らせて効率的です。今回はこの機能を使って1万件ずつ登録しました。ただ、それでも100万件近くあると時間がかかるので、1万件ごとに処理経過を表示するようにしています。
バルクインサートについては、TutorialのBulk Inserts(最新)に概要説明があります。
###インデックスの作成
インデックスはcollection.create_index()
(最新)で作成でき、検索を高速化できます。TutorialのIndexing(最新)に概要説明があります。
65本目のノックは以上です。誤りなどありましたら、ご指摘いただけますと幸いです。
実行結果には、100本ノックで用いるコーパス・データで配布されているデータの一部が含まれます。この第7章で用いているデータのライセンスはクリエイティブ・コモンズ 表示 - 非営利 - 継承 3.0 非移植(日本語訳)です。