ものすごく手軽にPythonでNoSQLを扱えるy_serialが気に入った

どうも、すのまるです。
ブログ開設の記事に、ちょこっとはてブをつけて頂いたようで、嬉しい限りです。
あのはてブは、このブログへの期待と受け取って、面白い記事を書いていきます。

さて、今日は Pythonのライブラリ の紹介です。

AV女優.com では、最近、データの管理画面を作っています。 AV女優.com は、ゴンゾーと二人で運用しているサイトです。
そのため、データの管理などは全て テキストベース です。
GUIの管理画面は工数削減のために、作ったりしません。

しかし、そんな中、女優の画像の管理のために、GUIの管理画面を作る必要が出てきました。
作るといっても、出来るだけ簡単に、コード量は減らしたいものです。
データベーステーブルでさえ、定義したくありません。

そこで見つけたライブラリが、 y_serial です。

y_serialとは

y_serialは、SQLiteライクなNoSQLライブラリです(厳密に言うと、SQLiteの上にy_serialが実装されています)。
ライブラリを読み込むだけで、NoSQLを使えます。
皆さんには、コードを見て頂いたほうが早いですね:

# -*- coding: utf-8 -*-

import y_serial

# sqliteのファイルを作成
cnn = y_serial.Main('/tmp/av-jyo.sqlite')

# 挿入するデータを定義
entry = {
    'draft': False,
    'dow': 'tue',
    'title': 'hello',
    'content':
    'this is content.'}
# データをentriesテーブルに挿入
cnn.insert(entry, 'dow-tue title-hello', 'entries')

entry = {
    'draft': False,
    'dow': 'tue',
    'title': 'good morning',
    'content': 'content is nothing.'}
cnn.insert(entry, 'draft dow-tue title-good-morning', 'entries')

# タイトルがhelloのデータをentriesから取得
print cnn.select('title-hello', 'entries')
# 火曜日の下書き記事をentriesから取得
print cnn.select('dow-tue,draft', 'entries')

ほんの数行で、 データの検索と保存 が出来ます。
notesにスペース区切りで文字列を設定します。
これだけで、1データに対して複数インデックスを張ることが出来ます。

このy_serial、簡単過ぎて説明することが無いレベルです。
しかし、もう少し記事のボリュームが欲しいですね。
インストールから、データの書き込みと読み込み、削除まで、お付き合い下さい。

インストール手順

まず、公式サイトから、ソースをダウンロードします。

ダウンロードしたソースを、自分のプロジェクトが読み込める場所に配置します。
あとは、Pythonの構文に従って読み込むだけです。
例えば、プロジェクトのルートに、y_serial.pyという名前で配置したのであれば:

# y_serialモジュールをインポートする。
# 今回は、プロジェクトルートにy_serial.pyという名前で配置したため、y_serialを指定する。
import y_serial
# sqliteのファイルにデータを保存する。そのため、sqliteのファイルパスを指定する。
# ファイルは自動で生成される。
cnn = y_serial.Main('/tmp/av-jyo.sqlite')

これで読み込めます。
もちろん、 /usr/share/pyshared などに配置しても構いません。

1ファイルのライブラリなので、インストールが簡単です。

また、標準ライブラリだけで動かすことが出来るため、依存を解決する必要もありません。

  • sqlite3 (as of Python v2.5)
  • zlib (for compression)
  • cPickle (for serializing objects)

データの書き込み

ライブラリを読み込んだ後は、データを書き込みます:

# 挿入するデータは、cPickleが使えるオブジェクトなら何でも良い。
# 基本的にディクショナリが使いやすい。
entry = {
    'dow': 'tue',
    'title': 'hello',
    'content': 'this is content.'}
# データをentriesテーブルに挿入する。
# 第1引数に保存するデータを渡す。
# 第2引数に検索時に使用するnotesをスペース区切りの文字列で渡す。
# 第3引数にテーブル名を指定する。
cnn.insert(entry, 'dow-tue title-hello', 'entries')

