Golangで言語処理100本ノック2015 第7章: データベース

言語処理100本ノック 2015の第7章: データベースの10問です。

redisとMongoDBを使うことにします。

redis

// 起動
# redis-server /usr/local/etc/redis.conf

// cli
# redis-cli

MongoDB

// 起動
# sudo mongod --dbpath /var/lib/mongodb --logpath /var/log/mongodb.log

// cli
# mongo

60. KVSの構築

Key-Value-Store (KVS) を用い,アーティスト名(name)から活動場所(area)を検索するためのデータベースを構築せよ.

gist103c8ead51aad42cfc5b6686caa2039a

bufioのdefault sizeである4096だとReadLine()しきれないところがあるのでNewReaderSize()を使っています。 Goでredisを扱うためには、redigoを使っています。
また、setを使ってKVSを構築すると同じアーティスト名が存在した場合に、上書きされてしまうのでのちのQ62,66で結果に不整合が生じます。

61. KVSの検索

60で構築したデータベースを用い,特定の(指定された)アーティストの活動場所を取得せよ.

giste6d4ca51b7b23914fadd5bdc0ec93c75

// 存在するアーティスト
# go run q61.go supercell
Japan

// 存在しないアーティスト
# go run q61.go hoge
` hoge ` is not found.

Q60でKVSの構築は済んでいるので、標準入力で受け取ったアーティストを検索するのみです。検索アーティストは趣味です。2015年当時のデータなので最近のアーティストはないようですが、知っているアーティストで検索すると引っかかるので楽しいです。

62. KVS内の反復処理

60で構築したデータベースを用い,活動場所が「Japan」となっているアーティスト数を求めよ.

gist6ac674e08083ff19c9154084b3d4787d

# go run q62.go
22821

keys *で全keyを取得して、それを反復してareaを検索しました。

63. オブジェクトを値に格納したKVS

KVSを用い,アーティスト名(name)からタグと被タグ数(タグ付けされた回数)のリストを検索するためのデータベースを構築せよ.さらに,ここで構築したデータベースを用い,アーティスト名からタグと被タグ数を検索せよ.

gista57f35c159e2693ca841051ecbc390ef

# go run q63.go "Fear of War"
count   |tag
--------------------
1   | punk
1   | sweden
1   | hardcore
1   | fagersta

list型でtagを入れました。検索は適当に複数タグがあるアーティストにしてみました。

64. MongoDBの構築

アーティスト情報(artist.json.gz)をデータベースに登録せよ.さらに,次のフィールドでインデックスを作成せよ: name, aliases.name, tags.value, rating.value

gistb4ce4fe18042ae02c8eafe941f59548b

# go run q64.go
before:  392.39824ms
after:  1.308422ms

Indexをはる前後でのnameでのクエリの実行時間を測定してみましたが、速くなっていますね。aliases.name, tags.value, rating.valueにはIndexをはらずに、nameのみIndexをはると800us程度になるので、むやみやたらにIndexをはればいいわけではないですね。
GolangでMongoDBを扱うためのbsonについては以前記事にまとめましたので、そちらも御覧ください。

65. MongoDBの検索

MongoDBのインタラクティブシェルを用いて,"Queen"というアーティストに関する情報を取得せよ.さらに,これと同様の処理を行うプログラムを実装せよ.

// インタラクティブシェル
> db.artist.find({"name": "Queen"})
{ "_id" : ObjectId("59aa3c32bb03c3f9e15a51ce"), "name" : "Queen", "tags" : [ { "count" : 1, "value" : "kamen rider w" }, { "count" : 1, "value" : "related-akb48" } ], "rating" : { "count" : 0, "value" : 0 }, "sortname" : "Queen", "ended" : true, "gid" : "420ca290-76c5-41af-999e-564d7c71f1a7", "id" : 701492, "area" : "Japan", "aliases" : [ { "name" : "Queen", "sortname" : "Queen" } ], "begin" : { "year" : 0, "month" : 0, "date" : 0 }, "end" : { "year" : 0, "month" : 0, "date" : 0 }, "gender" : "Female", "type" : "Character" }
{ "_id" : ObjectId("59aa3c3abb03c3f9e15bdf28"), "name" : "Queen", "tags" : [ { "count" : 2, "value" : "hard rock" }, { "count" : 1, "value" : "70s" }, { "count" : 1, "value" : "queen family" }, { "count" : 1, "value" : "90s" }, { "count" : 1, "value" : "80s" }, { "count" : 1, "value" : "glam rock" }, { "count" : 4, "value" : "british" }, { "count" : 1, "value" : "english" }, { "count" : 2, "value" : "uk" }, { "count" : 1, "value" : "pop/rock" }, { "count" : 1, "value" : "pop-rock" }, { "count" : 1, "value" : "britannique" }, { "count" : 1, "value" : "classic pop and rock" }, { "count" : 1, "value" : "queen" }, { "count" : 1, "value" : "united kingdom" }, { "count" : 1, "value" : "langham 1 studio bbc" }, { "count" : 1, "value" : "kind of magic" }, { "count" : 1, "value" : "band" }, { "count" : 6, "value" : "rock" }, { "count" : 1, "value" : "platinum" } ], "rating" : { "count" : 24, "value" : 92 }, "sortname" : "Queen", "ended" : true, "gid" : "0383dadf-2a4e-4d10-a46a-e9e041da8eb3", "id" : 192, "area" : "United Kingdom", "aliases" : [ { "name" : "女王", "sortname" : "女王" } ], "begin" : { "year" : 1970, "month" : 6, "date" : 27 }, "end" : { "year" : 0, "month" : 0, "date" : 0 }, "gender" : "", "type" : "Group" }
{ "_id" : ObjectId("59aa3c4bbb03c3f9e15f53d9"), "name" : "Queen", "tags" : [ ], "rating" : { "count" : 0, "value" : 0 }, "sortname" : "Queen", "ended" : true, "gid" : "5eecaf18-02ec-47af-a4f2-7831db373419", "id" : 992994, "area" : "", "aliases" : [ ], "begin" : { "year" : 0, "month" : 0, "date" : 0 }, "end" : { "year" : 0, "month" : 0, "date" : 0 }, "gender" : "", "type" : "" }

