SQLite 拡張機能

デフォルトのSqliteDatabase は、既に多くの SQLite 固有の機能を含んでいます。

playhouse.sqlite_ext には、さらに多くの SQLite 機能が含まれています。これらには以下が含まれます。

はじめに

このドキュメントで説明されている機能を使い始めるには、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インスタンス。

戻り値の型

Blob

詳細はBlobZeroBlobを参照してください。

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_dumpsjson_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 データへのアクセスを公開する特別なオブジェクト。

戻り値の型

JSONPath

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)
パラメータ

dataJSONFieldに現在格納されているデータとマージするスカラー値、リスト、または辞書。特定のキーを削除するには、更新されたデータでそのキーを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()関数のドキュメントを参照してください。

remove()

JSONデータの指定された位置に格納されているデータを削除します。

json1拡張機能のjson_type関数を使用します。

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 JSONBPath(field[, path=None])

jsonbデータの処理のためのJSONPathのサブクラスです。

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 関数は、さまざまな列の重みを指定できるオプションのパラメータを受け入れます。重みを指定しない場合、すべての列は同等の重要度と見なされます。

注記

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])

FTS5はBM25を組み込みでサポートしているため、bm25()メソッドはrank()メソッドと同一です。

classmethod VocabModel([table_type='row'|'col'|'instance'[, table_name=None]])
パラメータ
  • table_type (str) – 'row'、'col'、または'instance'のいずれか。

  • table_name – ボキャブラリーテーブルの名前。指定されていない場合、「fts5tablename_v」になります。

FTS5検索インデックスに対応するvocabテーブルにアクセスするのに適したモデルクラスを生成します。

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)
パラメータ

connsqlite3.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ツリーを管理し、テーブルの変更時にツリーを透過的に更新し、階層データに対する一般的なクエリの実行を容易にします。

プロジェクトでクロージャテーブル拡張機能を使用するには、以下が必要です。

  1. SQLite拡張機能のコピー。ソースコードはSQLiteコードリポジトリ、またはこのgistをクローンすることで見つけることができます。

    $ git clone https://gist.github.com/coleifer/7f3593c5c2a645913b92 closure
    $ cd closure/
    
  2. 共有ライブラリとして拡張機能をコンパイルします(例:)。

    $ gcc -g -fPIC -shared closure.c -o closure.so
    
  3. 階層データのモデルを作成します。ここでは、モデルに整数型の主キーと自己参照的な外部キーがあることが唯一の要件です。追加のフィールドは問題ありません。

    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)
    
  4. アプリケーションコードでは、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

VirtualModelClosureTable()によって返される)の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])
パラメータ
  • databaseSqliteExtDatabaseインスタンス。

  • 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()

ファイルを閉じ、関連するリソースを解放します。

reopen(rowid)
パラメータ

rowid (int) – 開く行の主キー。

特定のテーブル/カラムに対してBlobが既に開かれている場合、reopen()メソッドを使用して、同じBlobオブジェクトを再利用して、テーブル内の複数の行にアクセスできます。

追加機能

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 (デフォルトで有効): bm25luceneなどの検索結果をランク付けするための関数を登録します。

  • 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,)