言語処理100本ノック-63(RedisとPython使用):オブジェクトを値に格納したKVS

[言語処理100本ノック 2015](http://www.cl.ecei.tohoku.ac.jp/nlp100/)の63本目「オブジェクトを値に格納したKVS」の記録です。RedisとPythonを使っています。
63本目はKVSのNoSQLデータベースに対して値の格納と検索をする処理です。

# 「言語処理100本ノック」記録
- [言語処理100本ノック-60(RedisとPython使用):KVSの構築](https://qiita.com/FukuharaYohei/items/cb9c0333b1a341c74473)
- [言語処理100本ノック-61(RedisとPython使用):KVSの検索](https://qiita.com/FukuharaYohei/items/f53f2ccbd2e417118a5c#_reference-a78e9cc20267cd483480)
- [言語処理100本ノック-62(RedisとPython使用):KVS内の反復処理](https://qiita.com/FukuharaYohei/items/444ae057b1e8adaee5f4)
- [言語処理100本ノック-63(RedisとPython使用):オブジェクトを値に格納したKVS](https://qiita.com/FukuharaYohei/items/f30b7f2b228b0a3832fc)

# 参考リンク

|リンク|備考|
|:--|:--|
| [書籍「RDB技術者のためのNoSQLガイド」](https://amzn.to/2Ua8ObK)|RDBに浸かっていた私には非常にわかりやすかったです|
|[063_1.オブジェクトを値に格納したKVS.ipynb](https://github.com/YoheiFukuhara/nlp100/blob/master/07.%E3%83%87%E3%83%BC%E3%82%BF%E3%83%99%E3%83%BC%E3%82%B9/063_1.%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%82%92%E5%80%A4%E3%81%AB%E6%A0%BC%E7%B4%8D%E3%81%97%E3%81%9FKVS.ipynb)<br>[063_2.オブジェクトを値に格納したKVS.ipynb](https://github.com/YoheiFukuhara/nlp100/blob/master/07.%E3%83%87%E3%83%BC%E3%82%BF%E3%83%99%E3%83%BC%E3%82%B9/063_2.%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%82%92%E5%80%A4%E3%81%AB%E6%A0%BC%E7%B4%8D%E3%81%97%E3%81%9FKVS.ipynb)|回答プログラムのGitHubリンク<br>格納と検索で2つに分けています|
|[最新RedisのUbuntuへのインストールとPythonで使うまで](https://qiita.com/FukuharaYohei/items/4485d1b11f88fd4d1f55)|インストールについて|
|[【初心者向け】Redisのデータ型とPythonでの使い方サンプル](https://qiita.com/FukuharaYohei/items/48209d488bc7f412c3d7)|Redisのデータ型について整理|
|[言語処理100本ノックでPython入門 #63 - KVSにオブジェクトを格納](http://gushwell.ldblog.jp/archives/52523798.html)|Redis-pyの回答として参考にしました|
|[素人の言語処理100本ノック:63](http://qiita.com/segavvy/items/507a82ecd75d0c6ea14f)|言語処理100本ノックで常にお世話になっています|

# 環境
インストールに関しては記事[「最新RedisのUbuntuへのインストールとPythonで使うまで」](https://qiita.com/FukuharaYohei/items/4485d1b11f88fd4d1f55)を参照ください。

|種類|バージョン|内容|
|:--|:--|:--|
|OS|Ubuntu18.04.01 LTS|仮想で動かしています|
|Redis|5.0.4|2019/3/22時点でStableの最新です|
|pyenv|1.2.9|複数Python環境を使うことがあるのでpyenv使っています|
|Python|3.7.2|pyenv上でpython3.7.2を使っています<br>パッケージはvenvを使って管理しています|
|reids-py|3.2.1|2019/3/22時点で最新|

# 問題
## [第7章: データベース](http://www.cl.ecei.tohoku.ac.jp/nlp100/#ch7)

>[artist.json.gz](http://www.cl.ecei.tohoku.ac.jp/nlp100/data/artist.json.gz)は，オープンな音楽データベース[MusicBrainz](https://musicbrainz.org/)の中で，アーティストに関するものを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等を用いてもよい．

【注意事項】**このデータは「アーティスト名」で一意になりません**。なので、単純に「アーティスト名」をKVSのキーにすると2件目以降で上書きしてしまうためファイルの件数とKVSの件数が合致しません。

## 63. オブジェクトを値に格納したKVS
> KVSを用い，アーティスト名（name）からタグと被タグ数（タグ付けされた回数）のリストを検索するためのデータベースを構築せよ．さらに，ここで構築したデータベースを用い，アーティスト名からタグと被タグ数を検索せよ．

# プログラム
## 格納プログラム：063_1.オブジェクトを値に格納したKVS.ipynb
Jupyter Notebookの形式をPython形式に変換すると以下のコードです。
Jupyter Notebook形式の結果は、[「063_1.オブジェクトを値に格納したKVS.ipynb」](https://github.com/YoheiFukuhara/nlp100/blob/master/07.%E3%83%87%E3%83%BC%E3%82%BF%E3%83%99%E3%83%BC%E3%82%B9/063_1.%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%82%92%E5%80%A4%E3%81%AB%E6%A0%BC%E7%B4%8D%E3%81%97%E3%81%9FKVS.ipynb)を見てください。

```python:063_1.オブジェクトを値に格納したKVS.ipynb
#!/usr/bin/env python
# coding: utf-8

from redis import Redis
import json

r = Redis( )

FNAME = './artist.json'

# ファイル読み込み、パース
with open(FNAME, encoding='utf8') as data_file:
#    for line in data_file:
    for i, line in enumerate(data_file):
        data_json = json.loads(line)

        # tagがない場合もあり
        if 'tags' in data_json:
            tags = json.dumps(data_json['tags'])
        else:
            tags = ''

        # アーティスト名では一意でないためHashesデータ型として追加
        r.hset(data_json['name'], data_json['id'], tags)
r.save()

# 確認のため登録件数を表示
print('{:,}件登録しました。'.format(r.dbsize()))
```

## 格納プログラム解説
1件のアーティストに対して複数タグがあるので、タグ部分はJSONでそのまま入れてしまっています。「アーティスト名」で一意にならないのでこんな形式に・・・
どうせ、タグで検索しないのでと思い、これ以上の思考・調査をサボりました。
もっといい方法がありそうな気もします。

## 検索プログラム：063_2.オブジェクトを値に格納したKVS.ipynb
Jupyter Notebookの形式をPython形式に変換すると以下のコードです。
Jupyter Notebook形式の結果は、[「063_2.オブジェクトを値に格納したKVS.ipynb」](https://github.com/YoheiFukuhara/nlp100/blob/master/07.%E3%83%87%E3%83%BC%E3%82%BF%E3%83%99%E3%83%BC%E3%82%B9/063_2.%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%82%92%E5%80%A4%E3%81%AB%E6%A0%BC%E7%B4%8D%E3%81%97%E3%81%9FKVS.ipynb)を見てください。

```python:063_2.オブジェクトを値に格納したKVS.ipynb
#!/usr/bin/env python
# coding: utf-8

from redis import Redis
import json

r = Redis( )

def get_tags(name):
    for values in r.hvals(name):
        if values != b'':
            for tag in json.loads(values.decode()):
                print(tag)
        else:
            print('No tags')

get_tags("松任谷由実")
# => {'count': 1, 'value': 'fixme label mess'}
# => {'count': 1, 'value': 'likedis auto'}

get_tags("George Winston")
# => {'count': 1, 'value': 'new age'}
# => {'count': 1, 'value': 'folk'}

get_tags("The Woods Band")
# => No tags
```

## 検索プログラム解説
パラメータを使って「アーティスト名」を聞くなど親切な機能をつけていません。
適当に3つの「アーティスト名」で検索しています。
タグ部分はJSON形式となっているのでjson.loadsでパースしています。
最初の「松任谷由実」と2番目の「George Winston」で検索したときには、それぞれ2つのタグを表示しています。
3番目の「The Woods Band」の場合は、タグがありません。