北野坂備忘録

主にインストールやプログラミングのメモを載せています。

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