言語処理100本ノック 2015年版 (64~69)
第7章の後半はMongoDBを使っていきます。
64. MongoDBの構築
アーティスト情報(artist.json.gz)をデータベースに登録せよ.さらに,次のフィールドでインデックスを作成せよ: name, aliases.name, tags.value, rating.value
CentOSではMongoDBは簡単にインストールできます。
Python上での MongoDBの操作は PyMongoで行います。
データベースとコレクション(RDBMSで言うところのテーブル)の名前を別にしたい。設問に指定はないので、データベース名を MusicBrainz、コレクション名を artistにします。
kvsのときと違って全データを引き渡すのではるかに時間がかかります。
#!/usr/bin/env python import codecs import json import pymongo fin = codecs.open('artist.json', 'r', 'utf_8') if __name__ == "__main__": client = pymongo.MongoClient() db = client.MusicBrainz print("db:",db.name) collection = db.artist for line in fin: jsonData = json.loads(line) print(jsonData) collection.insert_one(jsonData)
次にインデックスを作成していきます。
64index.py
#!/usr/bin/env python import pymongo fin = codecs.open('artist.json', 'r', 'utf_8') if __name__ == "__main__": client = pymongo.MongoClient() db = client.MusicBrainz print("db:",db.name) collection = db.artist 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)])
65. MongoDBの検索
MongoDBのインタラクティブシェルを用いて,"Queen"というアーティストに関する情報を取得せよ.さらに,これと同様の処理を行うプログラムを実装せよ.
設問文におけるアーティスト名の指定は慎重にいきたいものです。これが「9人の麦わら海賊団」とかだと示しがつきません。
mongoDB上でのコマンドはこちら
db.artist.find({name: "Queen"})
プログラムはこちら
#!/usr/bin/env python import pymongo if __name__ == "__main__": client = pymongo.MongoClient() db = client.MusicBrainz print("db:",db.name) collection = db.artist for Queen in collection.find({"name": "Queen"}): print(Queen)
結果
> db.artist.find( { name: "Queen" } ) { "_id" : ObjectId("56c31febe13823b95d5c3c92"), "type" : "Character", "name" : "Queen", "ended" : true, "id" : 701492, "aliases" : [ { "name" : "Queen", "sort_name" : "Queen" } ], "gid" : "420ca290-76c5-41af-999e-564d7c71f1a7", "gender" : "Female", "sort_name" : "Queen", "area" : "Japan", "tags" : [ { "value" : "kamen rider w", "count" : 1 }, { "value" : "related-akb48", "count" : 1 } ] } { "_id" : ObjectId("56c3211de13823b95d5d033e"), "type" : "Group", "ended" : true, "id" : 192, "tags" : [ { "value" : "hard rock", "count" : 2 }, { "value" : "70s", "count" : 1 }, { "value" : "queen family", "count" : 1 }, { "value" : "90s", "count" : 1 }, { "value" : "80s", "count" : 1 }, { "value" : "glam rock", "count" : 1 }, { "value" : "british", "count" : 4 }, { "value" : "english", "count" : 1 }, { "value" : "uk", "count" : 2 }, { "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" : 1 }, { "value" : "rock", "count" : 6 }, { "value" : "platinum", "count" : 1 } ], "begin" : { "month" : 6, "year" : 1970, "date" : 27 }, "name" : "Queen", "rating" : { "value" : 92, "count" : 24 }, "gid" : "0383dadf-2a4e-4d10-a46a-e9e041da8eb3", "sort_name" : "Queen", "area" : "United Kingdom", "aliases" : [ { "name" : "女王", "sort_name" : "女王" } ] } { "_id" : ObjectId("56c323c6e13823b95d5ebd96"), "name" : "Queen", "ended" : true, "id" : 992994, "gid" : "5eecaf18-02ec-47af-a4f2-7831db373419", "sort_name" : "Queen" }
んんんー? "kamen rider w"とか予想外の単語が入ってるんですが……まさかこれを狙っての出題……?
66. 検索件数の取得
MongoDBのインタラクティブシェルを用いて,活動場所が「Japan」となっているアーティスト数を求めよ.
なぜこの設問だけインタラクティブシェルだけなのでしょうか。
> db.artist.find({area:'Japan'}).count() 22821
67. 複数のドキュメントの取得
特定の(指定した)別名を持つアーティストを検索せよ.
aliases[].name を使います。これはプログラムからにしました。いつもどおり引数で処理します。
#!/usr/bin/env python import pymongo import sys if __name__ == "__main__": param = sys.argv client = pymongo.MongoClient() db = client.MusicBrainz print("db:",db.name) collection = db.artist if len(param)>1: aliases_name = param[1] else: aliases_name = "Queen" for aliases in collection.find({"aliases.name": aliases_name}): print(aliases)
結果
>python3 67.py オアシス db: MusicBrainz {'type': 'Group', 'aliases': [{'name': 'OASIS', 'sort_name': 'OASIS'}, {'name': 'オアシス', 'sort_name': 'オアシス'}], '_id': ObjectId('56c3215ae13823b95d5d2851'), 'name': 'Oasis', 'sort_name': 'Oasis', 'ended': True, 'id': 20660, 'tags': [{'count': 1, 'value': 'rock'}, {'count': 3, 'value': 'britpop'}, {'count': 4, 'value': 'british'}, {'count': 1, 'value': 'uk'}, {'count': 1, 'value': 'britannique'}, {'count': 1, 'value': 'rock and indie'}, {'count': 1, 'value': 'england'}, {'count': 1, 'value': 'manchester'}], 'area': 'United Kingdom', 'rating': {'count': 13, 'value': 86}, 'begin': {'year': 1991}, 'end': {'year': 2009, 'date': 28, 'month': 8}, 'gid': '39ab1aed-75e0-4140-bd47-540276886b60'}
68. ソート
"dance"というタグを付与されたアーティストの中でレーティングの投票数が多いアーティスト・トップ10を求めよ
ratingが入っていないアーティストがいるので find().sort( { rating.count:-1 } ) で処理できません。
一旦 pythonのリストに入れてから処理します。
#!/usr/bin/env python import pymongo if __name__ == "__main__": client = pymongo.MongoClient() db = client.MusicBrainz print("db:",db.name) collection = db.artist dancer = [] for dance in collection.find({"tags.value": "dance"}): if "rating" in dance: dancer.append([dance["name"],dance["rating"]["count"]]) dancecount = sorted(dancer, key=lambda x:x[1], reverse=True) print("dance artist rating count top 10:") n = 0 for dance in dancecount: if n > 9: break print(" ",dance[0],dance[1]) n += 1
結果
>python3 68.py db: MusicBrainz dance artist rating count top 10: Madonna 26 Björk 23 The Prodigy 23 Rihanna 15 Britney Spears 13 Maroon 5 11 Adam Lambert 7 Fatboy Slim 7 Basement Jaxx 6 Cornershop 5
69. Webアプリケーションの作成
ユーザから入力された検索条件に合致するアーティストの情報を表示するWebアプリケーションを作成せよ.アーティスト名,アーティストの別名,タグ等で検索条件を指定し,アーティスト情報のリストをレーティングの高い順などで整列して表示せよ.
ここまで「やけに簡単だな」と思っていたら最後に大玉が来ました。「Webアプリケーションを作成せよ」ってか。
mongoDBと親和性の高いフレームワークはFlask。昔はpython3に対応していませんでしたが今は対応しています。
で、
1 アーティスト名,アーティストの別名,タグ等で検索条件を指定
2 アーティスト情報のリストを表示
3 レーティングの高い順などで整列して
という指示なんですが、厳しいのが3番。レーティング情報の入っていないアーティストがいっぱいいるからです。
結局レーティング情報の入っていないアーティストが非整列でズラズラ並ぶのでは無駄なので、今回はタグで検索して名前で整列させることにしました。名前なら必ず入っていますので。
もう一つの方法は68みたいにレーティング情報の入っていないアーティストはバッサリ切ってしまうかですね。このあたりは好みの問題でしょう。
とりあえず根っこの app.pyだけを掲載しておきます。
from flask import Flask, render_template, request, redirect, url_for import pymongo app = Flask(__name__) client = pymongo.MongoClient() db = client.MusicBrainz ul = db["artist"] @app.route('/') def index(): title = "artist" return render_template('index.html', title=title) @app.route('/post', methods=['GET', 'POST']) def post(): title = "artist" if request.method == 'POST': tag = request.form['tag'] artist = [i for i in ul.find({"tags.value":tag})] artist = sorted(artist,key=lambda x:x["name"]) return render_template('index.html', items=artist, title=title) else: return redirect(url_for('index')) if __name__ == '__main__': app.debug = True app.run(host='0.0.0.0')