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)

註冊一個回呼函數,在寫入資料庫時執行(透過 UPDATEINSERTDELETE 查詢)。該回呼函數應接受下列參數:

  • query – 查詢的類型,可以是 INSERTUPDATEDELETE

  • 資料庫名稱 – 預設資料庫名稱為 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

傳回一個布林值屬性,表示是否啟用自動提交。預設情況下,除非在交易內(或 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 表示應在單一步驟中備份所有頁面。

  • name (str) – 來源資料庫的名稱(如果您使用 ATTACH DATABASE 載入多個資料庫,則可能不同)。預設為 “main”。

  • progress – 進度回呼函式,使用三個參數呼叫:剩餘頁數、總頁數以及備份是否完成。

範例

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 表示應在單一步驟中備份所有頁面。

  • name (str) – 來源資料庫的名稱(如果您使用 ATTACH DATABASE 載入多個資料庫,則可能不同)。預設為 “main”。

  • progress – 進度回呼函式,使用三個參數呼叫:剩餘頁數、總頁數以及備份是否完成。

將目前資料庫備份到檔案。備份的資料不是資料庫傾印,而是實際的 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 實例,可有效存取基礎二進位資料。

返回類型

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 欄位的主鍵欄位。如需更多資訊,請參閱關於 rowid 表格的 SQLite 文件。

範例

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。此功能會產生少量效能成本。如需更多資訊,請參閱關於 autoincrement 的 SQLite 文件。

class JSONField(json_dumps=None, json_loads=None, ...)

適用於儲存 JSON 資料的欄位類別,具有專門設計用於與 json1 擴充功能搭配使用的方法。

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 函式的參數,您可以使用 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 資料中的特定金鑰或陣列索引。返回一個 JSONPath 物件,該物件公開了用於讀取或修改 JSON 物件特定部分的便捷方法。

範例

# 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 – 要提取的一個或多個 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 演算法將新資料合併到 JSON 值中,以針對欄資料套用修補程式(data 參數)。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:資料類型之一(請參閱 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:資料類型之一(請參閱 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 路徑的元件。

一種方便、Python 式的方式來表示與 JSONField 一起使用的 JSON 路徑。

JSONPath 物件會實作 __getitem__,累積路徑元件,它可以將這些元件轉換為對應的 json-path 表達式。

__getitem__(item)
參數

item – 存取子鍵鍵或陣列索引。

返回

代表新路徑的 JSONPath

存取 JSON 資料中的子鍵或陣列索引。傳回 JSONPath 物件,該物件會公開方便的方法來讀取或修改 JSON 物件的特定部分。

範例

# 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 演算法將新資料合併到 JSON 值中,以將修補程式(data 參數)應用於欄位資料。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

VirtualModel 的子類別,用於 FTS3 和 FTS4 全文搜尋擴充功能。

FTSModel 子類別應正常定義,但有幾個注意事項

  • 不支援唯一約束、非空值約束、檢查約束和外部索引鍵。

  • 欄位上的索引和多欄索引會完全忽略

  • Sqlite 會將所有欄位類型視為 TEXT(雖然您可以儲存其他資料類型,但 Sqlite 會將它們視為文字)。

  • FTS 模型包含一個 rowid 欄位,該欄位由 SQLite 自動建立和管理(除非您選擇在模型建立期間明確設定)。在此欄位上的查閱快速且有效率

考量到這些限制,強烈建議在 FTSModel 子類別中宣告的所有欄位,都應為 SearchField 的實例 (但明確宣告 RowIDField 的情況例外)。使用 SearchField 將有助於避免您意外建立無效的欄位限制。如果您希望在索引中儲存元數據,但不希望將其包含在全文索引中,請在實例化 SearchField 時指定 unindexed=True

上述唯一的例外是 rowid 主鍵,可以使用 RowIDField 宣告。對 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'}

若要將文件儲存在文件索引中,我們將使用 INSERT 將列插入 DocumentIndex 表格,手動設定 rowid,使其與對應的 Document 的主鍵相符。

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 選項接受單一 FieldModel,並且可以減少資料庫檔案使用的儲存空間量。但是,內容將需要手動在相關的 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) – 使用完整的 SQL 函數計算排名,而不是僅在 ORDER BY 子句中引用分數別名。

搜尋詞彙並根據符合的品質對結果進行排序的簡便方法。

注意

此方法使用簡化的演算法來確定結果的相關性排名。若要進行更複雜的結果排名,請使用 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) – 使用完整的 SQL 函數計算排名,而不是僅在 ORDER BY 子句中引用分數別名。

搜尋詞彙並使用 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 排名函數的內建實作。因此,searchsearch_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) – 使用完整的 SQL 函數計算排名,而不是僅在 ORDER BY 子句中引用分數別名。

搜尋術語並按匹配品質對結果排序的簡便方法。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 搜尋索引對應的詞彙表格的模型類別。

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 實作必須提供兩個屬性並實作兩個方法,如下所述。

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,用於處理封閉表。

用於創建適用於處理傳遞封閉表的模型類別的工廠函式。封閉表是 VirtualModel 的子類別,可與傳遞封閉 SQLite 擴充功能一起使用。這些特殊表格旨在輕鬆高效地查詢階層式資料。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 擴充功能時,您應該注意兩個注意事項。首先,它要求您的來源模型具有整數主鍵。其次,強烈建議您在指向自身的外部鍵上建立索引。

範例

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

ClosureTable() 返回的 VirtualModel 的 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 傳遞封閉擴充功能,請查看此部落格文章,使用 Python 和傳遞封閉擴充功能在 SQLite 中查詢樹狀結構

class LSMTable

適用於處理 lsm1 擴充功能VirtualModel 子類別。lsm1 擴充功能是一個虛擬表格,它為 SQLite4 的 lsm 鍵/值儲存引擎 提供 SQL 介面。

注意

LSM1 擴充功能尚未發布(撰寫本文時為 SQLite 版本 3.22),因此請將此功能視為實驗性功能,在後續版本中可能會變更。

LSM 表格定義一個主鍵欄和任意數量的其他值欄(它們會被序列化並儲存在儲存引擎的單個值欄位中)。主鍵必須是相同的類型,並使用以下欄位類型之一:

由於 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 儲存,您必須先將所需大小的 ZeroBlob 插入您想要用於增量 I/O 的列中。

例如,請參閱 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:註冊 regexp 函式。

範例

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