書き込むデータは、cPickleがシリアライズ出来るオブジェクトであれば、何でも構いません [1]
y_serialが自動でオブジェクトをシリアライズします。

また、notesと呼ばれるインデックスには空白で区切った文字列を指定します。
もちろん、日本語も使用できます:

entry = {
    'dow': 'tue',
    'title': 'hello',
    'author': u'すのまる',
    'content': 'this is content.'}
cnn.insert(entry, u'author-すのまる', 'entries')

ただし、コンマは使わないでください。
次のデータの読み込み時に、使用します。

データの読み込み

データを書き込んだら、次は読み込みです:

# dow-tueのnotesが付いたデータを1件取得する。
# {'content': 'this is content.', 'dow': 'tue', 'title': 'hello'}
print cnn.select('dow-tue', 'entries')
# dow-tueとtitle-helloのnotesが付いたデータを1件取得する。
# {'content': 'this is content.', 'dow': 'tue', 'title': 'hello'}
print cnn.select('dow-tue,title-hello', 'entries')
# dow-tueのnotesが付いたデータを全て取得する
# [{'content': 'this is content.', 'dow': 'tue', 'title': 'hello'}]
print cnn.selectdic('dow-tue', 'entries')

基本的に書き込んだnotesを使用して、検索します。
このnotesには正規表現を使用できます:

entry = {
    'title': 'hello1',
    'content': 'this is content1.'}
cnn.insert(entry, u'title-hello1', 'entries')
# title-hello[0-9]のnotesが付いたデータを1件取得する。
# {'content': 'this is content1.', 'title': 'hello1'}
print cnn.select('title-hello[0-9]', 'entries')
# title-から始まるnotesが付いたデータを1件取得する。
# notesは全て前方一致検索になる。
# {'content': 'this is content1.', 'title': 'hello1'}
print cnn.select('title-', 'entries')

n番目のデータを取り出すには、notesの代わりに数値nを指定します:

# 最も新しいデータを1件取得する。
# {'content': 'this is content1.', 'title': 'hello1'}
print cnn.select(0, 'entries')
# 1つ古いデータを1件取得する。
# {'content': 'this is content.', 'title': 'hello', 'dow': 'tue',
#  'author': u'\u3059\u306e\u307e\u308b'}
print cnn.select(1, 'entries')

データの削除

最後にデータを削除します:

# title-から始まるnotesを持つデータを全て削除する。
cnn.delete('title-', 'entries')
# None
print cnn.select('title-hello', 'entries')

データの参照と同時に、データを削除することも出来ます:

# author-から始まるnotesを持つデータを1つ取り出し、削除する。
# {'content': 'this is content.', 'title': 'hello', 'dow': 'tue',
# 'author': u'\u3059\u306e\u307e\u308b'}
print cnn.select('author-', 'entries', POP=True)
# None
print cnn.select('author', 'entries')

テーブルの削除には、droptableを使います:

cnn.droptable('entries')

それにしても、本当に素敵なライブラリです。
Pythonにおいて、データベースを使用しないデータの永続化には、 SQLiteshelve があります [2]
しかし、SQLiteはあくまでRDBであるがため、その扱いが面倒です。
Shelveは、データの永続化は出来ますが、データの検索をこちらで用意する必要があります。

SQLiteよりも手軽に扱え、Shelveよりも、便利に使える。
それが y_serial です。

現在、Flaskとy_serialを使った、画像管理アプリケーションの構築を予定しています。
この2ライブラリがあれば、楽しく作れそうです。

他に良いライブラリがあれば、コメントで教えてください。
それでは。

[1] cPickleについては、 Pythonの公式ドキュメント 11.1 pickle を参考にしてください。
[2] shelveについては、 Pythonの公式ドキュメント 11.4 shelve を参考にしてください。

ちょっと一言

sunomaru

@sunomaru

最近、ニンテンドー64をやる機会がありました。マリオカートをやったのですが、カセットが錆びついてしまって、うまく読み込めません。そもそも、カセットというのが時代を感じますね。