SQLite 拡張機能
デフォルトのSqliteDatabase
は、既に多くの SQLite 固有の機能を含んでいます。
playhouse.sqlite_ext
には、さらに多くの SQLite 機能が含まれています。これらには以下が含まれます。
バックアップ API を使用したオンラインバックアップのサポート:
backup_to_file()
追加のヘルパー(ブルームフィルタなど)
はじめに
このドキュメントで説明されている機能を使い始めるには、playhouse.sqlite_ext
モジュールから SqliteExtDatabase
クラスを使用することをお勧めします。さらに、一部の機能には playhouse._sqlite_ext
C 拡張機能が必要です。これらの機能については、ドキュメントで説明します。
SqliteExtDatabase
のインスタンス化
from playhouse.sqlite_ext import SqliteExtDatabase
db = SqliteExtDatabase('my_app.db', pragmas=(
('cache_size', -1024 * 64), # 64MB page-cache.
('journal_mode', 'wal'), # Use WAL-mode (you should always use this!).
('foreign_keys', 1))) # Enforce foreign-key constraints.
API
- class SqliteExtDatabase(database[, pragmas=None[, timeout=5[, c_extensions=None[, rank_functions=True[, hash_functions=False[, regexp_function=False[, bloomfilter=False]]]]]]])
- パラメータ
pragmas (list) – 接続を開くたびに設定する pragma のキーと値を含む 2 つのタプルのリスト。
timeout – SQLite ドライバのビジータイムアウトを設定します(秒単位)。
c_extensions (bool) – C 拡張機能による高速化を使用するかどうかを宣言します。
True
に設定され、拡張モジュールが利用できない場合は、ImproperlyConfigured
例外が発生します。rank_functions (bool) – 検索結果のランキング関数を有効にします。
hash_functions (bool) – ハッシュ関数を有効にします(md5、sha1 など)。
regexp_function (bool) – REGEXP 関数を有効にします。
bloomfilter (bool) – ブルームフィルタ を有効にします。
SqliteDatabase
を拡張し、ユーザー定義関数、pragma などの宣言メソッドを継承します。
- class CSqliteExtDatabase(database[, pragmas=None[, timeout=5[, c_extensions=None[, rank_functions=True[, hash_functions=False[, regexp_function=False[, bloomfilter=False[, replace_busy_handler=False]]]]]]]])
- パラメータ
pragmas (list) – 接続を開くたびに設定する pragma のキーと値を含む 2 つのタプルのリスト。
timeout – SQLite ドライバのビジータイムアウトを設定します(秒単位)。
c_extensions (bool) – C 拡張機能による高速化を使用するかどうかを宣言します。
True
に設定され、拡張モジュールが利用できない場合は、ImproperlyConfigured
例外が発生します。rank_functions (bool) – 検索結果のランキング関数を有効にします。
hash_functions (bool) – ハッシュ関数を有効にします(md5、sha1 など)。
regexp_function (bool) – REGEXP 関数を有効にします。
bloomfilter (bool) – ブルームフィルタ を有効にします。
replace_busy_handler (bool) – よりスマートなビジーハンドラ実装を使用します。
SqliteExtDatabase
を拡張し、playhouse._sqlite_ext
拡張モジュールが利用可能である必要があります。- on_commit(fn)
現在の接続でトランザクションがコミットされるたびに実行されるコールバックを登録します。コールバックはパラメータを受け取りません。戻り値は無視されます。
ただし、コールバックが
ValueError
を発生させると、トランザクションは中断され、ロールバックされます。例
db = CSqliteExtDatabase(':memory:') @db.on_commit def on_commit(): logger.info('COMMITing changes')
- on_rollback(fn)
現在の接続でトランザクションがロールバックされるたびに実行されるコールバックを登録します。コールバックはパラメータを受け取りません。戻り値は無視されます。
例
@db.on_rollback def on_rollback(): logger.info('Rolling back changes')
- on_update(fn)
データベースへの書き込み(*UPDATE*、*INSERT*、または *DELETE* クエリによる)が行われるたびに実行されるコールバックを登録します。コールバックは次のパラメータを受け入れる必要があります。
query
- クエリの種類、*INSERT*、*UPDATE*、または *DELETE* のいずれか。データベース名 - デフォルトのデータベース名は *main* です。
テーブル名 - 変更されたテーブルの名前。
rowid - 変更された行の rowid。
コールバックの戻り値は無視されます。
例
db = CSqliteExtDatabase(':memory:') @db.on_update def on_update(query_type, db, table, rowid): # e.g. INSERT row 3 into table users. logger.info('%s row %s into table %s', query_type, rowid, table)
- changes()
現在開いているトランザクションで変更された行数を返します。
- autocommit
autocommit が有効かどうかを示すブール値を返すプロパティです。トランザクション内(または
atomic()
ブロック内)でない限り、デフォルト値はTrue
になります。例
>>> db = CSqliteExtDatabase(':memory:') >>> db.autocommit True >>> with db.atomic(): ... print(db.autocommit) ... False >>> db.autocommit True
- backup(destination[, pages=None, name=None, progress=None])
- パラメータ
destination (SqliteDatabase) – バックアップの宛先となるデータベースオブジェクト。
pages (int) – 1イテレーションあたりのページ数。-1 のデフォルト値は、すべてのページを1ステップでバックアップすることを示します。
name (str) – ソースデータベースの名前(複数のデータベースを ATTACH DATABASE を使用してロードした場合、異なる場合があります)。デフォルトは "main" です。
progress – プログレスコールバック。残りのページ数、総ページ数、バックアップが完了したかどうかという3つのパラメータで呼び出されます。
例
master = CSqliteExtDatabase('master.db') replica = CSqliteExtDatabase('replica.db') # Backup the contents of master to replica. master.backup(replica)
- backup_to_file(filename[, pages, name, progress])
- パラメータ
filename – データベースバックアップを保存するファイル名。
pages (int) – 1イテレーションあたりのページ数。-1 のデフォルト値は、すべてのページを1ステップでバックアップすることを示します。
name (str) – ソースデータベースの名前(複数のデータベースを ATTACH DATABASE を使用してロードした場合、異なる場合があります)。デフォルトは "main" です。
progress – プログレスコールバック。残りのページ数、総ページ数、バックアップが完了したかどうかという3つのパラメータで呼び出されます。
現在のデータベースをファイルにバックアップします。バックアップされたデータはデータベースダンプではなく、実際の SQLite データベースファイルです。
例
db = CSqliteExtDatabase('app.db') def nightly_backup(): filename = 'backup-%s.db' % (datetime.date.today()) db.backup_to_file(filename)
- blob_open(table, column, rowid[, read_only=False])
- パラメータ
table (str) – データを含むテーブル名。
column (str) – データを含む列名。
rowid (int) – 取得する行のID。
read_only (bool) – ブロブを読み取り専用で開きます。
- 戻り値
基になるバイナリデータへの効率的なアクセスを提供する
Blob
インスタンス。- 戻り値の型
例
class Image(Model): filename = TextField() data = BlobField() buf_size = 1024 * 1024 * 8 # Allocate 8MB for storing file. rowid = Image.insert({Image.filename: 'thefile.jpg', Image.data: ZeroBlob(buf_size)}).execute() # Open the blob, returning a file-like object. blob = db.blob_open('image', 'data', rowid) # Write some data to the blob. blob.write(image_data) img_size = blob.tell() # Read the data back out of the blob. blob.seek(0) image_data = blob.read(img_size)
- class RowIDField
SQLite の
rowid
フィールドに対応する主キーフィールド。詳細は、SQLite のrowid テーブルに関するドキュメントを参照してください。例
class Note(Model): rowid = RowIDField() # Will be primary key. content = TextField() timestamp = TimestampField()
- class DocIDField
RowIDField
のサブクラスで、主キーにdocid
という規約を特に使用する仮想テーブルで使用します。私の知る限り、これは FTS3 および FTS4 フルテキスト検索拡張機能を使用するテーブルにのみ関係します。注意
FTS3 および FTS4 では、"docid" は単に "rowid" のエイリアスです。混乱を避けるために、常に
RowIDField
を使用し、DocIDField
は使用しない方が良いでしょう。class NoteIndex(FTSModel): docid = DocIDField() # "docid" is used as an alias for "rowid". content = SearchField() class Meta: database = db
- class AutoIncrementField
SQLite はデフォルトで、行が削除された後に主キー値を再利用することがあります。削除に関係なく、主キーが常に単調増加することを保証するには、
AutoIncrementField
を使用する必要があります。この機能にはわずかなパフォーマンスコストがあります。詳細は、SQLite のautoincrement に関するドキュメントを参照してください。
- class JSONField(json_dumps=None, json_loads=None, ...)
json1 拡張機能と連携するように設計された特殊なメソッドを持つ、JSON データの保存に適したフィールドクラス。
SQLite 3.9.0 では、拡張ライブラリとしてJSON サポートが追加されました。SQLite json1 拡張機能は、JSON データの操作のためのいくつかのヘルパー関数を提供します。これらの API は、特殊なフィールドタイプである
JSONField
のメソッドとして公開されています。JSON 構造内の特定のオブジェクトキーまたは配列インデックスにアクセスまたは変更するには、
JSONField
を辞書/リストのように扱うことができます。- パラメータ
json_dumps – (オプション)データを JSON 文字列にシリアル化する関数。指定しない場合、stdlib の
json.dumps
を使用します。json_loads – (オプション)JSON を Python オブジェクトに逆シリアル化する関数。指定しない場合、stdlib の
json.loads
を使用します。
注記
JSON のシリアル化または逆シリアル化をカスタマイズするには、カスタムの
json_dumps
とjson_loads
呼び出し可能オブジェクトを指定できます。これらの関数は、単一のパラメータ(シリアル化するオブジェクト、および JSON 文字列)を受け入れる必要があります。stdlib の JSON 関数の parameters を変更するには、functools.partial
を使用できます。# Do not escape unicode code-points. my_json_dumps = functools.partial(json.dumps, ensure_ascii=False) class SomeModel(Model): # Specify our custom serialization function. json_data = JSONField(json_dumps=my_json_dumps)
Peewee で SQLite json1 拡張機能を使用する例を見てみましょう。ここでは、json1 拡張機能をテストするためのデータベースとシンプルなモデルを準備します。
>>> from playhouse.sqlite_ext import * >>> db = SqliteExtDatabase(':memory:') >>> class KV(Model): ... key = TextField() ... value = JSONField() ... class Meta: ... database = db ... >>> KV.create_table()
データの保存は期待通りに機能します。辞書やリストを JSON としてシリアル化する必要はありません。Peewee によって自動的に行われます。
>>> KV.create(key='a', value={'k1': 'v1'}) <KV: 1> >>> KV.get(KV.key == 'a').value {'k1': 'v1'}
辞書検索を使用して、JSON データの特定の部分にアクセスできます。
>>> KV.get(KV.value['k1'] == 'v1').key 'a'
update()
メソッドを使用して、JSON 値をインプレースで更新できます。「k1=v1」は保存されます。>>> KV.update(value=KV.value.update({'k2': 'v2', 'k3': 'v3'})).execute() 1 >>> KV.get(KV.key == 'a').value {'k1': 'v1', 'k2': 'v2', 'k3': 'v3'}
既存のデータをアトミックに更新したり、値を
None
に設定してキーを削除したりすることもできます。次の例では、「k1」の値を更新し、「k3」を削除します(「k2」は変更されません)。>>> KV.update(value=KV.value.update({'k1': 'v1-x', 'k3': None})).execute() 1 >>> KV.get(KV.key == 'a').value {'k1': 'v1-x', 'k2': 'v2'}
set()
メソッドを使用して、JSONデータの個々の部分を設定することもできます。>>> KV.update(value=KV.value['k1'].set('v1')).execute() 1 >>> KV.get(KV.key == 'a').value {'k1': 'v1', 'k2': 'v2'}
set()
メソッドは、スカラー値に加えてオブジェクトでも使用できます。>>> KV.update(value=KV.value['k2'].set({'x2': 'y2'})).execute() 1 >>> KV.get(KV.key == 'a').value {'k1': 'v1', 'k2': {'x2': 'y2'}}
remove()
を使用して、JSON データの個々の部分をアトミックに削除することもできます。>>> KV.update(value=KV.value['k2'].remove()).execute() 1 >>> KV.get(KV.key == 'a').value {'k1': 'v1'}
json_type()
メソッドを使用して、JSONデータの特定の場所に保存されている値の型を取得することもできます。>>> KV.select(KV.value.json_type(), KV.value['k1'].json_type()).tuples()[:] [('object', 'text')]
入れ子になった値を追加し、
tree()
メソッドを使用して、その内容を再帰的に反復処理する方法を見てみましょう。>>> KV.create(key='b', value={'x1': {'y1': 'z1', 'y2': 'z2'}, 'x2': [1, 2]}) <KV: 2> >>> tree = KV.value.tree().alias('tree') >>> query = KV.select(KV.key, tree.c.fullkey, tree.c.value).from_(KV, tree) >>> query.tuples()[:] [('a', '$', {'k1': 'v1'}), ('a', '$.k1', 'v1'), ('b', '$', {'x1': {'y1': 'z1', 'y2': 'z2'}, 'x2': [1, 2]}), ('b', '$.x2', [1, 2]), ('b', '$.x2[0]', 1), ('b', '$.x2[1]', 2), ('b', '$.x1', {'y1': 'z1', 'y2': 'z2'}), ('b', '$.x1.y1', 'z1'), ('b', '$.x1.y2', 'z2')]
tree()
とchildren()
メソッドは強力です。それらの使用方法の詳細については、json1 拡張機能のドキュメントを参照してください。また、
JSONField
のルックアップはチェーンできます。>>> query = KV.select().where(KV.value['x1']['y1'] == 'z1') >>> for obj in query: ... print(obj.key, obj.value) ... 'b', {'x1': {'y1': 'z1', 'y2': 'z2'}, 'x2': [1, 2]}
詳細は、sqlite json1 のドキュメントを参照してください。
- __getitem__(item)
- パラメータ
item – JSON データ内の特定のキーまたは配列インデックスにアクセスします。
- 戻り値
JSON データへのアクセスを公開する特別なオブジェクト。
- 戻り値の型
JSON データ内の特定のキーまたは配列インデックスにアクセスします。JSON オブジェクトの特定の部分の読み取りまたは変更に便利なメソッドを公開する
JSONPath
オブジェクトを返します。例
# If metadata contains {"tags": ["list", "of", "tags"]}, we can # extract the first tag in this way: Post.select(Post, Post.metadata['tags'][0].alias('first_tag'))
詳細な例については、
JSONPath
のAPI ドキュメントを参照してください。
- extract(*paths)
- パラメータ
paths – 抽出する1つ以上のJSONパス。
指定された JSON パスにある値を抽出します。複数のパスが指定されている場合、Sqlite は値を
list
として返します。
- extract_json(path)
- パラメータ
path (str) – JSON パス
指定されたパスにある値を JSON データ型として抽出します。これは、Sqlite 3.38 で追加された
->
演算子に対応します。
- extract_text(path)
- パラメータ
path (str) – JSON パス
指定されたパスにある値をSQLデータ型として抽出します。これは、Sqlite 3.38に追加された
->>
演算子に対応します。
- set(value[, as_json=None])
- パラメータ
value – スカラー値、リスト、または辞書。
as_json (bool) – 値をJSONとして扱うように強制します。この場合、事前にPythonでJSONとしてシリアライズされます。デフォルトでは、リストと辞書はシリアライズされるJSONとして扱われ、文字列と整数はそのまま渡されます。
JSONField
に格納されている値を設定します。json1拡張機能のjson_set()関数を使用します。
- replace(value[, as_json=None])
- パラメータ
value – スカラー値、リスト、または辞書。
as_json (bool) – 値をJSONとして扱うように強制します。この場合、事前にPythonでJSONとしてシリアライズされます。デフォルトでは、リストと辞書はシリアライズされるJSONとして扱われ、文字列と整数はそのまま渡されます。
JSONField
に格納されている既存の値を置き換えます。json1拡張機能のjson_replace()関数を使用します。
- insert(value[, as_json=None])
- パラメータ
value – スカラー値、リスト、または辞書。
as_json (bool) – 値をJSONとして扱うように強制します。この場合、事前にPythonでJSONとしてシリアライズされます。デフォルトでは、リストと辞書はシリアライズされるJSONとして扱われ、文字列と整数はそのまま渡されます。
JSONField
に値を挿入します。json1拡張機能のjson_insert()関数を使用します。
- append(value[, as_json=None])
- パラメータ
value – スカラー値、リスト、または辞書。
as_json (bool) – 値をJSONとして扱うように強制します。この場合、事前にPythonでJSONとしてシリアライズされます。デフォルトでは、リストと辞書はシリアライズされるJSONとして扱われ、文字列と整数はそのまま渡されます。
JSONField
に格納されている配列に追加します。json1拡張機能のjson_set()関数を使用します。
- update(data)
- パラメータ
data –
JSONField
に現在格納されているデータとマージするスカラー値、リスト、または辞書。特定のキーを削除するには、更新されたデータでそのキーをNone
に設定します。
RFC-7396 MergePatchアルゴリズムを使用して、パッチ(
data
パラメータ)を列データに適用することにより、新しいデータをJSON値にマージします。MergePatchはJSONオブジェクトの要素を追加、変更、または削除できるため、update()
はset()
とremove()
の両方の汎用的な置き換えです。MergePatchはJSON配列オブジェクトをアトミックに扱うため、update()
では配列に追加したり、配列の個々の要素を変更したりすることはできません。詳細と例については、SQLiteのjson_patch()関数のドキュメントを参照してください。
- remove()
JSONField
に格納されているデータを削除します。json1拡張機能のjson_remove関数を使用します。
- json_type()
列に格納されている値の型を識別する文字列を返します。
返される型は次のいずれかになります。
object
array
integer
real
true
false
text
null <– 文字列"null"は実際のNULL値を意味します
NULL <– 実際のNULL値はパスが見つからなかったことを意味します
json1拡張機能のjson_type関数を使用します。
- length()
列に格納されている配列の長さを返します。
json1拡張機能のjson_array_length関数を使用します。
- children()
children
関数はjson_each
に対応しており、提供されたJSON値を巡回し、最上位の配列またはオブジェクトの直下の子供を返すテーブル値関数です。パスが指定されている場合、そのパスは最上位の要素として扱われます。children()
の呼び出しによって返される行には、次の属性があります。key
: 親に対する現在の要素のキー。value
: 現在の要素の値。type
: データ型の1つ(json_type()
を参照)。atom
: プリミティブ型のスカラー値、配列とオブジェクトの場合はNULL
。id
: ツリー内の現在のノードを参照する一意のID。parent
: 含まれているノードのID。fullkey
: 現在の要素を記述する完全パス。path
: 現在の行のコンテナへのパス。
内部的には、このメソッドはjson1拡張機能のjson_each(ドキュメントリンク)関数を使用します。
使用例(
tree()
メソッドと比較してください)class KeyData(Model): key = TextField() data = JSONField() KeyData.create(key='a', data={'k1': 'v1', 'x1': {'y1': 'z1'}}) KeyData.create(key='b', data={'x1': {'y1': 'z1', 'y2': 'z2'}}) # We will query the KeyData model for the key and all the # top-level keys and values in it's data field. kd = KeyData.data.children().alias('children') query = (KeyData .select(kd.c.key, kd.c.value, kd.c.fullkey) .from_(KeyData, kd) .order_by(kd.c.key) .tuples()) print(query[:]) # PRINTS: [('a', 'k1', 'v1', '$.k1'), ('a', 'x1', '{"y1":"z1"}', '$.x1'), ('b', 'x1', '{"y1":"z1","y2":"z2"}', '$.x1')]
- tree()
tree
関数はjson_tree
に対応しており、提供されたJSON値を再帰的に巡回し、各レベルのキーに関する情報を返すテーブル値関数です。パスが指定されている場合、そのパスは最上位の要素として扱われます。tree()
の呼び出しによって返される行には、children()
の呼び出しによって返される行と同じ属性があります。key
: 親に対する現在の要素のキー。value
: 現在の要素の値。type
: データ型の1つ(json_type()
を参照)。atom
: プリミティブ型のスカラー値、配列とオブジェクトの場合はNULL
。id
: ツリー内の現在のノードを参照する一意のID。parent
: 含まれているノードのID。fullkey
: 現在の要素を記述する完全パス。path
: 現在の行のコンテナへのパス。
内部的には、このメソッドはjson1拡張機能のjson_tree(ドキュメントリンク)関数を使用します。
使用例
class KeyData(Model): key = TextField() data = JSONField() KeyData.create(key='a', data={'k1': 'v1', 'x1': {'y1': 'z1'}}) KeyData.create(key='b', data={'x1': {'y1': 'z1', 'y2': 'z2'}}) # We will query the KeyData model for the key and all the # keys and values in it's data field, recursively. kd = KeyData.data.tree().alias('tree') query = (KeyData .select(kd.c.key, kd.c.value, kd.c.fullkey) .from_(KeyData, kd) .order_by(kd.c.key) .tuples()) print(query[:]) # PRINTS: [('a', None, '{"k1":"v1","x1":{"y1":"z1"}}', '$'), ('b', None, '{"x1":{"y1":"z1","y2":"z2"}}', '$'), ('a', 'k1', 'v1', '$.k1'), ('a', 'x1', '{"y1":"z1"}', '$.x1'), ('b', 'x1', '{"y1":"z1","y2":"z2"}', '$.x1'), ('a', 'y1', 'z1', '$.x1.y1'), ('b', 'y1', 'z1', '$.x1.y1'), ('b', 'y2', 'z2', '$.x1.y2')]
- class JSONPath(field[, path=None])
- パラメータ
field (JSONField) – アクセスしようとするフィールドオブジェクト。
path (tuple) – JSONパスを構成するコンポーネント。
JSONField
で使用するための、JSONパスの便利なPython的な表現方法。JSONPath
オブジェクトは__getitem__
を実装し、パスコンポーネントを蓄積し、対応するjsonパス式に変換できます。- __getitem__(item)
- パラメータ
item – サブキーまたは配列インデックスにアクセスします。
- 戻り値
新しいパスを表す
JSONPath
。
JSONデータのサブキーまたは配列インデックスにアクセスします。JSONオブジェクトの特定の部分を読み取ったり変更したりするための便利なメソッドを公開する
JSONPath
オブジェクトを返します。例
# If metadata contains {"tags": ["list", "of", "tags"]}, we can # extract the first tag in this way: first_tag = Post.metadata['tags'][0] query = (Post .select(Post, first_tag.alias('first_tag')) .order_by(first_tag))
- set(value[, as_json=None])
- パラメータ
value – スカラー値、リスト、または辞書。
as_json (bool) – 値をJSONとして扱うように強制します。この場合、事前にPythonでJSONとしてシリアライズされます。デフォルトでは、リストと辞書はシリアライズされるJSONとして扱われ、文字列と整数はそのまま渡されます。
JSONデータの指定された場所に値を設定します。
json1拡張機能のjson_set()関数を使用します。
- replace(value[, as_json=None])
- パラメータ
value – スカラー値、リスト、または辞書。
as_json (bool) – 値をJSONとして扱うように強制します。この場合、事前にPythonでJSONとしてシリアライズされます。デフォルトでは、リストと辞書はシリアライズされるJSONとして扱われ、文字列と整数はそのまま渡されます。
JSONデータの指定された位置にある既存の値を置き換えます。
json1拡張機能のjson_replace()関数を使用します。
- insert(value[, as_json=None])
- パラメータ
value – スカラー値、リスト、または辞書。
as_json (bool) – 値をJSONとして扱うように強制します。この場合、事前にPythonでJSONとしてシリアライズされます。デフォルトでは、リストと辞書はシリアライズされるJSONとして扱われ、文字列と整数はそのまま渡されます。
JSONデータの指定された位置に新しい値を挿入します。
json1拡張機能のjson_insert()関数を使用します。
- append(value[, as_json=None])
- パラメータ
value – スカラー値、リスト、または辞書。
as_json (bool) – 値をJSONとして扱うように強制します。この場合、事前にPythonでJSONとしてシリアライズされます。デフォルトでは、リストと辞書はシリアライズされるJSONとして扱われ、文字列と整数はそのまま渡されます。
JSONデータの指定された位置にある配列に値を追加します。
json1拡張機能のjson_set()関数を使用します。
- update(data)
- パラメータ
**data** – スカラー値、リスト、または辞書。JSONデータの指定された位置にあるデータとマージされます。特定のキーを削除するには、更新されたデータでそのキーを
None
に設定します。
RFC-7396 MergePatchアルゴリズムを使用して、パッチ(
data
パラメータ)を列データに適用し、新しいデータをJSON値にマージします。MergePatchはJSONオブジェクトの要素を追加、変更、または削除できるため、update()
はset()
とremove()
の両方の汎用的な代替手段となります。MergePatchはJSON配列オブジェクトをアトミックに扱うため、update()
では配列への追加や配列の個々の要素の変更はできません。詳細と例については、SQLiteのjson_patch()関数のドキュメントを参照してください。
- json_type()
JSONデータの指定された位置に格納されている値の型を識別する文字列を返します。
返される型は次のいずれかになります。
object
array
integer
real
true
false
text
null <– 文字列"null"は実際のNULL値を意味します
NULL <– 実際のNULL値はパスが見つからなかったことを意味します
json1拡張機能のjson_type関数を使用します。
- length()
JSONデータの指定された位置にある配列の長さを返します。
json1拡張機能のjson_array_length関数を使用します。
- children()
指定された位置のJSONオブジェクトの直接の子孫を公開するテーブル値関数です。
JSONField.children()
も参照してください。
- tree()
指定された位置のJSONオブジェクトのすべての子孫を再帰的に公開するテーブル値関数です。
JSONField.tree()
も参照してください。
- class JSONBField(json_dumps=None, json_loads=None, ...)
ディスク上に
jsonb
形式で保存されたデータで使用できるフィールドクラス(Sqlite 3.45.0以降で使用可能)。クエリ方法によっては、データがエンコードされた形式で返される可能性があるため、このフィールドクラスは注意して使用する必要があります。例:>>> KV.create(key='a', value={'k1': 'v1'}) <KV: 1> >>> KV.get(KV.key == 'a').value b"l'k1'v1"
JSON値を取得するには、
fn.json()
またはヘルパーメソッドJSONBField.json()
を使用する必要があります。>>> kv = KV.select(KV.value.json()).get() >>> kv.value {'k1': 'v1'}
- class SearchField([unindexed=False[, column_name=None]])
全文検索仮想テーブルを表すモデルの列に使用されるフィールドクラス。全文検索拡張機能では、列への型指定や制約の指定が禁止されています。この動作は
SearchField
によって強制され、全文検索拡張機能と互換性のない設定が試行されると例外が発生します。ドキュメント検索インデックスの例モデル(タイムスタンプはテーブルに格納されますが、データは検索できません)
class DocumentIndex(FTSModel): title = SearchField() content = SearchField() tags = SearchField() timestamp = SearchField(unindexed=True)
- match(term)
- パラメータ
**term** (str) – 全文検索クエリ/用語
- 戻り値
MATCH
演算子に対応するExpression
。
Sqliteの全文検索は、すべてのインデックス付き列を含むテーブル全体を検索するか、個々の列を検索することができます。
match()
メソッドを使用して、単一の列への検索を制限できます。class SearchIndex(FTSModel): title = SearchField() body = SearchField() # Search *only* the title field and return results ordered by # relevance, using bm25. query = (SearchIndex .select(SearchIndex, SearchIndex.bm25().alias('score')) .where(SearchIndex.title.match('python')) .order_by(SearchIndex.bm25()))
代わりに、すべてのインデックス付き列を検索するには、
FTSModel.match()
メソッドを使用します。# Searches *both* the title and body and return results ordered by # relevance, using bm25. query = (SearchIndex .select(SearchIndex, SearchIndex.bm25().alias('score')) .where(SearchIndex.match('python')) .order_by(SearchIndex.bm25()))
- highlight(left, right)
- パラメータ
**left** (str) – ハイライトの開始タグ、例:
'<b>'
**right** (str) – ハイライトの終了タグ、例:
'</b>'
MATCH
演算子を使用して検索を実行する場合、FTS5は指定された列の一致するテキストをハイライト表示して返すことができます。# Search for items matching string 'python' and return the title # highlighted with square brackets. query = (SearchIndex .search('python') .select(SearchIndex.title.highlight('[', ']').alias('hi'))) for result in query: print(result.hi) # For example, might print: # Learn [python] the hard way
- snippet(left, right, over_length='...', max_tokens=16)
- パラメータ
**left** (str) – ハイライトの開始タグ、例:
'<b>'
**right** (str) – ハイライトの終了タグ、例:
'</b>'
**over_length** (str) – スニペットが最大トークン数を超えた場合にプレフィックスまたはサフィックスとして追加されるテキスト。
**max_tokens** (int) – 返される最大トークン数。**1~64でなければなりません**。
MATCH
演算子を使用して検索を実行する場合、FTS5はハイライトされた一致を含むスニペットを含むテキストを指定された列から返すことができます。# Search for items matching string 'python' and return the title # highlighted with square brackets. query = (SearchIndex .search('python') .select(SearchIndex.title.snippet('[', ']').alias('snip'))) for result in query: print(result.snip)
- class VirtualModel
仮想テーブルを表すために使用されるように設計されたモデルクラス。デフォルトのメタデータ設定は、仮想テーブルで頻繁に使用される設定に合わせてわずかに異なります。
メタデータオプション
arguments
- 仮想テーブルコンストラクタに渡される引数。extension_module
- 仮想テーブルに使用する拡張機能の名前。options
- 仮想テーブルに適用する設定の辞書。コンストラクタ。
primary_key
- デフォルトはFalse
で、主キーがないことを示します。
これらはすべて以下の方法で組み合わせられます。
CREATE VIRTUAL TABLE <table_name> USING <extension_module> ([prefix_arguments, ...] fields, ... [arguments, ...], [options...])
- class FTSModel
FTS3およびFTS4全文検索拡張機能で使用される
VirtualModel
のサブクラスです。FTSModelサブクラスは通常どおり定義する必要がありますが、いくつかの注意点があります。
一意制約、NULL制約、チェック制約、外部キーはサポートされていません。
フィールドのインデックスと複数列のインデックスは完全に無視されます。
Sqliteはすべての列の型を
TEXT
として扱います(他のデータ型を格納できますが、Sqliteはテキストとして扱います)。FTSモデルには、SQLiteによって自動的に作成および管理される
rowid
フィールドが含まれています(モデル作成時に明示的に設定しない限り)。この列に対するルックアップは**高速で効率的です**。
これらの制約を考慮すると、
FTSModel
サブクラスで宣言されたすべてのフィールドは、SearchField
のインスタンスであることが強く推奨されます(RowIDField
を明示的に宣言する場合を除きます)。SearchField
を使用すると、無効な列制約を誤って作成するのを防ぐのに役立ちます。メタデータをインデックスに格納したいが、全文検索インデックスに含めたくない場合は、SearchField
のインスタンス化時にunindexed=True
を指定します。上記の唯一の例外は、
RowIDField
を使用して宣言できるrowid
プライマリキーです。rowid
のルックアップは非常に効率的です。FTS4 を使用している場合は、DocIDField
も使用できます。これはrowid
のエイリアスですが、そうすることによるメリットはありません。セカンダリインデックスがないため、通常は
rowid
プライマリキーを通常のテーブルの行へのポインタとして使用するのが妥当です。例:class Document(Model): # Canonical source of data, stored in a regular table. author = ForeignKeyField(User, backref='documents') title = TextField(null=False, unique=True) content = TextField(null=False) timestamp = DateTimeField() class Meta: database = db class DocumentIndex(FTSModel): # Full-text search index. rowid = RowIDField() title = SearchField() content = SearchField() class Meta: database = db # Use the porter stemming algorithm to tokenize content. options = {'tokenize': 'porter'}
ドキュメントインデックスにドキュメントを格納するには、
DocumentIndex
テーブルに行をINSERT
し、対応するDocument
のプライマリキーと一致するようにrowid
を手動で設定します。def store_document(document): DocumentIndex.insert({ DocumentIndex.rowid: document.id, DocumentIndex.title: document.title, DocumentIndex.content: document.content}).execute()
検索を実行してランク付けされた結果を返すには、
Document
テーブルをクエリし、DocumentIndex
で結合できます。FTSModel のrowid
フィールドのルックアップは高速であるため、この結合は効率的です。def search(phrase): # Query the search index and join the corresponding Document # object on each search result. return (Document .select() .join( DocumentIndex, on=(Document.id == DocumentIndex.rowid)) .where(DocumentIndex.match(phrase)) .order_by(DocumentIndex.bm25()))
警告
FTSModel
クラスに対するすべての SQL クエリは、全文検索とrowid
ルックアップを除いて、フルテーブルスキャンになります。インデックスしているコンテンツの主要なソースが別のテーブルにある場合は、SQLite に検索インデックスコンテンツの追加コピーを格納しないように指示することで、ディスク容量を節約できます。SQLite は依然としてコンテンツを検索するために必要なメタデータとデータ構造を作成しますが、コンテンツ自体は検索インデックスには格納されません。
これを実現するには、
content
オプションを使用してテーブルまたは列を指定できます。詳細は、FTS4 ドキュメント を参照してください。peewee を使用してこれを実装する方法を示す短い例を以下に示します。
class Blog(Model): title = TextField() pub_date = DateTimeField(default=datetime.datetime.now) content = TextField() # We want to search this. class Meta: database = db class BlogIndex(FTSModel): content = SearchField() class Meta: database = db options = {'content': Blog.content} # <-- specify data source. db.create_tables([Blog, BlogIndex]) # Now, we can manage content in the BlogIndex. To populate the # search index: BlogIndex.rebuild() # Optimize the index. BlogIndex.optimize()
content
オプションは、単一のField
またはModel
を受け入れ、データベースファイルで使用されるストレージ容量を削減できます。ただし、コンテンツは関連付けられたFTSModel
に手動で移動する必要があります。- classmethod match(term)
- パラメータ
term – 検索用語または式。
テーブル内の指定された用語または式を検索する SQL 式を生成します。SQLite は、全文検索を示すために
MATCH
演算子を使用します。例
# Search index for "search phrase" and return results ranked # by relevancy using the BM25 algorithm. query = (DocumentIndex .select() .where(DocumentIndex.match('search phrase')) .order_by(DocumentIndex.bm25())) for result in query: print('Result: %s' % result.title)
- classmethod search(term[, weights=None[, with_score=False[, score_alias='score'[, explicit_ordering=False]]]])
- パラメータ
term (str) – 使用する検索用語。
weights – テーブル内の列の位置に関して順序付けられた、列の重みのリスト。**または**、フィールドまたはフィールド名をキーとし、値にマッピングされた辞書。
with_score – スコアを
SELECT
ステートメントの一部として返すかどうか。score_alias (str) – 計算されたランクスコアに使用するエイリアス。これは、
with_score=True
の場合にスコアにアクセスするために使用する属性です。explicit_ordering (bool) – スコアエイリアスを ORDER BY 句で参照するのではなく、ランクを計算するための完全な SQL 関数を使用して順序付けます。
用語を検索し、一致の精度で結果をソートするための簡単な方法です。
注記
このメソッドは、結果の関連性のランクを決定するための簡略化されたアルゴリズムを使用します。より高度な結果ランキングについては、
search_bm25()
メソッドを使用してください。# Simple search. docs = DocumentIndex.search('search term') for result in docs: print(result.title) # More complete example. docs = DocumentIndex.search( 'search term', weights={'title': 2.0, 'content': 1.0}, with_score=True, score_alias='search_score') for result in docs: print(result.title, result.search_score)
- classmethod search_bm25(term[, weights=None[, with_score=False[, score_alias='score'[, explicit_ordering=False]]]])
- パラメータ
term (str) – 使用する検索用語。
weights – テーブル内の列の位置に関して順序付けられた、列の重みのリスト。**または**、フィールドまたはフィールド名をキーとし、値にマッピングされた辞書。
with_score – スコアを
SELECT
ステートメントの一部として返すかどうか。score_alias (str) – 計算されたランクスコアに使用するエイリアス。これは、
with_score=True
の場合にスコアにアクセスするために使用する属性です。explicit_ordering (bool) – スコアエイリアスを ORDER BY 句で参照するのではなく、ランクを計算するための完全な SQL 関数を使用して順序付けます。
BM25 アルゴリズムを使用して、用語を検索し、一致の精度で結果をソートするための簡単な方法です。
注意
BM25 ランク付けアルゴリズムは、FTS4 でのみ使用できます。FTS3 を使用している場合は、代わりに
search()
メソッドを使用してください。
- classmethod search_bm25f(term[, weights=None[, with_score=False[, score_alias='score'[, explicit_ordering=False]]]])
FTSModel.search_bm25()
と同じですが、BM25 ランク付けアルゴリズムの BM25f バリアントを使用します。
- classmethod search_lucene(term[, weights=None[, with_score=False[, score_alias='score'[, explicit_ordering=False]]]])
FTSModel.search_bm25()
と同じですが、Lucene 検索エンジンの結果ランク付けアルゴリズムを使用します。
- classmethod rank([col1_weight, col2_weight...coln_weight])
- パラメータ
col_weight (float) – (オプション) モデルの *i* 番目の列に与える重み。デフォルトでは、すべての列の重みは
1.0
です。
検索一致の精度を計算して返す式を生成します。この
rank
を使用して検索結果をソートできます。ランクスコアが高いほど、一致が優れていることを示します。rank
関数は、さまざまな列の重みを指定できるオプションのパラメータを受け入れます。重みを指定しない場合、すべての列は同等の重要度と見なされます。query = (DocumentIndex .select( DocumentIndex, DocumentIndex.rank().alias('score')) .where(DocumentIndex.match('search phrase')) .order_by(DocumentIndex.rank())) for search_result in query: print(search_result.title, search_result.score)
- classmethod bm25([col1_weight, col2_weight...coln_weight])
- パラメータ
col_weight (float) – (オプション) モデルの *i* 番目の列に与える重み。デフォルトでは、すべての列の重みは
1.0
です。
BM25 アルゴリズム を使用して検索一致の精度を計算して返す式を生成します。この値を使用して検索結果をソートできます。スコアが高いほど、一致が優れていることを示します。
rank()
と同様に、bm25
関数は、さまざまな列の重みを指定できるオプションのパラメータを受け入れます。重みを指定しない場合、すべての列は同等の重要度と見なされます。注意
BM25 結果ランク付けアルゴリズムには FTS4 が必要です。FTS3 を使用している場合は、代わりに
rank()
を使用してください。query = (DocumentIndex .select( DocumentIndex, DocumentIndex.bm25().alias('score')) .where(DocumentIndex.match('search phrase')) .order_by(DocumentIndex.bm25())) for search_result in query: print(search_result.title, search_result.score)
注記
上記のコード例は、
search_bm25()
メソッドを呼び出すことと同等です。query = DocumentIndex.search_bm25('search phrase', with_score=True) for search_result in query: print(search_result.title, search_result.score)
- classmethod bm25f([col1_weight, col2_weight...coln_weight])
bm25()
と同一ですが、BM25ランキングアルゴリズムのBM25f変種を使用している点が異なります。
- classmethod lucene([col1_weight, col2_weight...coln_weight])
bm25()
と同一ですが、Lucene検索結果ランキングアルゴリズムを使用している点が異なります。
- classmethod rebuild()
検索インデックスを再構築します。これは、テーブル作成時に
content
オプションが指定されている場合のみ機能します。
- classmethod optimize()
検索インデックスを最適化します。
- class FTS5Model
VirtualModel
のサブクラスで、FTS5全文検索拡張機能で使用されます。FTS5Modelサブクラスは通常通り定義されますが、いくつかの注意点があります。
FTS5では、列に対する制約、データ型、インデックスの指定が明示的に禁止されています。そのため、すべての列は
SearchField
のインスタンスである**必要があります**。FTS5モデルには
rowid
フィールドが含まれており、これはSQLiteによって自動的に作成および管理されます(モデル作成時に明示的に設定しない限り)。この列に対する検索は**高速で効率的です**。フィールドのインデックスと複数列のインデックスはサポートされていません。
FTS5
拡張機能には、BM25ランキング関数の組み込み実装が含まれています。そのため、search
メソッドとsearch_bm25
メソッドは、ユーザー定義関数ではなく、組み込みランキング関数を使用するようにオーバーライドされています。- classmethod fts5_installed()
FTS5拡張機能がインストールされているかどうかを示すブール値を返します。インストールされていない場合は、拡張機能のロードが試みられます。
- classmethod search(term[, weights=None[, with_score=False[, score_alias='score']]])
- パラメータ
term (str) – 使用する検索用語。
weights – テーブル内の列の位置に関して順序付けられた、列の重みのリスト。**または**、フィールドまたはフィールド名をキーとし、値にマッピングされた辞書。
with_score – スコアを
SELECT
ステートメントの一部として返すかどうか。score_alias (str) – 計算されたランクスコアに使用するエイリアス。これは、
with_score=True
の場合にスコアにアクセスするために使用する属性です。explicit_ordering (bool) – スコアエイリアスを ORDER BY 句で参照するのではなく、ランクを計算するための完全な SQL 関数を使用して順序付けます。
用語を検索し、一致の精度で結果をソートするための簡単な方法です。
FTS5
拡張機能はBM25アルゴリズムの組み込み実装を提供しており、これを使用して関連性の高い順に結果をランク付けします。スコアが高いほど、一致度が高くなります。
# Simple search. docs = DocumentIndex.search('search term') for result in docs: print(result.title) # More complete example. docs = DocumentIndex.search( 'search term', weights={'title': 2.0, 'content': 1.0}, with_score=True, score_alias='search_score') for result in docs: print(result.title, result.search_score)
- classmethod search_bm25(term[, weights=None[, with_score=False[, score_alias='score']]])
FTS5では、
search_bm25()
はsearch()
メソッドと同一です。
- classmethod rank([col1_weight, col2_weight...coln_weight])
- パラメータ
col_weight (float) – (オプション) モデルの *i* 番目の列に与える重み。デフォルトでは、すべての列の重みは
1.0
です。
BM25 アルゴリズム を使用して検索一致の精度を計算して返す式を生成します。この値を使用して検索結果をソートできます。スコアが高いほど、一致が優れていることを示します。
rank()
関数は、さまざまな列の重みを指定できるオプションのパラメーターを受け入れます。重みが指定されていない場合、すべての列は同等の重要度と見なされます。query = (DocumentIndex .select( DocumentIndex, DocumentIndex.rank().alias('score')) .where(DocumentIndex.match('search phrase')) .order_by(DocumentIndex.rank())) for search_result in query: print(search_result.title, search_result.score)
注記
上記のコード例は、
search()
メソッドを呼び出すことと同じです。query = DocumentIndex.search('search phrase', with_score=True) for search_result in query: print(search_result.title, search_result.score)
- classmethod bm25([col1_weight, col2_weight...coln_weight])
- class TableFunction
ユーザー定義テーブル値関数を実装します。単一の値を返す単純なスカラー関数または集計関数とは異なり、テーブル値関数は、任意の数の行の表形式データを返すことができます。
簡単な例
from playhouse.sqlite_ext import TableFunction class Series(TableFunction): # Name of columns in each row of generated data. columns = ['value'] # Name of parameters the function may be called with. params = ['start', 'stop', 'step'] def initialize(self, start=0, stop=None, step=1): """ Table-functions declare an initialize() method, which is called with whatever arguments the user has called the function with. """ self.start = self.current = start self.stop = stop or float('Inf') self.step = step def iterate(self, idx): """ Iterate is called repeatedly by the SQLite database engine until the required number of rows has been read **or** the function raises a `StopIteration` signalling no more rows are available. """ if self.current > self.stop: raise StopIteration ret, self.current = self.current, self.current + self.step return (ret,) # Register the table-function with our database, which ensures it # is declared whenever a connection is opened. db.table_function('series')(Series) # Usage: cursor = db.execute_sql('SELECT * FROM series(?, ?, ?)', (0, 5, 2)) for value, in cursor: print(value)
注記
TableFunction
は、使用できるようになる前に、データベース接続に登録する必要があります。テーブル関数を常に利用できるようにするには、SqliteDatabase.table_function()
デコレータを使用して、関数をデータベースに登録することができます。TableFunction
の実装では、2つの属性を提供し、2つのメソッドを実装する必要があります(下記参照)。- columns
関数によって返されるデータの列の名前を含むリストです。たとえば、デリミタで文字列を分割するために使用される関数は、3つの列
[substring, start_idx, end_idx]
を指定する場合があります。
- params
関数が呼び出される可能性のあるパラメータの名前です。オプションのパラメータを含むすべてのパラメータをリストする必要があります。たとえば、デリミタで文字列を分割するために使用される関数は、2つのパラメータ
[string, delimiter]
を指定する場合があります。
- name
オプション - テーブル関数の名前を指定します。指定しない場合、名前はクラス名から取得されます。
- print_tracebacks = True
テーブル関数のコールバックメソッドで発生したエラーの完全なトレースバックを出力します。Falseに設定すると、一般的なOperationalErrorのみが表示されます。
- initialize(**parameter_values)
- パラメータ
parameter_values – 関数が呼び出されたときのパラメータ。
- 戻り値
戻り値はありません。
initialize
メソッドは、ユーザーが関数を呼び出す際に指定したパラメータを使用してテーブル関数を初期化するために呼び出されます。
- iterate(idx)
- パラメータ
idx (int) – 現在の反復ステップ。
- 戻り値
columns
属性で指定された列に対応する行データのタプル。- 例外が発生します。
StopIteration – 使用可能な行がなくなったことを示すために発生します。
この関数は繰り返し呼び出され、連続する行のデータを返します。この関数は、すべての行が消費される前に終了することがあります(特に、ユーザーが結果に
LIMIT
を指定した場合)。あるいは、StopIteration
例外を発生させることで、データがなくなったことを示すことができます。
- classmethod register(conn)
- パラメータ
conn –
sqlite3.Connection
オブジェクト。
DB-API 2.0
sqlite3.Connection
オブジェクトにテーブル関数を登録します。テーブル値関数は、クエリで使用できるようになる前に**登録する必要があります**。例
class MyTableFunction(TableFunction): name = 'my_func' # ... other attributes and methods ... db = SqliteDatabase(':memory:') db.connect() MyTableFunction.register(db.connection())
接続が確立されるたびに
TableFunction
が登録されるようにするには、table_function()
デコレータを使用します。
- ClosureTable(model_class[, foreign_key=None[, referencing_class=None[, referencing_key=None]]])
- パラメータ
model_class – ツリー内のノードを含むモデルクラス。
foreign_key – モデルクラスの自己参照的な親ノードフィールド。指定しない場合、peeweeは適切なキーを内省して探します。
referencing_class – 多対多関係の中間テーブル。
referencing_key – 多対多関係の場合、関係の発信側。
- 戻り値
クロージャテーブルを操作するための
VirtualModel
を返します。
推移閉包テーブルを操作するのに適したモデルクラスを作成するためのファクトリー関数です。クロージャテーブルは、推移閉包SQLite拡張機能を使用する
VirtualModel
のサブクラスです。これらの特別なテーブルは、階層データの効率的なクエリを容易にするように設計されています。SQLite拡張機能は、バックグラウンドでAVLツリーを管理し、テーブルの変更時にツリーを透過的に更新し、階層データに対する一般的なクエリの実行を容易にします。プロジェクトでクロージャテーブル拡張機能を使用するには、以下が必要です。
SQLite拡張機能のコピー。ソースコードはSQLiteコードリポジトリ、またはこのgistをクローンすることで見つけることができます。
$ git clone https://gist.github.com/coleifer/7f3593c5c2a645913b92 closure $ cd closure/
共有ライブラリとして拡張機能をコンパイルします(例:)。
$ gcc -g -fPIC -shared closure.c -o closure.so
階層データのモデルを作成します。ここでは、モデルに整数型の主キーと自己参照的な外部キーがあることが唯一の要件です。追加のフィールドは問題ありません。
class Category(Model): name = CharField() metadata = TextField() parent = ForeignKeyField('self', index=True, null=True) # Required. # Generate a model for the closure virtual table. CategoryClosure = ClosureTable(Category)
自己参照性は、中間テーブル(多対多関係の場合)によっても実現できます。
class User(Model): name = CharField() class UserRelations(Model): user = ForeignKeyField(User) knows = ForeignKeyField(User, backref='_known_by') class Meta: primary_key = CompositeKey('user', 'knows') # Alternatively, a unique index on both columns. # Generate a model for the closure virtual table, specifying the UserRelations as the referencing table UserClosure = ClosureTable( User, referencing_class=UserRelations, foreign_key=UserRelations.knows, referencing_key=UserRelations.user)
アプリケーションコードでは、
Database
オブジェクトをインスタンス化するときに、拡張機能をロードしてください。load_extension()
メソッドに共有ライブラリのパスを渡すことで行います。db = SqliteExtDatabase('my_database.db') db.load_extension('/path/to/closure')
警告
transitive_closure
拡張機能を使用する際の注意点が2つあります。まず、*ソースモデル*に整数型の主キーが必要です。次に、自己参照的な外部キーにインデックスを作成することを強くお勧めします。例
class Category(Model): name = CharField() metadata = TextField() parent = ForeignKeyField('self', index=True, null=True) # Required. # Generate a model for the closure virtual table. CategoryClosure = ClosureTable(Category) # Create the tables if they do not exist. db.create_tables([Category, CategoryClosure], True)
これで、クロージャテーブルのデータを使用して興味深いクエリを実行できるようになりました。
# Get all ancestors for a particular node. laptops = Category.get(Category.name == 'Laptops') for parent in Closure.ancestors(laptops): print(parent.name) # Computer Hardware # Computers # Electronics # All products # Get all descendants for a particular node. hardware = Category.get(Category.name == 'Computer Hardware') for node in Closure.descendants(hardware): print(node.name) # Laptops # Desktops # Hard-drives # Monitors # LCD Monitors # LED Monitors
VirtualModel
(ClosureTable()
によって返される)のAPI。- class BaseClosureTable
- id
指定されたノードの主キーのフィールド。
- depth
指定されたノードの相対的な深さを表すフィールド。
- root
相対的なルートノードを表すフィールド。
- descendants(node[, depth=None[, include_node=False]])
指定されたノードのすべての子孫を取得します。深さが指定されている場合、その深さ(指定されたノードを基準とした)にあるノードのみが返されます。
node = Category.get(Category.name == 'Electronics') # Direct child categories. children = CategoryClosure.descendants(node, depth=1) # Grand-child categories. children = CategoryClosure.descendants(node, depth=2) # Descendants at all depths. all_descendants = CategoryClosure.descendants(node)
- ancestors(node[, depth=None[, include_node=False]])
指定されたノードのすべての祖先を取得します。深さが指定されている場合、その深さ(指定されたノードを基準とした)にあるノードのみが返されます。
node = Category.get(Category.name == 'Laptops') # All ancestors. all_ancestors = CategoryClosure.ancestors(node) # Grand-parent category. grandparent = CategoryClosure.ancestores(node, depth=2)
- siblings(node[, include_node=False])
指定されたノードの親の子であるすべてのノードを取得します。
注記
SQLite推移閉包拡張機能の詳細については、このブログ記事を参照してください。Querying Tree Structures in SQLite using Python and the Transitive Closure Extension.
- class LSMTable
VirtualModel
サブクラス。lsm1拡張機能で使用できます。lsm1拡張機能は、SQLite4のlsmキー/値ストレージエンジンへのSQLインターフェースを提供する仮想テーブルです。注記
LSM1拡張機能はまだリリースされていません(執筆時点ではSQLiteバージョン3.22)ため、この機能は実験的なものとみなし、今後のリリースで変更される可能性があります。
LSMテーブルは、1つの主キー列と任意の数の追加の値列(ストレージエンジン内の単一の値フィールドにシリアライズされて格納される)を定義します。主キーはすべて同じ型でなければならず、次のフィールドタイプのいずれかを使用する必要があります。
LSMストレージエンジンはキー/値ストアであるため、主キー(整数を含む)はアプリケーションで指定する必要があります。
注意
LSMエンジンはセカンダリインデックスをサポートしていないため、効率的なクエリは主キーに関するルックアップ(または範囲クエリ)のみになります。他のフィールドはクエリおよびフィルタリングできますが、フルテーブルスキャンになる可能性があります。
モデル宣言の例
db = SqliteExtDatabase('my_app.db') db.load_extension('lsm.so') # Load shared library. class EventLog(LSMTable): timestamp = IntegerField(primary_key=True) action = TextField() sender = TextField() target = TextField() class Meta: database = db filename = 'eventlog.ldb' # LSM data is stored in separate db. # Declare virtual table. EventLog.create_table()
クエリの例
# Use dictionary operators to get, set and delete rows from the LSM # table. Slices may be passed to represent a range of key values. def get_timestamp(): # Return time as integer expressing time in microseconds. return int(time.time() * 1000000) # Create a new row, at current timestamp. ts = get_timestamp() EventLog[ts] = ('pageview', 'search', '/blog/some-post/') # Retrieve row from event log. log = EventLog[ts] print(log.action, log.sender, log.target) # Prints ("pageview", "search", "/blog/some-post/") # Delete the row. del EventLog[ts] # We can also use the "create()" method. EventLog.create( timestamp=get_timestamp(), action='signup', sender='newsletter', target='sqlite-news')
単純なキー/値モデルの宣言
class KV(LSMTable): key = TextField(primary_key=True) value = TextField() class Meta: database = db filename = 'kv.ldb' db.create_tables([KV])
単一の値フィールドで構成されるテーブルの場合、Peeweeは単一のアイテムを取得するときに値を直接返します。行のスライスを要求することもできます。その場合、Peeweeは対応する
Select
クエリを返し、それを反復処理できます。以下に例を示します。>>> KV['k0'] = 'v0' >>> print(KV['k0']) 'v0' >>> data = [{'key': 'k%d' % i, 'value': 'v%d' % i} for i in range(20)] >>> KV.insert_many(data).execute() >>> KV.select().count() 20 >>> KV['k8'] 'v8' >>> list(KV['k4.1':'k7.x'] [Row(key='k5', value='v5'), Row(key='k6', value='v6'), Row(key='k7', value='v7')] >>> list(KV['k6xxx':]) [Row(key='k7', value='v7'), Row(key='k8', value='v8'), Row(key='k9', value='v9')]
LSMTable
に式を使用してインデックスを作成することもできます。>>> list(KV[KV.key > 'k6']) [Row(key='k7', value='v7'), Row(key='k8', value='v8'), Row(key='k9', value='v9')] >>> list(KV[(KV.key > 'k6') & (KV.value != 'v8')]) [Row(key='k7', value='v7'), Row(key='k9', value='v9')]
del
を使用して単一行を削除するか、スライスまたは式を使用して複数行を削除できます。>>> del KV['k1'] >>> del KV['k3x':'k8'] >>> del KV[KV.key.between('k10', 'k18')] >>> list(KV[:]) [Row(key='k0', value='v0'), Row(key='k19', value='v19'), Row(key='k2', value='v2'), Row(key='k3', value='v3'), Row(key='k9', value='v9')]
存在しない単一のキーを取得しようとすると
DoesNotExist
が発生しますが、スライスでは例外が発生しません。>>> KV['k1'] ... KV.DoesNotExist: <Model:KV> instance matching query does not exist: ... >>> list(KV['k1':'k1']) []
- class ZeroBlob(length)
- パラメータ
length (int) – BLOBのサイズ(バイト単位)。
ZeroBlob
は、増分I/OをサポートするBLOBの領域を予約するためだけに使用されます。SQLite BLOBストアを使用するには、まず、増分I/Oで使用したい行に、目的のサイズのZeroBlobを挿入する必要があります。例については、
Blob
を参照してください。
- class Blob(database, table, column, rowid[, read_only=False])
- パラメータ
database –
SqliteExtDatabase
インスタンス。table (str) – アクセスされるテーブルの名前。
column (str) – アクセスされる列の名前。
rowid (int) – アクセスされる行の主キー。
read_only (bool) – BLOBデータの変更を防止します。
指定されたテーブル/列/行に格納されているBLOBを増分I/Oのために開きます。新しいデータのストレージを割り当てるには、非常に効率的な
ZeroBlob
を使用できます。class RawData(Model): data = BlobField() # Allocate 100MB of space for writing a large file incrementally: query = RawData.insert({'data': ZeroBlob(1024 * 1024 * 100)}) rowid = query.execute() # Now we can open the row for incremental I/O: blob = Blob(db, 'rawdata', 'data', rowid) # Read from the file and write to the blob in chunks of 4096 bytes. while True: data = file_handle.read(4096) if not data: break blob.write(data) bytes_written = blob.tell() blob.close()
- read([n=None])
- パラメータ
n (int) – ファイルの現在の位置から最大nバイト読み込みます。
Blobファイルの現在の位置から最大nバイト読み込みます。nが指定されていない場合、Blob全体が読み込まれます。
- seek(offset[, whence=0])
- パラメータ
offset (int) – ファイル内の指定されたオフセット位置にシークします。
whence (int) – 参照するフレームを指定して相対的にシークします。
whence
の値0
: ファイルの先頭1
: 現在の位置2
: ファイルの末尾
- tell()
ファイル内の現在のオフセットを返します。
- write(data)
- パラメータ
data (bytes) – 書き込むデータ
ファイルの現在の位置から、指定されたデータを書き込みます。
- close()
ファイルを閉じ、関連するリソースを解放します。
追加機能
SqliteExtDatabase
は、シンプルなブルームフィルタのサポートを登録するための初期化オプションを受け付けます。ブルームフィルタは、一度初期化されると、大量のデータセットに対する効率的なメンバシップクエリに使用できます。
例
db = CSqliteExtDatabase(':memory:', bloomfilter=True)
# Create and define a table to store some data.
db.execute_sql('CREATE TABLE "register" ("data" TEXT)')
Register = Table('register', ('data',)).bind(db)
# Populate the database with a bunch of text.
with db.atomic():
for i in 'abcdefghijklmnopqrstuvwxyz':
keys = [i * j for j in range(1, 10)] # a, aa, aaa, ... aaaaaaaaa
Register.insert([{'data': key} for key in keys]).execute()
# Collect data into a 16KB bloomfilter.
query = Register.select(fn.bloomfilter(Register.data, 16 * 1024).alias('buf'))
row = query.get()
buf = row['buf']
# Use bloomfilter buf to test whether other keys are members.
test_keys = (
('aaaa', True),
('abc', False),
('zzzzzzz', True),
('zyxwvut', False))
for key, is_present in test_keys:
query = Register.select(fn.bloomfilter_contains(key, buf).alias('is_member'))
answer = query.get()['is_member']
assert answer == is_present
SqliteExtDatabase
は、他の便利な関数も登録できます。
rank_functions
(デフォルトで有効): bm25やluceneなどの検索結果をランク付けするための関数を登録します。hash_functions
: md5、sha1、sha256、adler32、crc32、murmurhash関数を登録します。regexp_function
: 正規表現関数を登録します。
例
def create_new_user(username, password):
# DO NOT DO THIS IN REAL LIFE. PLEASE.
query = User.insert({'username': username, 'password': fn.sha1(password)})
new_user_id = query.execute()
murmurhash関数を用いて、バイト列をコンパクトに格納するための整数にハッシュすることができます。
>>> db = SqliteExtDatabase(':memory:', hash_functions=True)
>>> db.execute_sql('SELECT murmurhash(?)', ('abcdefg',)).fetchone()
(4188131059,)