2017年11月3日金曜日

【Python】簡易ドキュメントデータベース「TinyDB」を使ってみる

屋敷森
Ricoh GR

TinyDBとは

 Python用の簡易型のドキュメントデータベースです。
 サーバーを立てる必要がなく、データはJSON形式で保存されます。
 インストールもpipで簡単にできるので、ちょっとしたデータベースを作りたいときにお手軽に使えます。

 この記事では、自分用のメモを兼ねて簡単に使い方を記載します。
 詳しいドキュメントは「Welcome to TinyDB! — TinyDB 3.6.0 documentation」を御覧ください。

インストール方法

 pipで簡単にインストールできます。

pip install tinydb

使い方

ライブラリのインポート
from tinydb import TinyDB, Query
データベースの作成・オープン

 TinyDBメソッドにファイル名(必要ならパスを入れて)を引数で指定してデータベースを作成・オープンします。既にファイルがあれば開き、なければ新規に作成します。

# データベースの作成・オープン
db = TinyDB("db.json")

 なお、インメモリーで使う場合は、次のように開きます。

from tinydb.storages import MemoryStorage
db = TinyDB(storage=MemoryStorage)
テーブルの作成・指定

 データベースの中にテーブルを作成するとき(あるは既存のテーブルを開くとき)は、table() メソッドの引数で指定します。なお、テーブルを指定しないで使用することも可能です。この場合は「_default」というテーブルが使用されます。

tb_fruit = db.table("fruit")
tb_animal = db.table("animal")

 データベース内にあるテーブルの一覧は tables()メソッドで取得します。テーブルのリストが戻り値となります。

tables = db.tables()
データの追加

 一件ずつデータを追加するときは insert()メソッドに辞書を引数として渡します。
 後述しますが、戻り値としてドキュメントIDが返ります。
 テーブルを省略した形でデータベースから直接 insert()メソッドを使うと「_default」テーブルに追加されます。他のメソッドも同様です。

db.insert({"kind": "ネギ", "count": 1}) #テーブルを指定しない場合「_defaul」テーブルに追加される
tb_fruit.insert({"kind": "リンゴ", "count": 2})
tb_animal.insert({"kind": "サル", "count": 3})

 複数のデータを追加するときは、insert_multiple()メソッドを使います。引数は辞書のリスト、戻り値はドキュメントIDのリストとなります。

new_fruits = [{"kind": "オレンジ", "count": 4}, {"kind": "メロン", "count": 10}]
tb_fruit.insert_multiple(new_fruits)
new_animals = [{"kind": "キリン", "count": 2}, {"kind": "サル", "count": 10}]
tb_animal.insert_multiple(new_animals)
データの一覧出力 

 all()メソッドでテーブルのデータの一覧が出力されます。

# fruitテーブルのデータを一覧出力
tb_fruit.all()

#データベースに使用した場合は _defaultテーブルのデータが出力される
db.all()
データの検索

 検索にはQueryオブジェクトを使います。

query = Query()

# search()は該当するドキュメント(辞書)のリストが出力
tb_animal.search(query.kind == "サル")  # 該当するドキュメントのリストが出力
tb_fruit.search(query.kind == "キウイ")  # 該当するものがない場合は空のリストで出力
tb_fruit.search(query.count > 3) # 比較演算子が使える

# get()は該当する1件のみの出力
# 戻り値はドキュメント
tb_animal.get(query.kind == "サル")

# contains()は該当するドキュメントがあるか否か(戻り値は True / False)
tb_fruit.contains(query.kind == "オレンジ")
tb_fruit.contains(query.kind == "キウイ")

# count()は該当するドキュメントの数
print(tb_animal.count(query.kind == "サル"))
print("\n---------\n")
データの更新

 データの更新にはupdate()メソッドを使います。

# kindがキリンのドキュメントのcount値を1に更新
tb_animal.update({"count": 1}, query.kind == "キリン")

# 検索条件が複数ドキュメントに該当する場合はすべてのドキュメントが更新される
tb_animal.update({"count": 5}, query.kind == "サル")
データの削除

 データの削除にはremove()メソッドを使います。

# countが5未満のドキュメントを削除
tb_fruit.remove(query.count < 5)
ドキュメントID

 テーブル内の各ドキュメントにはそれぞれドキュメントIDが自動で振られます。
 insert()メソッドでデータ(ドキュメント)を挿入した場合の戻り値は挿入したドキュメントのIDとなり、insert_mulipleで挿入した場合の戻り値は挿入したドキュメントのIDのリストとなります。

# 挿入したデータにはドキュメントIDが振られる
id = tb_fruit.insert({"kind": "ブドウ", "count": 2})

# 複数挿入した場合はIDがリストで返る
new_fruits = [{"kind": "パパイヤ", "count": 12}, {"kind": "マンゴー", "count": 15}]
ids = tb_fruit.insert_multiple(new_fruits)
print("パパイヤとマンゴーのdocumentID: {0}".format(ids))

 既にテーブルにあるドキュメントのIDを取得したい場合は、ドキュメントのdoc_idプロパティから取得できます。

id = tb_fruit.get(query.kind == "メロン").doc_id  # documnet.doc_idでIDを取得

 get() は doc_id 引数、 contains()、 update()、 remove() はdoc_ids引数(IDのリスト)でドキュメントを指定できます。

