sqlor/aidocs/filter.md
2025-10-05 11:24:24 +08:00

7.1 KiB
Raw Blame History

DBFilter 过滤器解释器技术文档

概述

DBFilter 是一个用于将 JSON 格式的 SQL 查询过滤条件解析为参数化 SQL 片段的 Python 类。它支持常见的逻辑运算符AND、OR、NOT和关系表达式=, <>, IN, LIKE 等),适用于动态构建安全的数据库查询语句。

该模块通过递归遍历 JSON 结构,生成可嵌入 SQL 语句中的字符串,并提取常量与变量绑定信息,便于后续参数绑定或模板渲染。


安装依赖

本模块无外部强依赖,但推荐使用 ujson 提升性能:

pip install ujson

日志功能依赖于 appPublic.log.debug,确保环境中已安装对应库或替换为标准日志模块。


支持的操作类型

1. 逻辑运算符Logic Operators

操作符 说明
AND 所有子条件都必须为真
OR 至少一个子条件为真
NOT 对单个条件取反

2. 关系运算符Relational Operators

运算符 含义
= 等于
!=<> 不等于
> 大于
< 小于
>= 大于等于
<= 小于等于
IN 在某个集合中
NOT IN 不在某个集合中
IS NULL 为空
IS NOT NULL 非空
LIKE 模糊匹配
NOT LIKE 非模糊匹配

注意:所有操作符不区分大小写处理,内部统一转为大写进行校验。


数据结构定义

过滤条件以嵌套 JSON 对象形式表示,基本结构如下:

{
  "AND": [
    { "field": "name", "op": "=", "var": "username" },
    { "field": "status", "op": "=", "const": "active" }
  ]
}

字段说明

字段名 类型 必需 描述
field string 数据库字段名
op string 操作符(见上表)
const any 常量值(直接序列化为占位符)
var string 变量名,需在命名空间 ns 中提供
  • constvar 至少存在其一(除非是 IS NULL / IS NOT NULL 类型)
  • 使用 ${name}$ 作为参数占位符格式,避免与主流 ORM 冲突

API 文档

类:DBFilter

初始化方法:__init__(self, filterjson)

参数:

  • filterjson (str or dict)JSON 字符串或字典格式的过滤条件

行为:

  • 若输入为字符串,则自动调用 json.loads() 解析
  • 存储解析后的结构供后续生成 SQL 使用

示例:

fj = {"AND": [...]}
dbf = DBFilter(fj)

方法:gen(ns={}) -> str or None

生成参数化的 SQL 过滤片段。

参数:

  • ns (dict):变量命名空间,提供 var 到实际值的映射

返回值:

  • 成功时返回 SQL 表达式字符串(可能包含括号)
  • 若无法生成有效 SQL如变量缺失返回 None
  • 所有常量会被分配唯一名称并存入 self.consts

副作用:

  • 更新 self.consts 字典,记录所有常量占位符及其值

示例:

sql = dbf.gen({'username': 'alice'})
# 输出: "name = ${filter_const_0}$ AND status = 'active'"

方法:get_variables() -> dict

获取过滤器中所有 var 映射到 field 的变量引用关系。

返回值:

  • dict: { variable_name: field_name }

可用于预检查所需变量是否齐全。

示例:

dbf.get_variables()
# 返回: {'username': 'name', 'status': 'status'}

私有方法:_genFilterSQL(fj, ns)

递归生成 SQL 表达式的核心逻辑。

处理规则:

  • 单键对象优先判断是否为逻辑操作符
  • AND / OR:连接多个子表达式,OR 自动加括号
  • NOT:对子表达式加 NOT (...)
  • 其他情况交由 _genFilterItems 处理

私有方法:_genFilterItems(fj, ns)

处理单个字段比较表达式。

逻辑:

  • 校验必填字段:field, op, (constvar)
  • 支持 IS NULL / IS NOT NULL 无需值
  • 若使用 var,检查其是否存在于 ns
  • 若使用 const,生成唯一占位符名(如 filter_const_0

函数:default_filterjson(fields: list, ns: dict) -> dict or None

根据字段白名单和变量空间生成默认等值过滤条件。

参数:

  • fields: 允许参与过滤的字段列表
  • ns: 当前可用变量字典

返回值:

  • 匹配字段的等值条件组成的 AND 结构
  • 若无匹配项返回 None
  • 单条件时不包装 AND

用途: 常用于自动生成基于请求参数的简单过滤器。

示例:

default_filterjson(['name', 'age'], {'name': 'Bob', 'city': 'Shanghai'})
# 返回:
# {
#   "AND": [
#     {"field": "name", "op": "=", "var": "name"}
#   ]
# }

使用示例

示例 1基础用法

fj = {
  "AND": [
    {
      "field": "field1",
      "op": "=",
      "var": "name"
    },
    {
      "field": "del_flg",
      "op": "=",
      "const": "0"
    }
  ]
}

dbf = DBFilter(fj)
sql = dbf.gen({"name": "joe"})
print(sql)
# 输出: field1 = ${name}$ AND del_flg = ${filter_const_0}$
print(dbf.consts)
# 输出: {'filter_const_0': '0'}

示例 2复杂嵌套逻辑

fj = {
  "AND": [
    {
      "OR": [
        {"field": "age", "op": ">=", "const": 18},
        {"field": "role", "op": "IN", "const": ["admin", "mod"]}
      ]
    },
    {
      "NOT": {
        "field": "status",
        "op": "=",
        "var": "blocked"
      }
    }
  ]
}

dbf = DBFilter(fj)
sql = dbf.gen({"blocked": "inactive"})
# 输出:
# ((age >= ${filter_const_0}$ OR role IN ${filter_const_1}$)) AND not (status = ${blocked}$)

示例 3NULL 判断

fj = {
  "field": "email",
  "op": "IS NOT NULL"
}

dbf = DBFilter(fj)
sql = dbf.gen({})
# 输出: email IS NOT NULL

错误处理

异常场景 抛出异常
AND/OR 的值不是数组或长度小于 2 Exception
NOT 的值不是对象 Exception
缺少必要字段(field, op AssertionError
使用非法操作符 AssertionError
varns 中不存在 返回 None(静默跳过)

占位符设计

采用 ${name}$ 形式作为参数占位符,优点包括:

  • 不与主流 SQL 参数风格冲突(如 %s, ?, :name
  • 易于被模板引擎识别和替换
  • 支持嵌入任意文本上下文

最终应配合参数绑定系统完成实际值注入。


设计特点

  • 安全性:所有常量均通过占位符传递,防止 SQL 注入
  • 灵活性:支持任意深度嵌套的布尔逻辑
  • 可扩展性:可通过继承修改 operators 或重写生成逻辑
  • 轻量级:无需依赖完整 ORM适合微服务或中间层使用

许可证

MIT License假设项目允许默认添加


提示:建议结合 Jinja2 或类似模板引擎使用生成的 SQL 片段,实现完整的动态查询构造。