SQLite 擴充功能
預設的 SqliteDatabase
已經包含許多 SQLite 特有的功能
playhouse.sqlite_ext
包含更多 SQLite 功能,包括:
使用備份 API 支援線上備份:
backup_to_file()
其他輔助工具,包括布隆篩選器等等。
入門
若要開始使用本文件中描述的功能,您會需要使用 playhouse.sqlite_ext
模組中的 SqliteExtDatabase
類別。此外,某些功能需要 playhouse._sqlite_ext
C 擴充功能 – 這些功能將會在文件中註明。
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
傳回一個布林值屬性,表示是否啟用自動提交。預設情況下,除非在交易內(或
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
實例,可有效存取基礎二進位資料。- 返回類型
範例
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_dumps
和json_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 資料。
- 返回類型
存取 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() 函數文件。
- 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
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
選項接受單一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) – 使用完整的 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
函數接受可選參數,讓您可以指定各個欄位的權重。如果未指定權重,則所有欄位都被認為同等重要。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 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) – 使用完整的 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])
- 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 樹,當您的表格變更時會透明地更新樹狀結構,並讓您可以輕鬆地對階層式資料執行常見的查詢。若要在您的專案中使用封閉表擴充功能,您需要:
一份 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
擴充功能時,您應該注意兩個注意事項。首先,它要求您的來源模型具有整數主鍵。其次,強烈建議您在指向自身的外部鍵上建立索引。範例
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])
- 參數
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
:註冊 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,)