# get()はdoc_id引数、 contains(), update(), remove()でdoc_ids引数で指定できる
tb_fruit.get(doc_id=id)
tb_fruit.update({"kind": "メロン", "count": 10, "size": "large"}, doc_ids=[id])
テーブル内のデータの個数

 Len()関数で引数にテーブルを指定するとテーブル内に含まれるデータの個数が返ります。

# fruitテーブルのデータの個数を出力
len(tb_fruit)

# データベースを指定すると _defaultテーブルおデータの個数が出力される
len(db)
テーブル内のすべてのデータの削除

 purge()メソッドでテーブル内のすべてのデータが削除されます。

tb_fruit.purge()
tb_animal.purge()
db.purge()  # _defaultテーブルのデータのみ適用される

サンプルコード

from pprint import pprint
from tinydb import TinyDB, Query

db_path = "db.json"

# データベースの作成・オープン
db = TinyDB(db_path)

# インメモリーで使う場合
# from tinydb.storages import MemoryStorage
# db = TinyDB(storage=MemoryStorage)


# テーブルの作成・指定(未指定の場合は_defaultというテーブルが使用される)
tb_fruit = db.table("fruit")
tb_animal = db.table("animal")

# データベースにあるテーブルの一覧リスト
tables = db.tables()
print("# データベースにあるテーブルの一覧リスト")
pprint(tables)
print("\n---------\n")

# 一件ずつのデータの追加
db.insert({"kind": "ネギ", "count": 1})
tb_fruit.insert({"kind": "リンゴ", "count": 2})
tb_animal.insert({"kind": "サル", "count": 3})

# 複数のデータの追加
new_fruits = [{"kind": "オレンジ", "count": 4}, {"kind": "メロン", "count": 10}]
tb_fruit.insert_multiple(new_fruits)
new_animals = [{"kind": "キリン", "count": 2}, {"kind": "サル", "count": 10}]
tb_animal.insert_multiple(new_animals)

# データの一覧出力
print("# データの一覧出力")
print("_defaultテーブルのデータ")
pprint(db.all())  # _defaultテーブルのデータのみ適用される
print("fruitテーブルのデータ")
pprint(tb_fruit.all())
print("animalテーブルのデータ")
pprint(tb_animal.all())
print("\n---------\n")

# データの検索
query = Query()
# search()は該当するものがリストで出力
print("# データの検索")
pprint(tb_animal.search(query.kind == "サル"))  # 該当するものがリストで出力
pprint(tb_fruit.search(query.kind == "キウイ"))  # 該当するものがない場合は空のリストで出力
pprint(tb_fruit.search(query.count > 3))
# get()は1件のみの出力
pprint(tb_animal.get(query.kind == "サル"))
# contains()は該当するものがあるか否か
print(tb_fruit.contains(query.kind == "オレンジ"))
print(tb_fruit.contains(query.kind == "キウイ"))
# count()は該当するものの数
print(tb_animal.count(query.kind == "サル"))
print("\n---------\n")

# データの更新
print("# データの更新")
tb_animal.update({"count": 1}, query.kind == "キリン")
tb_animal.update({"count": 5}, query.kind == "サル")  # 複数該当する場合はすべての項目で更新
pprint(tb_animal.all())
print("\n---------\n")

# データの削除
print("# データの削除")
tb_fruit.remove(query.count < 5)
pprint(tb_fruit.all())
print("\n---------\n")

# ドキュメントID
# 挿入したデータにはドキュメントIDが振られる
print("# ドキュメントID")
id = tb_fruit.insert({"kind": "ブドウ", "count": 2})
print("ブドウのdocumentID: {0}".format(id))

# 複数挿入した場合はidがリストで返る
new_fruits = [{"kind": "パパイヤ", "count": 12}, {"kind": "マンゴー", "count": 15}]
ids = tb_fruit.insert_multiple(new_fruits)
print("パパイヤとマンゴーのdocumentID: {0}".format(ids))

# documnet.doc_idでIDを取得
id = tb_fruit.get(query.kind == "メロン").doc_id
print("メロンのdocumentID: {0}".format(id))
print("\n---------\n")

# get()はdoc_id引数、 contains(), update(), remove()でdoc_ids引数で指定できる
print("# ドキュメントIDでのデータの指定")
print("doc_id:{0} は {1}".format(id, tb_fruit.get(doc_id=id)))
tb_fruit.update({"kind": "メロン", "count": 10, "size": "large"}, doc_ids=[id])
print("doc_id:{0} は {1}".format(id, tb_fruit.get(doc_id=id)))
print("\n---------\n")

# データの個数
print("# データの個数")
print("db(_defaultテーブル)のデータの個数 = {0}".format(len(db)))
print("fruitテーブルのデータの個数 = {0}".format(len(tb_fruit)))
print("animalテーブルのデータの個数 = {0}".format(len(tb_animal)))
print("\n---------\n")

# すべてのデータの削除
print("# すべてのデータの削除")
db.purge()  # _defaultテーブルのデータのみ適用される
tb_fruit.purge()
tb_animal.purge()
print("_defaultテーブルのデータ")
pprint(db.all())
print("fruitテーブルのデータ")
pprint(tb_fruit.all())
print("animalテーブルのデータ")
pprint(tb_animal.all())
print("\n---------\n")

0 件のコメント: