1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-01-21

言語処理100本ノック 2015の挑戦記録です。環境はUbuntu 16.04 LTS + Python 3.5.2 :: Anaconda 4.1.1 (64-bit)です。過去のノックの一覧はこちらからどうぞ。

第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等を用いてもよい.

###65. MongoDBの検索

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

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

main.py
# coding: utf-8
import json
from pymongo import MongoClient
from bson.objectid import ObjectId


def support_ObjectId(obj):
	'''json.dumps()でObjectIdを処理するための関数
	ObjectIdはjsonエンコードできない型なので、文字列型に変換する

	戻り値:
	ObjectIdから変換した文字列
	'''
	if isinstance(obj, ObjectId):
		return str(obj)		# 文字列として扱う
	raise TypeError(repr(obj) + " is not JSON serializable")


# MongoDBのデータベースtestdbにコレクションartistにアクセス
client = MongoClient()
db = client.testdb
collection = db.artist

# 検索
for i, doc in enumerate(collection.find({'name': 'Queen'}), start=1):

	# 整形して表示
	print('{}件目の内容:\n{}'.format(i,
		json.dumps(
			doc, indent='\t', ensure_ascii=False,
			sort_keys=True, default=support_ObjectId
		)
	))

####実行結果:

まず、インタラクティブシェルの実行結果です。

インタラクティブシェルでの実行結果
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-14T17:47:12.528+0900 I STORAGE  [initandlisten] 
2017-01-14T17:47:12.528+0900 I STORAGE  [initandlisten] ** WARNING: Using the XFS filesystem is strongly recommended with the WiredTiger storage engine
2017-01-14T17:47:12.528+0900 I STORAGE  [initandlisten] **          See http://dochub.mongodb.org/core/prodnotes-filesystem
2017-01-14T17:47:12.898+0900 I CONTROL  [initandlisten] 
2017-01-14T17:47:12.898+0900 I CONTROL  [initandlisten] ** WARNING: Access control is not enabled for the database.
2017-01-14T17:47:12.898+0900 I CONTROL  [initandlisten] **          Read and write access to data and configuration is unrestricted.
2017-01-14T17:47:12.898+0900 I CONTROL  [initandlisten] 
2017-01-14T17:47:12.898+0900 I CONTROL  [initandlisten] 
2017-01-14T17:47:12.898+0900 I CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is 'always'.
2017-01-14T17:47:12.898+0900 I CONTROL  [initandlisten] **        We suggest setting it to 'never'
2017-01-14T17:47:12.898+0900 I CONTROL  [initandlisten] 
2017-01-14T17:47:12.898+0900 I CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/defrag is 'always'.
2017-01-14T17:47:12.898+0900 I CONTROL  [initandlisten] **        We suggest setting it to 'never'
2017-01-14T17:47:12.898+0900 I CONTROL  [initandlisten] 
> show dbs
admin   0.000GB
local   0.000GB
testdb  0.131GB
> use testdb
switched to db testdb
> show collections
artist
> db.artist.find( { 'name': 'Queen' } );
{ "_id" : ObjectId("5879fda21d41c85827363e72"), "name" : "Queen", "id" : 701492, "type" : "Character", "tags" : [ { "value" : "kamen rider w", "count" : 1 }, { "value" : "related-akb48", "count" : 1 } ], "gid" : "420ca290-76c5-41af-999e-564d7c71f1a7", "ended" : true, "sort_name" : "Queen", "gender" : "Female", "area" : "Japan", "aliases" : [ { "name" : "Queen", "sort_name" : "Queen" } ] }
{ "_id" : ObjectId("5879fda51d41c8582737051e"), "name" : "Queen", "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 } ], "gid" : "0383dadf-2a4e-4d10-a46a-e9e041da8eb3", "sort_name" : "Queen", "ended" : true, "area" : "United Kingdom", "type" : "Group", "begin" : { "date" : 27, "month" : 6, "year" : 1970 }, "aliases" : [ { "name" : "女王", "sort_name" : "女王" } ], "rating" : { "value" : 92, "count" : 24 } }
{ "_id" : ObjectId("5879fdab1d41c8582738bf76"), "id" : 992994, "name" : "Queen", "gid" : "5eecaf18-02ec-47af-a4f2-7831db373419", "sort_name" : "Queen", "ended" : true }
> exit
bye

続いてプログラムの実行結果です。

実行結果
1件目の内容:
{
	"_id": "5879fda21d41c85827363e72",
	"aliases": [
		{
			"name": "Queen",
			"sort_name": "Queen"
		}
	],
	"area": "Japan",
	"ended": true,
	"gender": "Female",
	"gid": "420ca290-76c5-41af-999e-564d7c71f1a7",
	"id": 701492,
	"name": "Queen",
	"sort_name": "Queen",
	"tags": [
		{
			"count": 1,
			"value": "kamen rider w"
		},
		{
			"count": 1,
			"value": "related-akb48"
		}
	],
	"type": "Character"
}
2件目の内容:
{
	"_id": "5879fda51d41c8582737051e",
	"aliases": [
		{
			"name": "女王",
			"sort_name": "女王"
		}
	],
	"area": "United Kingdom",
	"begin": {
		"date": 27,
		"month": 6,
		"year": 1970
	},
	"ended": true,
	"gid": "0383dadf-2a4e-4d10-a46a-e9e041da8eb3",
	"id": 192,
	"name": "Queen",
	"rating": {
		"count": 24,
		"value": 92
	},
	"sort_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"
		}
	],
	"type": "Group"
}
3件目の内容:
{
	"_id": "5879fdab1d41c8582738bf76",
	"ended": true,
	"gid": "5eecaf18-02ec-47af-a4f2-7831db373419",
	"id": 992994,
	"name": "Queen",
	"sort_name": "Queen"
}

