Symfoware

Symfowareについての考察blog

Python TinyDB Custom Storageの作り方

TinyDBを触ってみました。
Python 軽量ドキュメントデータベース TinyDB


ドキュメントを見てみると、独自のストレージを作成できるようです。
http://tinydb.readthedocs.org/en/latest/extend.html

面白そうなので、CSVファイルでデータを保存するストレージを作ってみます。



データ形式



readされた時、こんな辞書型を返却すれば良いようです。


{"_default": {"1": {"value": "value1"}, "2": {"value": "value2"}}}




また、write時もこの形式でデータが渡されます。
とりあえず、_defaultのテーブルに対応することだけを考え、
動くものを実装してみました。


  1. import os
  2. import codecs
  3. import tinydb
  4. class CSVStorage(tinydb.Storage):
  5.     
  6.     def __init__(self, filename):
  7.         self.filename = filename
  8.     
  9.     def read(self):
  10.         
  11.         if not os.path.exists(self.filename):
  12.             return None
  13.         
  14.         with codecs.open(self.filename, 'r', 'utf-8') as handle:
  15.             try:
  16.                 headers = handle.readline().strip().split('\t')
  17.                 data = {}
  18.                 eid = 1
  19.                 for line in handle:
  20.                     values = line.strip().split('\t')
  21.                     data[eid] = { k:v for k,v in zip(headers, values) }
  22.                     eid += 1
  23.                     
  24.                 return { '_default' : data }
  25.                 
  26.             except:
  27.                 return None
  28.     
  29.     def write(self, data):
  30.         rows = data['_default']
  31.         headers = None
  32.         for row in rows.values():
  33.             headers = row.keys()
  34.         
  35.         with codecs.open(self.filename, 'w', 'utf-8') as handle:
  36.             
  37.             if headers:
  38.                 handle.write(u'\t'.join(headers) + u'\n')
  39.             
  40.             for row in rows.values():
  41.                 handle.write(u'\t'.join(row.values()) + u'\n')
  42.         
  43.         
  44.             
  45.     def close(self):
  46.         pass
  47.         
  48.         
  49.         
  50. # データベースオープン 引数はファイル名
  51. db = tinydb.TinyDB('db.csv', storage=CSVStorage)
  52. db.insert({'key_1':'value_1-1', 'key_2':'value_1-2'})
  53. db.insert({'key_1':'value_2-1', 'key_2':'value_2-2'})
  54. q = tinydb.Query()
  55. print db.search(q.key_1 == 'value_2-1')




実行結果


$ python sample.py
[{u'key_1': u'value_2-1', u'key_2': u'value_2-2'}]




とりあえず動くかどうか試した程度の実装ですが、
任意のストレージを作成できそうです。




郵便番号検索



続いて、郵便番号のzipファイルを読み込んで検索してみます。
Pythonでzipファイルを解凍せずに中身のテキストファイルを読み込む


こんな感じになりました。


  1. import tinydb
  2. import zipfile
  3. import cStringIO
  4. class CSVStorage(tinydb.Storage):
  5.     
  6.     def __init__(self, filename):
  7.         self.filename = filename
  8.     
  9.     def read(self):
  10.         
  11.         headers = [
  12.             'f1', # 全国地方公共団体コード(JIS X0401、X0402)
  13.             'f2', # (旧)郵便番号(5桁)
  14.             'f3', # 郵便番号(7桁)
  15.             'f4', # 都道府県名 - 半角カタカナ
  16.             'f5', # 市区町村名 - 半角カタカナ
  17.             'f6', # 町域名 - 半角カタカナ
  18.             'f7', # 都道府県名 - 漢字
  19.             'f8', # 市区町村名 - 漢字
  20.             'f9', # 町域名 - 漢字
  21.             'f10', # 一町域が二以上の郵便番号で表される場合の表示
  22.             'f11', # 小字毎に番地が起番されている町域の表示
  23.             'f12', # 丁目を有する町域の場合の表示
  24.             'f13', # 一つの郵便番号で二以上の町域を表す場合の表示
  25.             'f14', # 更新の表示
  26.             'f15' # 変更理由
  27.         ]
  28.         
  29.         data = {}
  30.         
  31.         with zipfile.ZipFile(self.filename, 'r') as handle:
  32.             f = cStringIO.StringIO(handle.read('KEN_ALL.CSV'))
  33.             eid = 1
  34.             for line in f:
  35.                 uni_line = unicode(line, 'ms932').strip()
  36.                 values = [v.strip('"') for v in uni_line.split(',')]
  37.                 data[eid] = { k:v for k,v in zip(headers, values) }
  38.                 eid += 1
  39.         return { '_default' : data }
  40.     
  41.     def write(self, data):
  42.         pass
  43.             
  44.     def close(self):
  45.         pass
  46.         
  47.         
  48.         
  49. # データベースオープン 引数はファイル名
  50. db = tinydb.TinyDB('ken_all.zip', storage=CSVStorage)
  51. q = tinydb.Query()
  52. # 郵便番号検索
  53. for row in db.search(q.f3 == '0640941'):
  54.     print row['f3'], row['f7'], row['f8'], row['f9']
  55. print '-'*10
  56. # 住所に「銀座」を含むものを検索
  57. for row in db.search(q.f9.matches(u'.*銀座.*')):
  58.     print row['f3'], row['f7'], row['f8'], row['f9']




実行結果


$ python sample.py
0640941 北海道 札幌市中央区 旭ケ丘
----------
0691331 北海道 夕張郡長沼町 銀座
3220052 栃木県 鹿沼市 銀座
3600032 埼玉県 熊谷市 銀座
3670052 埼玉県 本庄市 銀座
1040061 東京都 中央区 銀座
9300991 富山県 富山市 新庄銀座
3940022 長野県 岡谷市 銀座
3940023 長野県 岡谷市 東銀座
3950031 長野県 飯田市 銀座
4240817 静岡県 静岡市清水区 銀座
4130013 静岡県 熱海市 銀座町
4140028 静岡県 伊東市 銀座元町
4750874 愛知県 半田市 銀座本町
4480845 愛知県 刈谷市 銀座
5220088 滋賀県 彦根市 銀座町
6128089 京都府 京都市伏見区 銀座町
7450032 山口県 周南市 銀座
7450033 山口県 周南市 みなみ銀座
7700916 徳島県 徳島市 銀座
8040076 福岡県 北九州市戸畑区 銀座




狙い通りの結果です。


関連記事

テーマ:プログラミング - ジャンル:コンピュータ

  1. 2016/02/02(火) 23:06:01|
  2. Python
  3. | トラックバック:0
  4. | コメント:0
  5. | 編集
<<Python ORM Ponyを使ってデータベース操作 | ホーム | Python 軽量ドキュメントデータベース TinyDB>>

コメント

コメントの投稿


管理者にだけ表示を許可する

トラックバック

トラックバック URL
http://symfoware.blog68.fc2.com/tb.php/1831-c5303a03
この記事にトラックバックする(FC2ブログユーザー)