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


[言語処理100本ノック 2015](http://www.cl.ecei.tohoku.ac.jp/nlp100/)の挑戦記録です。環境はUbuntu 16.04 LTS ＋ Python 3.5.2 \:\: Anaconda 4.1.1 (64-bit)です。過去のノックの一覧は[こちら](http://qiita.com/segavvy/items/fb50ba8097d59475f760)からどうぞ。

## 第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

####出来上がったコード：

```python: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)])
```

####実行結果：

```console:実行結果
10000件追加完了
20000件追加完了
30000件追加完了
40000件追加完了
50000件追加完了
（中略）
890000件追加完了
900000件追加完了
910000件追加完了
920000件追加完了
921337件追加完了
```

###MongoDBとは
MongoDBはドキュメント指向データベースと呼ばれるもので、今回の問題で使う音楽データベースのような構造を持ったデータを放り込んで管理できるのが特徴のようです。オフィシャルサイトは[こちら](https://www.mongodb.com)です。残念ながら英語ですが、[MongoDBの概要](https://www.mongodb.com/jp)だけは日本語がありました。他にもMongoDBの解説はネットにたくさんありますので、詳細はググってみてください。

なお、MongoDBには有償版もあるようですが、今回は無償のCommunity Editionを使っています。

###MongoDBのインストール

condaで一発かと思ったのですが、サービスのスクリプトを書かないといけない？だかでちょっと面倒のようなので、今回はオフィシャルサイトの手順でインストールしました。

私が使っているUbuntu 16.04 LTSへのインストール手順は[Install MongoDB Community Edition on Ubuntu](https://docs.mongodb.com/manual/tutorial/install-mongodb-on-ubuntu/)にあります。英語ですがそれほど難しくはありません。[Install MongoDB Community Edition](https://docs.mongodb.com/manual/tutorial/install-mongodb-on-ubuntu/#install-mongodb-community-edition)の通りにやっていくとインストールできました。

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

```console:MongoDBの起動
segavvy@ubuntu:~$ sudo service mongod start
```

正常に起動したかどうかは、[Verify that MongoDB has started successfully](https://docs.mongodb.com/manual/tutorial/install-mongodb-on-ubuntu/#verify-that-mongodb-has-started-successfully)の手順で確認できます。

```console:/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一発です。

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

```console: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](https://docs.mongodb.com/manual/tutorial/install-mongodb-on-ubuntu/#run-mongodb-community-edition)にまとまっています。

```console:MongoDBの起動
segavvy@ubuntu:~$ sudo service mongod start
```

```console:MongoDBの終了
segavvy@ubuntu:~$ sudo service mongod stop
```

```console:MongoDBの再起動
segavvy@ubuntu:~$ sudo service mongod restart
```

###MongoDBのコマンド（インタラクティブシェル）
MongoDBの起動中はmongoコマンドでいろいろできます。svjunicさんがまとめられている[MongoDB コマンドメモとか書き](http://qiita.com/svjunic/items/285e9cf20169d70aa1fa)が便利です。
以下、DB一覧の表示例です。

```console: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のもの）](http://api.mongodb.com/python/3.3.0/tutorial.html)（最新は[こちら](http://api.mongodb.com/python/current/tutorial.html)）を見るとだいたいわかります。

今回の問題では、「testdb」というデータベースを作り、そこに「artist」というコレクションを作って、jsonのデータをどんどん登録していきました。

###バルクインサート

登録は[`collection.insert_one()`](http://api.mongodb.com/python/3.3.0/api/pymongo/collection.html#pymongo.collection.Collection.insert_one)（[最新](http://api.mongodb.com/python/current/api/pymongo/collection.html#pymongo.collection.Collection.insert_one)）で1件ずつ繰り返してもいいのですが、今回は100万件近くもあるため、バルクインサートという機能を使いました。

[`collection.insert_one()`](http://api.mongodb.com/python/3.3.0/api/pymongo/collection.html#pymongo.collection.Collection.insert_one)は、実行の度にMongoDBのサーバーへ登録依頼を投げるので、大量に繰り返すと登録依頼の度に発生するオーバーヘッドが無視できません。[`collection.insert_many()`](http://api.mongodb.com/python/3.3.0/api/pymongo/collection.html#pymongo.collection.Collection.insert_many)（[最新](http://api.mongodb.com/python/current/api/pymongo/collection.html#pymongo.collection.Collection.insert_many)）なら、リストに詰め込んだ複数件のデータの登録依頼を1回で投げるため、サーバーとの往復が減らせて効率的です。今回はこの機能を使って1万件ずつ登録しました。ただ、それでも100万件近くあると時間がかかるので、1万件ごとに処理経過を表示するようにしています。

バルクインサートについては、Tutorialの[Bulk Inserts](http://api.mongodb.com/python/3.3.0/tutorial.html#bulk-inserts)（[最新](http://api.mongodb.com/python/current/tutorial.html#bulk-inserts)）に概要説明があります。


###インデックスの作成
インデックスは[`collection.create_index()`](http://api.mongodb.com/python/3.3.0/api/pymongo/collection.html#pymongo.collection.Collection.create_index)（[最新](http://api.mongodb.com/python/current/api/pymongo/collection.html#pymongo.collection.Collection.create_index)）で作成でき、検索を高速化できます。Tutorialの[Indexing](http://api.mongodb.com/python/3.3.0/tutorial.html#indexing)（[最新](http://api.mongodb.com/python/current/tutorial.html#indexing)）に概要説明があります。


　
65本目のノックは以上です。誤りなどありましたら、ご指摘いただけますと幸いです。
<hr>

*実行結果には、[100本ノックで用いるコーパス・データ](http://www.cl.ecei.tohoku.ac.jp/nlp100/#data)で配布されているデータの一部が含まれます。この第7章で用いているデータのライセンスは[クリエイティブ・コモンズ 表示 - 非営利 - 継承 3.0 非移植](https://creativecommons.org/licenses/by-nc-sa/3.0/)（[日本語訳](https://creativecommons.org/licenses/by-nc-sa/3.0/deed.ja)）です。*