Edited at

素人の言語処理100本ノック:64

More than 1 year has passed since last update.

言語処理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



出来上がったコード:


main.py

# 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の通りにやっていくとインストールできました。


MongoDBのインストール

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本体のインストールができましたので、起動することを確認します。


MongoDBの起動

segavvy@ubuntu:~$ sudo service mongod start


正常に起動したかどうかは、Verify that MongoDB has started successfullyの手順で確認できます。


/var/log/mongodb/mongod.logの確認

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一発です。


PyMongoのインストール

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で使えることを確認しておきます。


PyMongoのインストールの確認

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にまとまっています。


MongoDBの起動

segavvy@ubuntu:~$ sudo service mongod start



MongoDBの終了

segavvy@ubuntu:~$ sudo service mongod stop



MongoDBの再起動

segavvy@ubuntu:~$ sudo service mongod restart



MongoDBのコマンド(インタラクティブシェル)

MongoDBの起動中はmongoコマンドでいろいろできます。svjunicさんがまとめられているMongoDB コマンドメモとか書きが便利です。

以下、DB一覧の表示例です。


mongoコマンドで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 非移植日本語訳)です。