12.2.14 class LSMTable
class LSMTable
VirtualModel适合使用lsm1 扩展 的子类lsm1扩展是一个虚拟表,它为来自 SQLite4 的 lsm 键/值存储引擎提供 SQL 接口。
笔记
LSM1 扩展尚未发布(撰写本文时 SQLite 版本为 3.22),因此请考虑此功能是实验性的,可能会在后续版本中进行更改。
LSM 表定义了一个主键列和任意数量的附加值列(它们被序列化并存储在存储引擎的单个值字段中)。主键必须是同一类型并使用以下字段类型之一:
- IntegerField
- TextField
- BlobField
由于 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')]
您还可以索引LSMTableusing 表达式:
>>> 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')]
尝试获取单个不存在的键将导致 a DoesNotExist,但切片不会引发异常:
>>> KV['k1']
...
KV.DoesNotExist: <Model:KV> instance matching query does not exist: ...
>>> list(KV['k1':'k1'])
[]
12.2.15 class ZeroBlob
class ZeroBlob(length)
参数: length( int ) – blob 的大小(以字节为单位)。
ZeroBlob仅用于为存储支持增量 I/O 的 BLOB 预留空间。要使用SQLite BLOB 存储 ,必须首先将所需大小的 ZeroBlob 插入您希望用于增量 I/O 的行中。
例如,请参阅Blob。
12.2.16 class 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(value)
参数: 数据( bytes ) – 要写入的数据
从文件中的当前位置开始写入给定的数据。
close()
关闭文件并释放相关资源。
reopen(rowid)
参数: rowid( int ) – 要打开的行的主键。
如果已经为给定的表/列打开了 blob,则可以使用该reopen()方法重新使用同一Blob 对象来访问表中的多行。
12.3 附加的功能
SqliteExtDatabase接受一个初始化选项来注册对简单bloomfilter的支持。bloomfilter一旦初始化,就可以用于对大量数据进行有效的成员资格查询。
这是一个例子:
db = CSqliteExtDatabase(':memory:', bloomfilter=True)
# Create and define a table to store some data.
db.execute_sql('CREATE TABLE "register" ("data" TEXT)')
Register = Table('register', ('data',)).bind(db)
# Populate the database with a bunch of text.
with db.atomic():
for i in 'abcdefghijklmnopqrstuvwxyz':
keys = [i * j for j in range(1, 10)] # a, aa, aaa, ... aaaaaaaaa
Register.insert([{'data': key} for key in keys]).execute()
# Collect data into a 16KB bloomfilter.
query = Register.select(fn.bloomfilter(Register.data, 16 * 1024).alias('buf'))
row = query.get()
buf = row['buf']
# Use bloomfilter buf to test whether other keys are members.
test_keys = (
('aaaa', True),
('abc', False),
('zzzzzzz', True),
('zyxwvut', False))
for key, is_present in test_keys:
query = Register.select(fn.bloomfilter_contains(key, buf).alias('is_member'))
answer = query.get()['is_member']
assert answer == is_present
SqliteExtDatabase还可以注册其他有用的功能:
- rank_functions(默认开启):注册对搜索结果进行排名的函数,例如bm25和lucene。
- hash_functions: 注册 md5、sha1、sha256、adler32、crc32 和 murmurhash 函数。
- regexp_function: 注册一个正则表达式函数。
例子:
def create_new_user(username, password):
# DO NOT DO THIS IN REAL LIFE. PLEASE.
query = User.insert({'username': username, 'password': fn.sha1(password)})
new_user_id = query.execute()
您可以使用murmurhash函数将字节散列为整数以进行紧凑存储:
>>> db = SqliteExtDatabase(':memory:', hash_functions=True)
>>> db.execute_sql('SELECT murmurhash(?)', ('abcdefg',)).fetchone()
(4188131059,)