gist2f86dd77e7d54026d04bf478bff96fb0

# go run q65.go
{Queen [{1 kamen rider w} {1 related-akb48}] {0 0} Queen true 420ca290-76c5-41af-999e-564d7c71f1a7 701492 Japan [{Queen Queen}] {0 0 0} {0 0 0} Female Character}
{Queen [{2 hard rock} {1 70s} {1 queen family} {1 90s} {1 80s} {1 glam rock} {4 british} {1 english} {2 uk} {1 pop/rock} {1 pop-rock} {1 britannique} {1 classic pop and rock} {1 queen} {1 united kingdom} {1 langham 1 studio bbc} {1 kind of magic} {1 band} {6 rock} {1 platinum}] {24 92} Queen true 0383dadf-2a4e-4d10-a46a-e9e041da8eb3 192 United Kingdom [{女王 女王}] {1970 6 27} {0 0 0}  Group}
{Queen [] {0 0} Queen true 5eecaf18-02ec-47af-a4f2-7831db373419 992994  [] {0 0 0} {0 0 0}  }

Q64で構築したDBのクエリを投げるだけです。

66. 検索件数の取得

MongoDBのインタラクティブシェルを用いて,活動場所が「Japan」となっているアーティスト数を求めよ.

> db.artist.find({"area": "Japan"}).count()
22821

67. 複数のドキュメントの取得

特定の(指定した)別名を持つアーティストを検索せよ.

gist2ae3e6047aa223568e14a7abef7bd9b5

# go run q67.go supercell
{supercell [] {3 100} supercell true 9b15ff5e-5bd1-43c2-821d-e31240aad334 311779 Japan [{超级单体 超级单体} {supercell スーパーセル}] {2007 0 0} {0 0 0}  Group}

ほぼQ65と同じですね。

68. ソート

"dance"というタグを付与されたアーティストの中でレーティングの投票数が多いアーティスト・トップ10を求めよ.

gist6ae30eb5984a5b4699604fe6baa6eb0d

# go run q68.go
Madonna
Björk
The Prodigy
Rihanna
Britney Spears
Maroon 5
Adam Lambert
Fatboy Slim
Basement Jaxx
Gigi D’Agostino
Cornershop
Duran Duran

sortパッケージがgo1.8以上で変わっていたのは知っていたものの試していなかったので触れてよかったです。

69. Webアプリケーションの作成

ユーザから入力された検索条件に合致するアーティストの情報を表示するWebアプリケーションを作成せよ.アーティスト名,アーティストの別名,タグ等で検索条件を指定し,アーティスト情報のリストをレーティングの高い順などで整列して表示せよ.

gistb97736d9ce01ae3766f919d2d749fcb6

テンプレートファイルは以下です。

gist3d7903a98687bfcf6e5ce9f49be189a3

gist308ac9755a102f56f44f9f1eb7307304

どうやらginというHttpRouterが有名そうなので、今回はginを使って、検索フォームから検索し、テーブルで結果を表示するようにしてみました。 CLIからq69.goを実行した状態で、ブラウザでhttp://localhost:8080/indexへアクセスすると以下のように表示されます。

f:id:cipepser:20170903234012p:plain

検索してあげるとこんな感じです。空欄は元データが入っていないところです。

f:id:cipepser:20170903234017p:plain

初めてginを使ってみましたが、c.JSONなどとすると簡単にjsonを返せるようなのでAPIサーバも書けそうです。
今回のフォームからユーザに入力させる形式のところはインジェクション対策は何もしていないのでこのままでは使えないですが、Goでひと通り書いてみて勉強になりました。

感想

redisは軽くしか触れたことがなかったので、復習も兼ねて勉強になりました。Q68まではサクサク進みますが、Q69は重めですね。実りが多かったのでよかったですが。

いつも通りコードはGithubにも置いてあります。

Reference