(八)把筛选条件落实为同一组查询
1. 参数进入 SQL 的位置
# 续写同一文件:catalog_project/src/cataloghub/storage.py
def query(connection, q="", finished="all", tag="", page=1, size=3):
conditions, values = ["instr(lower(title),lower(?))>0"], [q]
if finished != "all":
conditions.append("finished=?")
values.append(int(finished))
if tag:
# json_each 将这本书的标签数组展开,value 是其中一项标签。
conditions.append(
"EXISTS(SELECT 1 FROM json_each(books.tags_json) WHERE value=?)"
)
values.append(tag)
where = " AND ".join(conditions)
count = connection.execute(
"SELECT count(*) FROM books WHERE " + where, values
).fetchone()[0]
rows = connection.execute(
"SELECT * FROM books WHERE " + where + " ORDER BY source_id LIMIT ? OFFSET ?",
[*values, size, (page - 1) * size],
).fetchall()
return {
"total": count,
"page": page,
"size": size,
"pages": max(1, (count + size - 1) // size),
"items": [public(row) for row in rows],
}
def public(row):
return {
"source_id": row["source_id"],
"title": row["title"],
"finished": bool(row["finished"]),
"tags": json.loads(row["tags_json"]),
"updated_run": row["updated_run"],
}SQL 结构来自程序,用户输入放进 ? 参数。书名使用 instr(lower(title), lower(?)) 判断是否包含关键词;输入 % 时就查找百分号本身。标签列是 JSON 文本,json_each 把当前书的数组逐项展开,EXISTS 判断是否有与参数完全相同的一项。
例如 b102 保存 ["数值"],展开后有一行 value="数值";b104 的数组展开为 "网络",不会匹配。json_each 的列和展开行为可查 SQLite JSON 函数文档。
2. 手算计数、页数与偏移量
六本书中有四本未读,按编号排成 b101、b103、b104、b106。每页三本,第二页先跳过三本:offset=(page-1)*size=3,因此只剩 b106。页数取向上整除:(total+size-1)//size=2。
计数和取本页记录使用相同的 where。Web 依赖会显式开始读取事务,让它们看到同一个数据库快照。采集进程在旁边提交新书时,这一次响应中的总数与记录仍来自同一份读取视图。