###インタラクティブシェルでの検索
mongoでシェルを起動して、useでデータベースを切り替えて、db.(コレクション).find()で検索できます。前問同様、svjunicさんがまとめられているMongoDB コマンドメモとか書きが便利です。

###プログラムでの検索
インタラクティブシェル同様、collection.find()(最新はこちら)で検索できます。検索するとCursor最新)オブジェクトが返って、結果を列挙できるようになっています。

###JSON形式で整形
結果から取り出したドキュメントは辞書型になっているので、そのまま表示すると1行になって非常に見にくくなります。

1件目をprint(doc)した結果
{'tags': [{'count': 1, 'value': 'kamen rider w'}, {'count': 1, 'value': 'related-akb48'}], 'type': 'Character', '_id': ObjectId('5879fda21d41c85827363e72'), 'id': 701492, 'gender': 'Female', 'gid': '420ca290-76c5-41af-999e-564d7c71f1a7', 'sort_name': 'Queen', 'name': 'Queen', 'aliases': [{'sort_name': 'Queen', 'name': 'Queen'}], 'ended': True, 'area': 'Japan'}

この辞書を見やすく整形できないか考えていたところ、Hyperion13fleetさんの[python] JSONファイルのフォーマットを整えてDumpするを見つけました。辞書をjson.dumps()でJSON形式に戻せば整形できそうです。

ところが、単純にjson.dumps(doc, indent='\t', sort_keys=True)とかするとエラーになってしまいます。

json.dumps(doc)のエラー
TypeError: ObjectId('5879fda21d41c85827363e72') is not JSON serializable

これは、docで指定した辞書の中にJSON形式に変換できないObjectIdというオブジェクトがあるためです。問題20で勉強しましたが、JSON形式とPythonの辞書への変換は変換表に従って行われるので、ここにない型はエラーになってしまうのです。

でも、前問でJSON形式のデータを読み込んで辞書に変換してMongoDBに登録し、今回それをMongoDBから辞書として取り出してJSON形式に戻そうとしただけです。可逆じゃないのが不思議で最初はちょっと悩みましたが、このObjectIdは元データにはなく、MongoDBに登録する時に自動的に追加される12バイトのユニークキーであることがわかりました。ObjectIdの詳細はこちら最新)です。

###JSON形式の変換表にない型の扱い

幸い、変換表にない型については、自分で変換関数を実装して指定することができます。それがjson.dumps()defaultという引数で指定できるコールバック関数です。引数の説明はjson.dump()と共通なのでそちらに説明があります。このdefaultの説明部分は翻訳されておらず英語のままなのですが、ちゃんと読むと使い方がわかりました。今回はsupport_ObjectId()を作って文字列に変換しています。

なお、引数defaultを使うのではなく、JSONEncoderの派生クラスを指定する方法もあるそうです。どちらもpodhmoさんのpythonでjson出力する際で対応していない型(e.g. datetime)の値を変換しながら出力したいの解説がわかりやすいです。

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


実行結果には、100本ノックで用いるコーパス・データで配布されているデータの一部が含まれます。この第7章で用いているデータのライセンスはクリエイティブ・コモンズ 表示 - 非営利 - 継承 3.0 非移植日本語訳)です。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?