统一查询语言设计规范 (Unified Query DSL Specification) #50

Description

@hotlong

这是一份基于行业最佳实践(参考 Prisma, Strapi, TypeORM, LoopBack)起草的通用查询语言(Generic Query DSL)设计规范文档。
你可以直接将这份文档作为你团队内部的技术规格说明书(Spec)使用。
统一查询语言设计规范 (Unified Query DSL Specification)
版本: 1.0.0
状态: 草案 (Draft)
目标: 定义一套基于 JSON 的、跨数据库兼容的查询语法标准,用于前端与后端 API 的数据筛选交互。

  1. 核心设计原则
  • 声明式 (Declarative): 前端只描述“要什么数据”,不描述“如何查询数据库”。
  • 后端无关性 (Database Agnostic): 语法不包含特定数据库(如 SQL 或 Mongo)的专用指令。所有指令在后端通过适配器层(Adapter Layer)转换。
  • 类型安全 (Type Safety): 结构必须能被 TypeScript 静态推断。
  • 默认约定优于配置 (Convention over Configuration): 提供隐式写法以简化常见查询。
  1. 语法结构定义
    查询对象(Filter Object)是一个递归的树状结构。
    2.1 基础结构
    一个合法的查询对象由以下三种元素组成:
  • 隐式相等 (Implicit Equality): key: value
  • 显式操作符 (Explicit Operators): key: { $op: value }
  • 逻辑组合 (Logical Groups): $and, $or, $not
    2.2 完整示例
    {
    "where": {
    "status": "active", // 隐式相等 (AND)
    "age": { "$gte": 18 }, // 显式比较 (AND)
    "$or": [ // 逻辑分支
    { "role": "admin" },
    { "email": { "$contains": "@company.com" } }
    ],
    "profile": { // 关联查询 (Relation)
    "verified": true
    }
    }
    }
  1. 操作符标准 (Operator Standards)
    为了保证跨库兼容,必须严格限制支持的操作符清单。不要直接透传数据库指令。
    3.1 比较操作符 (Comparison)
    | 操作符 | 描述 | SQL 映射示例 | MongoDB 映射示例 | 数据类型限制 |
    |---|---|---|---|---|
    | $eq | 等于 (默认) | = | $eq | Any |
    | $ne | 不等于 | <> 或 != | $ne | Any |
    | $gt | 大于 | > | $gt | Number, Date |
    | $gte | 大于等于 | >= | $gte | Number, Date |
    | $lt | 小于 | < | $lt | Number, Date |
    | $lte | 小于等于 | <= | $lte | Number, Date |
    3.2 集合与区间 (Set & Range)
    | 操作符 | 描述 | SQL 映射示例 | MongoDB 映射示例 |
    |---|---|---|---|
    | $in | 在列表中 | IN (?, ?, ?) | $in: [...] |
    | $nin | 不在列表中 | NOT IN (...) | $nin: [...] |
    | $between | 区间 (闭合) | BETWEEN ? AND ? | $gte AND $lte |
    3.3 字符串专用 (String Specific)
    注意:此处需在后端处理大小写敏感(Case Sensitivity)配置。
    | 操作符 | 描述 | SQL 映射示例 | MongoDB 映射示例 |
    |---|---|---|---|
    | $contains | 包含 | LIKE %?% | $regex |
    | $startsWith | 前缀匹配 | LIKE ?% | $regex |
    | $endsWith | 后缀匹配 | LIKE %? | $regex |
    3.4 逻辑操作符 (Logical)
    | 操作符 | 描述 | SQL 映射 | MongoDB 映射 |
    |---|---|---|---|
    | $and | 逻辑与 | (A AND B) | $and |
    | $or | 逻辑或 | (A OR B) | $or |
    | $not | 逻辑非 | NOT (A) | $not |
    3.5 特殊检查 (Special)
    | 操作符 | 描述 | SQL 映射 | MongoDB 映射 |
    |---|---|---|---|
    | $null | 是否为空 | IS NULL (true) / IS NOT NULL (false) | field: null |
    | $exist | 字段是否存在 | (通常用于 NoSQL) | $exists |
  2. 解析与执行流程 (Architecture)
    这是实现跨数据库的核心架构逻辑。
    阶段一:标准化 (Normalization Pass)
    在进入适配器之前,必须将所有“语法糖”转换为“标准 AST 结构”。这能极大简化后续适配器的编写难度。
    规则:
  • 将所有 key: value 转换为 key: { $eq: value }。
  • 将同级的所有 Key 合并入 $and 数组。
    输入:
    { "age": 18, "role": "admin" }

标准化后输出 (Internal IR):
{
"$and": [
{ "age": { "$eq": 18 } },
{ "role": { "$eq": "admin" } }
]
}

阶段二:适配器转换 (Adapter Translation)
使用 Visitor Pattern 遍历标准化后的对象。
SQL Adapter 伪逻辑:

  • 遍历对象。
  • 遇到 $and/$or: 递归生成子 SQL,用 AND/OR 连接,并包裹括号。
  • 遇到字段 { age: { $gt: 18 } }:
    • 提取字段名 age (需做安全校验,防 SQL 注入)。
    • 提取操作符 $gt -> 映射为 >。
    • 提取值 18 -> 存入 Parameters 数组,SQL 中替换为占位符 ? 或 $1。
  1. 关联查询规范 (Relation / Join)
    这是 ObjectQL 的核心优势,利用 JSON 的嵌套特性表达 SQL JOIN。
    规则:
    如果一个 Key 对应的值是对象(且不是操作符对象),则视为关联查询。
    输入:
    {
    "department": {
    "name": { "$eq": "IT" }
    }
    }

SQL Adapter 行为:

  • 检测到 department 是关联字段。
  • 自动执行 INNER JOIN department ON users.dept_id = department.id。
  • 添加 WHERE 条件:department.name = 'IT'。
  1. TypeScript 定义 (Interfaces)
    直接提供给前端使用的类型定义。
    // 基础标量类型
    type Scalar = string | number | boolean | Date | null;

// 操作符定义
type FilterOperators = {
$eq?: T;
$ne?: T;
$in?: T[];
$nin?: T[];
$gt?: T; // 仅限 number/date
$lt?: T;
$contains?: string; // 仅限 string
$startsWith?: string;
$not?: FilterOperators;
};

// 递归过滤器定义
export type Filter = {
[K in keyof T]?:
| T[K] // 隐式相等
| FilterOperators<T[K]> // 显式操作符
| Filter<T[K]>; // 关联表嵌套 (Join)
} & {
$and?: Filter[];
$or?: Filter[];
};

  1. 安全性与边界限制 (Security Guardrails)
    在实现解析器时,必须强制执行以下规则以防止恶意攻击:
  • 最大深度限制 (Max Depth): 防止深度嵌套导致的堆栈溢出(建议限制为 5-6 层)。
  • 字段白名单 (Field Whitelist): 解析器必须检查 Key 是否存在于定义的 Schema 中,禁止查询数据库中存在但未开放 API 的字段(如 password_hash, salt)。
  • 数组长度限制: $in 操作符的数组长度不得超过 1000(防止 SQL 性能问题)。
  • 禁止全表扫描: 强制要求查询必须包含索引字段(可选的高级配置)。
    下一步行动建议
  • 定义 Schema: 确定你的实体模型(Model)。
  • 编写 Normalizer: 实现阶段一的“标准化函数”,这一步是通用的。
  • 选择 ORM/QueryBuilder: 你的后端是用 TypeORM, Prisma, Knex 还是原生 SQL?这决定了 Adapter 的写法。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

    , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
     blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
    }
    } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
    })();
    (function(){
    try {
    var __m = "github.com";
    var __re = new RegExp('^' + "github\\.com" + '
    
    Skip to content

    统一查询语言设计规范 (Unified Query DSL Specification) #50

    Description

    @hotlong

    这是一份基于行业最佳实践(参考 Prisma, Strapi, TypeORM, LoopBack)起草的通用查询语言(Generic Query DSL)设计规范文档。
    你可以直接将这份文档作为你团队内部的技术规格说明书(Spec)使用。
    统一查询语言设计规范 (Unified Query DSL Specification)
    版本: 1.0.0
    状态: 草案 (Draft)
    目标: 定义一套基于 JSON 的、跨数据库兼容的查询语法标准,用于前端与后端 API 的数据筛选交互。

    1. 核心设计原则
    • 声明式 (Declarative): 前端只描述“要什么数据”,不描述“如何查询数据库”。
    • 后端无关性 (Database Agnostic): 语法不包含特定数据库(如 SQL 或 Mongo)的专用指令。所有指令在后端通过适配器层(Adapter Layer)转换。
    • 类型安全 (Type Safety): 结构必须能被 TypeScript 静态推断。
    • 默认约定优于配置 (Convention over Configuration): 提供隐式写法以简化常见查询。
    1. 语法结构定义
      查询对象(Filter Object)是一个递归的树状结构。
      2.1 基础结构
      一个合法的查询对象由以下三种元素组成:
    • 隐式相等 (Implicit Equality): key: value
    • 显式操作符 (Explicit Operators): key: { $op: value }
    • 逻辑组合 (Logical Groups): $and, $or, $not
      2.2 完整示例
      {
      "where": {
      "status": "active", // 隐式相等 (AND)
      "age": { "$gte": 18 }, // 显式比较 (AND)
      "$or": [ // 逻辑分支
      { "role": "admin" },
      { "email": { "$contains": "@company.com" } }
      ],
      "profile": { // 关联查询 (Relation)
      "verified": true
      }
      }
      }
    1. 操作符标准 (Operator Standards)
      为了保证跨库兼容,必须严格限制支持的操作符清单。不要直接透传数据库指令。
      3.1 比较操作符 (Comparison)
      | 操作符 | 描述 | SQL 映射示例 | MongoDB 映射示例 | 数据类型限制 |
      |---|---|---|---|---|
      | $eq | 等于 (默认) | = | $eq | Any |
      | $ne | 不等于 | <> 或 != | $ne | Any |
      | $gt | 大于 | > | $gt | Number, Date |
      | $gte | 大于等于 | >= | $gte | Number, Date |
      | $lt | 小于 | < | $lt | Number, Date |
      | $lte | 小于等于 | <= | $lte | Number, Date |
      3.2 集合与区间 (Set & Range)
      | 操作符 | 描述 | SQL 映射示例 | MongoDB 映射示例 |
      |---|---|---|---|
      | $in | 在列表中 | IN (?, ?, ?) | $in: [...] |
      | $nin | 不在列表中 | NOT IN (...) | $nin: [...] |
      | $between | 区间 (闭合) | BETWEEN ? AND ? | $gte AND $lte |
      3.3 字符串专用 (String Specific)
      注意:此处需在后端处理大小写敏感(Case Sensitivity)配置。
      | 操作符 | 描述 | SQL 映射示例 | MongoDB 映射示例 |
      |---|---|---|---|
      | $contains | 包含 | LIKE %?% | $regex |
      | $startsWith | 前缀匹配 | LIKE ?% | $regex |
      | $endsWith | 后缀匹配 | LIKE %? | $regex |
      3.4 逻辑操作符 (Logical)
      | 操作符 | 描述 | SQL 映射 | MongoDB 映射 |
      |---|---|---|---|
      | $and | 逻辑与 | (A AND B) | $and |
      | $or | 逻辑或 | (A OR B) | $or |
      | $not | 逻辑非 | NOT (A) | $not |
      3.5 特殊检查 (Special)
      | 操作符 | 描述 | SQL 映射 | MongoDB 映射 |
      |---|---|---|---|
      | $null | 是否为空 | IS NULL (true) / IS NOT NULL (false) | field: null |
      | $exist | 字段是否存在 | (通常用于 NoSQL) | $exists |
    2. 解析与执行流程 (Architecture)
      这是实现跨数据库的核心架构逻辑。
      阶段一:标准化 (Normalization Pass)
      在进入适配器之前,必须将所有“语法糖”转换为“标准 AST 结构”。这能极大简化后续适配器的编写难度。
      规则:
    • 将所有 key: value 转换为 key: { $eq: value }。
    • 将同级的所有 Key 合并入 $and 数组。
      输入:
      { "age": 18, "role": "admin" }

    标准化后输出 (Internal IR):
    {
    "$and": [
    { "age": { "$eq": 18 } },
    { "role": { "$eq": "admin" } }
    ]
    }

    阶段二:适配器转换 (Adapter Translation)
    使用 Visitor Pattern 遍历标准化后的对象。
    SQL Adapter 伪逻辑:

    • 遍历对象。
    • 遇到 $and/$or: 递归生成子 SQL,用 AND/OR 连接,并包裹括号。
    • 遇到字段 { age: { $gt: 18 } }:
      • 提取字段名 age (需做安全校验,防 SQL 注入)。
      • 提取操作符 $gt -> 映射为 >。
      • 提取值 18 -> 存入 Parameters 数组,SQL 中替换为占位符 ? 或 $1。
    1. 关联查询规范 (Relation / Join)
      这是 ObjectQL 的核心优势,利用 JSON 的嵌套特性表达 SQL JOIN。
      规则:
      如果一个 Key 对应的值是对象(且不是操作符对象),则视为关联查询。
      输入:
      {
      "department": {
      "name": { "$eq": "IT" }
      }
      }

    SQL Adapter 行为:

    • 检测到 department 是关联字段。
    • 自动执行 INNER JOIN department ON users.dept_id = department.id。
    • 添加 WHERE 条件:department.name = 'IT'。
    1. TypeScript 定义 (Interfaces)
      直接提供给前端使用的类型定义。
      // 基础标量类型
      type Scalar = string | number | boolean | Date | null;

    // 操作符定义
    type FilterOperators = {
    $eq?: T;
    $ne?: T;
    $in?: T[];
    $nin?: T[];
    $gt?: T; // 仅限 number/date
    $lt?: T;
    $contains?: string; // 仅限 string
    $startsWith?: string;
    $not?: FilterOperators;
    };

    // 递归过滤器定义
    export type Filter = {
    [K in keyof T]?:
    | T[K] // 隐式相等
    | FilterOperators<T[K]> // 显式操作符
    | Filter<T[K]>; // 关联表嵌套 (Join)
    } & {
    $and?: Filter[];
    $or?: Filter[];
    };

    1. 安全性与边界限制 (Security Guardrails)
      在实现解析器时,必须强制执行以下规则以防止恶意攻击:
    • 最大深度限制 (Max Depth): 防止深度嵌套导致的堆栈溢出(建议限制为 5-6 层)。
    • 字段白名单 (Field Whitelist): 解析器必须检查 Key 是否存在于定义的 Schema 中,禁止查询数据库中存在但未开放 API 的字段(如 password_hash, salt)。
    • 数组长度限制: $in 操作符的数组长度不得超过 1000(防止 SQL 性能问题)。
    • 禁止全表扫描: 强制要求查询必须包含索引字段(可选的高级配置)。
      下一步行动建议
    • 定义 Schema: 确定你的实体模型(Model)。
    • 编写 Normalizer: 实现阶段一的“标准化函数”,这一步是通用的。
    • 选择 ORM/QueryBuilder: 你的后端是用 TypeORM, Prisma, Knex 还是原生 SQL?这决定了 Adapter 的写法。

    Activity

    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

    Metadata

    Metadata

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
      Skip to content

      统一查询语言设计规范 (Unified Query DSL Specification) #50

      Description

      @hotlong

      这是一份基于行业最佳实践(参考 Prisma, Strapi, TypeORM, LoopBack)起草的通用查询语言(Generic Query DSL)设计规范文档。
      你可以直接将这份文档作为你团队内部的技术规格说明书(Spec)使用。
      统一查询语言设计规范 (Unified Query DSL Specification)
      版本: 1.0.0
      状态: 草案 (Draft)
      目标: 定义一套基于 JSON 的、跨数据库兼容的查询语法标准,用于前端与后端 API 的数据筛选交互。

      1. 核心设计原则
      • 声明式 (Declarative): 前端只描述“要什么数据”,不描述“如何查询数据库”。
      • 后端无关性 (Database Agnostic): 语法不包含特定数据库(如 SQL 或 Mongo)的专用指令。所有指令在后端通过适配器层(Adapter Layer)转换。
      • 类型安全 (Type Safety): 结构必须能被 TypeScript 静态推断。
      • 默认约定优于配置 (Convention over Configuration): 提供隐式写法以简化常见查询。
      1. 语法结构定义
        查询对象(Filter Object)是一个递归的树状结构。
        2.1 基础结构
        一个合法的查询对象由以下三种元素组成:
      • 隐式相等 (Implicit Equality): key: value
      • 显式操作符 (Explicit Operators): key: { $op: value }
      • 逻辑组合 (Logical Groups): $and, $or, $not
        2.2 完整示例
        {
        "where": {
        "status": "active", // 隐式相等 (AND)
        "age": { "$gte": 18 }, // 显式比较 (AND)
        "$or": [ // 逻辑分支
        { "role": "admin" },
        { "email": { "$contains": "@company.com" } }
        ],
        "profile": { // 关联查询 (Relation)
        "verified": true
        }
        }
        }
      1. 操作符标准 (Operator Standards)
        为了保证跨库兼容,必须严格限制支持的操作符清单。不要直接透传数据库指令。
        3.1 比较操作符 (Comparison)
        | 操作符 | 描述 | SQL 映射示例 | MongoDB 映射示例 | 数据类型限制 |
        |---|---|---|---|---|
        | $eq | 等于 (默认) | = | $eq | Any |
        | $ne | 不等于 | <> 或 != | $ne | Any |
        | $gt | 大于 | > | $gt | Number, Date |
        | $gte | 大于等于 | >= | $gte | Number, Date |
        | $lt | 小于 | < | $lt | Number, Date |
        | $lte | 小于等于 | <= | $lte | Number, Date |
        3.2 集合与区间 (Set & Range)
        | 操作符 | 描述 | SQL 映射示例 | MongoDB 映射示例 |
        |---|---|---|---|
        | $in | 在列表中 | IN (?, ?, ?) | $in: [...] |
        | $nin | 不在列表中 | NOT IN (...) | $nin: [...] |
        | $between | 区间 (闭合) | BETWEEN ? AND ? | $gte AND $lte |
        3.3 字符串专用 (String Specific)
        注意:此处需在后端处理大小写敏感(Case Sensitivity)配置。
        | 操作符 | 描述 | SQL 映射示例 | MongoDB 映射示例 |
        |---|---|---|---|
        | $contains | 包含 | LIKE %?% | $regex |
        | $startsWith | 前缀匹配 | LIKE ?% | $regex |
        | $endsWith | 后缀匹配 | LIKE %? | $regex |
        3.4 逻辑操作符 (Logical)
        | 操作符 | 描述 | SQL 映射 | MongoDB 映射 |
        |---|---|---|---|
        | $and | 逻辑与 | (A AND B) | $and |
        | $or | 逻辑或 | (A OR B) | $or |
        | $not | 逻辑非 | NOT (A) | $not |
        3.5 特殊检查 (Special)
        | 操作符 | 描述 | SQL 映射 | MongoDB 映射 |
        |---|---|---|---|
        | $null | 是否为空 | IS NULL (true) / IS NOT NULL (false) | field: null |
        | $exist | 字段是否存在 | (通常用于 NoSQL) | $exists |
      2. 解析与执行流程 (Architecture)
        这是实现跨数据库的核心架构逻辑。
        阶段一:标准化 (Normalization Pass)
        在进入适配器之前,必须将所有“语法糖”转换为“标准 AST 结构”。这能极大简化后续适配器的编写难度。
        规则:
      • 将所有 key: value 转换为 key: { $eq: value }。
      • 将同级的所有 Key 合并入 $and 数组。
        输入:
        { "age": 18, "role": "admin" }

      标准化后输出 (Internal IR):
      {
      "$and": [
      { "age": { "$eq": 18 } },
      { "role": { "$eq": "admin" } }
      ]
      }

      阶段二:适配器转换 (Adapter Translation)
      使用 Visitor Pattern 遍历标准化后的对象。
      SQL Adapter 伪逻辑:

      • 遍历对象。
      • 遇到 $and/$or: 递归生成子 SQL,用 AND/OR 连接,并包裹括号。
      • 遇到字段 { age: { $gt: 18 } }:
        • 提取字段名 age (需做安全校验,防 SQL 注入)。
        • 提取操作符 $gt -> 映射为 >。
        • 提取值 18 -> 存入 Parameters 数组,SQL 中替换为占位符 ? 或 $1。
      1. 关联查询规范 (Relation / Join)
        这是 ObjectQL 的核心优势,利用 JSON 的嵌套特性表达 SQL JOIN。
        规则:
        如果一个 Key 对应的值是对象(且不是操作符对象),则视为关联查询。
        输入:
        {
        "department": {
        "name": { "$eq": "IT" }
        }
        }

      SQL Adapter 行为:

      • 检测到 department 是关联字段。
      • 自动执行 INNER JOIN department ON users.dept_id = department.id。
      • 添加 WHERE 条件:department.name = 'IT'。
      1. TypeScript 定义 (Interfaces)
        直接提供给前端使用的类型定义。
        // 基础标量类型
        type Scalar = string | number | boolean | Date | null;

      // 操作符定义
      type FilterOperators = {
      $eq?: T;
      $ne?: T;
      $in?: T[];
      $nin?: T[];
      $gt?: T; // 仅限 number/date
      $lt?: T;
      $contains?: string; // 仅限 string
      $startsWith?: string;
      $not?: FilterOperators;
      };

      // 递归过滤器定义
      export type Filter = {
      [K in keyof T]?:
      | T[K] // 隐式相等
      | FilterOperators<T[K]> // 显式操作符
      | Filter<T[K]>; // 关联表嵌套 (Join)
      } & {
      $and?: Filter[];
      $or?: Filter[];
      };

      1. 安全性与边界限制 (Security Guardrails)
        在实现解析器时,必须强制执行以下规则以防止恶意攻击:
      • 最大深度限制 (Max Depth): 防止深度嵌套导致的堆栈溢出(建议限制为 5-6 层)。
      • 字段白名单 (Field Whitelist): 解析器必须检查 Key 是否存在于定义的 Schema 中,禁止查询数据库中存在但未开放 API 的字段(如 password_hash, salt)。
      • 数组长度限制: $in 操作符的数组长度不得超过 1000(防止 SQL 性能问题)。
      • 禁止全表扫描: 强制要求查询必须包含索引字段(可选的高级配置)。
        下一步行动建议
      • 定义 Schema: 确定你的实体模型(Model)。
      • 编写 Normalizer: 实现阶段一的“标准化函数”,这一步是通用的。
      • 选择 ORM/QueryBuilder: 你的后端是用 TypeORM, Prisma, Knex 还是原生 SQL?这决定了 Adapter 的写法。

      Activity

      Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

      Metadata

      Metadata

      Labels

      No labels
      No labels

      Type

      No type

      Projects

      No projects

        Milestone

        No milestone

        Relationships

        None yet

        Development

        No branches or pull requests

        Issue actions

        , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
        Skip to content

        统一查询语言设计规范 (Unified Query DSL Specification) #50

        Description

        @hotlong

        这是一份基于行业最佳实践(参考 Prisma, Strapi, TypeORM, LoopBack)起草的通用查询语言(Generic Query DSL)设计规范文档。
        你可以直接将这份文档作为你团队内部的技术规格说明书(Spec)使用。
        统一查询语言设计规范 (Unified Query DSL Specification)
        版本: 1.0.0
        状态: 草案 (Draft)
        目标: 定义一套基于 JSON 的、跨数据库兼容的查询语法标准,用于前端与后端 API 的数据筛选交互。

        1. 核心设计原则
        • 声明式 (Declarative): 前端只描述“要什么数据”,不描述“如何查询数据库”。
        • 后端无关性 (Database Agnostic): 语法不包含特定数据库(如 SQL 或 Mongo)的专用指令。所有指令在后端通过适配器层(Adapter Layer)转换。
        • 类型安全 (Type Safety): 结构必须能被 TypeScript 静态推断。
        • 默认约定优于配置 (Convention over Configuration): 提供隐式写法以简化常见查询。
        1. 语法结构定义
          查询对象(Filter Object)是一个递归的树状结构。
          2.1 基础结构
          一个合法的查询对象由以下三种元素组成:
        • 隐式相等 (Implicit Equality): key: value
        • 显式操作符 (Explicit Operators): key: { $op: value }
        • 逻辑组合 (Logical Groups): $and, $or, $not
          2.2 完整示例
          {
          "where": {
          "status": "active", // 隐式相等 (AND)
          "age": { "$gte": 18 }, // 显式比较 (AND)
          "$or": [ // 逻辑分支
          { "role": "admin" },
          { "email": { "$contains": "@company.com" } }
          ],
          "profile": { // 关联查询 (Relation)
          "verified": true
          }
          }
          }
        1. 操作符标准 (Operator Standards)
          为了保证跨库兼容,必须严格限制支持的操作符清单。不要直接透传数据库指令。
          3.1 比较操作符 (Comparison)
          | 操作符 | 描述 | SQL 映射示例 | MongoDB 映射示例 | 数据类型限制 |
          |---|---|---|---|---|
          | $eq | 等于 (默认) | = | $eq | Any |
          | $ne | 不等于 | <> 或 != | $ne | Any |
          | $gt | 大于 | > | $gt | Number, Date |
          | $gte | 大于等于 | >= | $gte | Number, Date |
          | $lt | 小于 | < | $lt | Number, Date |
          | $lte | 小于等于 | <= | $lte | Number, Date |
          3.2 集合与区间 (Set & Range)
          | 操作符 | 描述 | SQL 映射示例 | MongoDB 映射示例 |
          |---|---|---|---|
          | $in | 在列表中 | IN (?, ?, ?) | $in: [...] |
          | $nin | 不在列表中 | NOT IN (...) | $nin: [...] |
          | $between | 区间 (闭合) | BETWEEN ? AND ? | $gte AND $lte |
          3.3 字符串专用 (String Specific)
          注意:此处需在后端处理大小写敏感(Case Sensitivity)配置。
          | 操作符 | 描述 | SQL 映射示例 | MongoDB 映射示例 |
          |---|---|---|---|
          | $contains | 包含 | LIKE %?% | $regex |
          | $startsWith | 前缀匹配 | LIKE ?% | $regex |
          | $endsWith | 后缀匹配 | LIKE %? | $regex |
          3.4 逻辑操作符 (Logical)
          | 操作符 | 描述 | SQL 映射 | MongoDB 映射 |
          |---|---|---|---|
          | $and | 逻辑与 | (A AND B) | $and |
          | $or | 逻辑或 | (A OR B) | $or |
          | $not | 逻辑非 | NOT (A) | $not |
          3.5 特殊检查 (Special)
          | 操作符 | 描述 | SQL 映射 | MongoDB 映射 |
          |---|---|---|---|
          | $null | 是否为空 | IS NULL (true) / IS NOT NULL (false) | field: null |
          | $exist | 字段是否存在 | (通常用于 NoSQL) | $exists |
        2. 解析与执行流程 (Architecture)
          这是实现跨数据库的核心架构逻辑。
          阶段一:标准化 (Normalization Pass)
          在进入适配器之前,必须将所有“语法糖”转换为“标准 AST 结构”。这能极大简化后续适配器的编写难度。
          规则:
        • 将所有 key: value 转换为 key: { $eq: value }。
        • 将同级的所有 Key 合并入 $and 数组。
          输入:
          { "age": 18, "role": "admin" }

        标准化后输出 (Internal IR):
        {
        "$and": [
        { "age": { "$eq": 18 } },
        { "role": { "$eq": "admin" } }
        ]
        }

        阶段二:适配器转换 (Adapter Translation)
        使用 Visitor Pattern 遍历标准化后的对象。
        SQL Adapter 伪逻辑:

        • 遍历对象。
        • 遇到 $and/$or: 递归生成子 SQL,用 AND/OR 连接,并包裹括号。
        • 遇到字段 { age: { $gt: 18 } }:
          • 提取字段名 age (需做安全校验,防 SQL 注入)。
          • 提取操作符 $gt -> 映射为 >。
          • 提取值 18 -> 存入 Parameters 数组,SQL 中替换为占位符 ? 或 $1。
        1. 关联查询规范 (Relation / Join)
          这是 ObjectQL 的核心优势,利用 JSON 的嵌套特性表达 SQL JOIN。
          规则:
          如果一个 Key 对应的值是对象(且不是操作符对象),则视为关联查询。
          输入:
          {
          "department": {
          "name": { "$eq": "IT" }
          }
          }

        SQL Adapter 行为:

        • 检测到 department 是关联字段。
        • 自动执行 INNER JOIN department ON users.dept_id = department.id。
        • 添加 WHERE 条件:department.name = 'IT'。
        1. TypeScript 定义 (Interfaces)
          直接提供给前端使用的类型定义。
          // 基础标量类型
          type Scalar = string | number | boolean | Date | null;

        // 操作符定义
        type FilterOperators = {
        $eq?: T;
        $ne?: T;
        $in?: T[];
        $nin?: T[];
        $gt?: T; // 仅限 number/date
        $lt?: T;
        $contains?: string; // 仅限 string
        $startsWith?: string;
        $not?: FilterOperators;
        };

        // 递归过滤器定义
        export type Filter = {
        [K in keyof T]?:
        | T[K] // 隐式相等
        | FilterOperators<T[K]> // 显式操作符
        | Filter<T[K]>; // 关联表嵌套 (Join)
        } & {
        $and?: Filter[];
        $or?: Filter[];
        };

        1. 安全性与边界限制 (Security Guardrails)
          在实现解析器时,必须强制执行以下规则以防止恶意攻击:
        • 最大深度限制 (Max Depth): 防止深度嵌套导致的堆栈溢出(建议限制为 5-6 层)。
        • 字段白名单 (Field Whitelist): 解析器必须检查 Key 是否存在于定义的 Schema 中,禁止查询数据库中存在但未开放 API 的字段(如 password_hash, salt)。
        • 数组长度限制: $in 操作符的数组长度不得超过 1000(防止 SQL 性能问题)。
        • 禁止全表扫描: 强制要求查询必须包含索引字段(可选的高级配置)。
          下一步行动建议
        • 定义 Schema: 确定你的实体模型(Model)。
        • 编写 Normalizer: 实现阶段一的“标准化函数”,这一步是通用的。
        • 选择 ORM/QueryBuilder: 你的后端是用 TypeORM, Prisma, Knex 还是原生 SQL?这决定了 Adapter 的写法。

        Activity

        Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

        Metadata

        Metadata

        Labels

        No labels
        No labels

        Type

        No type

        Projects

        No projects

          Milestone

          No milestone

          Relationships

          None yet

          Development

          No branches or pull requests

          Issue actions

          , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
          Skip to content

          统一查询语言设计规范 (Unified Query DSL Specification) #50

          Description

          @hotlong

          这是一份基于行业最佳实践(参考 Prisma, Strapi, TypeORM, LoopBack)起草的通用查询语言(Generic Query DSL)设计规范文档。
          你可以直接将这份文档作为你团队内部的技术规格说明书(Spec)使用。
          统一查询语言设计规范 (Unified Query DSL Specification)
          版本: 1.0.0
          状态: 草案 (Draft)
          目标: 定义一套基于 JSON 的、跨数据库兼容的查询语法标准,用于前端与后端 API 的数据筛选交互。

          1. 核心设计原则
          • 声明式 (Declarative): 前端只描述“要什么数据”,不描述“如何查询数据库”。
          • 后端无关性 (Database Agnostic): 语法不包含特定数据库(如 SQL 或 Mongo)的专用指令。所有指令在后端通过适配器层(Adapter Layer)转换。
          • 类型安全 (Type Safety): 结构必须能被 TypeScript 静态推断。
          • 默认约定优于配置 (Convention over Configuration): 提供隐式写法以简化常见查询。
          1. 语法结构定义
            查询对象(Filter Object)是一个递归的树状结构。
            2.1 基础结构
            一个合法的查询对象由以下三种元素组成:
          • 隐式相等 (Implicit Equality): key: value
          • 显式操作符 (Explicit Operators): key: { $op: value }
          • 逻辑组合 (Logical Groups): $and, $or, $not
            2.2 完整示例
            {
            "where": {
            "status": "active", // 隐式相等 (AND)
            "age": { "$gte": 18 }, // 显式比较 (AND)
            "$or": [ // 逻辑分支
            { "role": "admin" },
            { "email": { "$contains": "@company.com" } }
            ],
            "profile": { // 关联查询 (Relation)
            "verified": true
            }
            }
            }
          1. 操作符标准 (Operator Standards)
            为了保证跨库兼容,必须严格限制支持的操作符清单。不要直接透传数据库指令。
            3.1 比较操作符 (Comparison)
            | 操作符 | 描述 | SQL 映射示例 | MongoDB 映射示例 | 数据类型限制 |
            |---|---|---|---|---|
            | $eq | 等于 (默认) | = | $eq | Any |
            | $ne | 不等于 | <> 或 != | $ne | Any |
            | $gt | 大于 | > | $gt | Number, Date |
            | $gte | 大于等于 | >= | $gte | Number, Date |
            | $lt | 小于 | < | $lt | Number, Date |
            | $lte | 小于等于 | <= | $lte | Number, Date |
            3.2 集合与区间 (Set & Range)
            | 操作符 | 描述 | SQL 映射示例 | MongoDB 映射示例 |
            |---|---|---|---|
            | $in | 在列表中 | IN (?, ?, ?) | $in: [...] |
            | $nin | 不在列表中 | NOT IN (...) | $nin: [...] |
            | $between | 区间 (闭合) | BETWEEN ? AND ? | $gte AND $lte |
            3.3 字符串专用 (String Specific)
            注意:此处需在后端处理大小写敏感(Case Sensitivity)配置。
            | 操作符 | 描述 | SQL 映射示例 | MongoDB 映射示例 |
            |---|---|---|---|
            | $contains | 包含 | LIKE %?% | $regex |
            | $startsWith | 前缀匹配 | LIKE ?% | $regex |
            | $endsWith | 后缀匹配 | LIKE %? | $regex |
            3.4 逻辑操作符 (Logical)
            | 操作符 | 描述 | SQL 映射 | MongoDB 映射 |
            |---|---|---|---|
            | $and | 逻辑与 | (A AND B) | $and |
            | $or | 逻辑或 | (A OR B) | $or |
            | $not | 逻辑非 | NOT (A) | $not |
            3.5 特殊检查 (Special)
            | 操作符 | 描述 | SQL 映射 | MongoDB 映射 |
            |---|---|---|---|
            | $null | 是否为空 | IS NULL (true) / IS NOT NULL (false) | field: null |
            | $exist | 字段是否存在 | (通常用于 NoSQL) | $exists |
          2. 解析与执行流程 (Architecture)
            这是实现跨数据库的核心架构逻辑。
            阶段一:标准化 (Normalization Pass)
            在进入适配器之前,必须将所有“语法糖”转换为“标准 AST 结构”。这能极大简化后续适配器的编写难度。
            规则:
          • 将所有 key: value 转换为 key: { $eq: value }。
          • 将同级的所有 Key 合并入 $and 数组。
            输入:
            { "age": 18, "role": "admin" }

          标准化后输出 (Internal IR):
          {
          "$and": [
          { "age": { "$eq": 18 } },
          { "role": { "$eq": "admin" } }
          ]
          }

          阶段二:适配器转换 (Adapter Translation)
          使用 Visitor Pattern 遍历标准化后的对象。
          SQL Adapter 伪逻辑:

          • 遍历对象。
          • 遇到 $and/$or: 递归生成子 SQL,用 AND/OR 连接,并包裹括号。
          • 遇到字段 { age: { $gt: 18 } }:
            • 提取字段名 age (需做安全校验,防 SQL 注入)。
            • 提取操作符 $gt -> 映射为 >。
            • 提取值 18 -> 存入 Parameters 数组,SQL 中替换为占位符 ? 或 $1。
          1. 关联查询规范 (Relation / Join)
            这是 ObjectQL 的核心优势,利用 JSON 的嵌套特性表达 SQL JOIN。
            规则:
            如果一个 Key 对应的值是对象(且不是操作符对象),则视为关联查询。
            输入:
            {
            "department": {
            "name": { "$eq": "IT" }
            }
            }

          SQL Adapter 行为:

          • 检测到 department 是关联字段。
          • 自动执行 INNER JOIN department ON users.dept_id = department.id。
          • 添加 WHERE 条件:department.name = 'IT'。
          1. TypeScript 定义 (Interfaces)
            直接提供给前端使用的类型定义。
            // 基础标量类型
            type Scalar = string | number | boolean | Date | null;

          // 操作符定义
          type FilterOperators = {
          $eq?: T;
          $ne?: T;
          $in?: T[];
          $nin?: T[];
          $gt?: T; // 仅限 number/date
          $lt?: T;
          $contains?: string; // 仅限 string
          $startsWith?: string;
          $not?: FilterOperators;
          };

          // 递归过滤器定义
          export type Filter = {
          [K in keyof T]?:
          | T[K] // 隐式相等
          | FilterOperators<T[K]> // 显式操作符
          | Filter<T[K]>; // 关联表嵌套 (Join)
          } & {
          $and?: Filter[];
          $or?: Filter[];
          };

          1. 安全性与边界限制 (Security Guardrails)
            在实现解析器时,必须强制执行以下规则以防止恶意攻击:
          • 最大深度限制 (Max Depth): 防止深度嵌套导致的堆栈溢出(建议限制为 5-6 层)。
          • 字段白名单 (Field Whitelist): 解析器必须检查 Key 是否存在于定义的 Schema 中,禁止查询数据库中存在但未开放 API 的字段(如 password_hash, salt)。
          • 数组长度限制: $in 操作符的数组长度不得超过 1000(防止 SQL 性能问题)。
          • 禁止全表扫描: 强制要求查询必须包含索引字段(可选的高级配置)。
            下一步行动建议
          • 定义 Schema: 确定你的实体模型(Model)。
          • 编写 Normalizer: 实现阶段一的“标准化函数”,这一步是通用的。
          • 选择 ORM/QueryBuilder: 你的后端是用 TypeORM, Prisma, Knex 还是原生 SQL?这决定了 Adapter 的写法。

          Activity

          Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

          Metadata

          Metadata

          Labels

          No labels
          No labels

          Type

          No type

          Projects

          No projects

            Milestone

            No milestone

            Relationships

            None yet

            Development

            No branches or pull requests

            Issue actions

            , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
            Skip to content

            统一查询语言设计规范 (Unified Query DSL Specification) #50

            Description

            @hotlong

            这是一份基于行业最佳实践(参考 Prisma, Strapi, TypeORM, LoopBack)起草的通用查询语言(Generic Query DSL)设计规范文档。
            你可以直接将这份文档作为你团队内部的技术规格说明书(Spec)使用。
            统一查询语言设计规范 (Unified Query DSL Specification)
            版本: 1.0.0
            状态: 草案 (Draft)
            目标: 定义一套基于 JSON 的、跨数据库兼容的查询语法标准,用于前端与后端 API 的数据筛选交互。

            1. 核心设计原则
            • 声明式 (Declarative): 前端只描述“要什么数据”,不描述“如何查询数据库”。
            • 后端无关性 (Database Agnostic): 语法不包含特定数据库(如 SQL 或 Mongo)的专用指令。所有指令在后端通过适配器层(Adapter Layer)转换。
            • 类型安全 (Type Safety): 结构必须能被 TypeScript 静态推断。
            • 默认约定优于配置 (Convention over Configuration): 提供隐式写法以简化常见查询。
            1. 语法结构定义
              查询对象(Filter Object)是一个递归的树状结构。
              2.1 基础结构
              一个合法的查询对象由以下三种元素组成:
            • 隐式相等 (Implicit Equality): key: value
            • 显式操作符 (Explicit Operators): key: { $op: value }
            • 逻辑组合 (Logical Groups): $and, $or, $not
              2.2 完整示例
              {
              "where": {
              "status": "active", // 隐式相等 (AND)
              "age": { "$gte": 18 }, // 显式比较 (AND)
              "$or": [ // 逻辑分支
              { "role": "admin" },
              { "email": { "$contains": "@company.com" } }
              ],
              "profile": { // 关联查询 (Relation)
              "verified": true
              }
              }
              }
            1. 操作符标准 (Operator Standards)
              为了保证跨库兼容,必须严格限制支持的操作符清单。不要直接透传数据库指令。
              3.1 比较操作符 (Comparison)
              | 操作符 | 描述 | SQL 映射示例 | MongoDB 映射示例 | 数据类型限制 |
              |---|---|---|---|---|
              | $eq | 等于 (默认) | = | $eq | Any |
              | $ne | 不等于 | <> 或 != | $ne | Any |
              | $gt | 大于 | > | $gt | Number, Date |
              | $gte | 大于等于 | >= | $gte | Number, Date |
              | $lt | 小于 | < | $lt | Number, Date |
              | $lte | 小于等于 | <= | $lte | Number, Date |
              3.2 集合与区间 (Set & Range)
              | 操作符 | 描述 | SQL 映射示例 | MongoDB 映射示例 |
              |---|---|---|---|
              | $in | 在列表中 | IN (?, ?, ?) | $in: [...] |
              | $nin | 不在列表中 | NOT IN (...) | $nin: [...] |
              | $between | 区间 (闭合) | BETWEEN ? AND ? | $gte AND $lte |
              3.3 字符串专用 (String Specific)
              注意:此处需在后端处理大小写敏感(Case Sensitivity)配置。
              | 操作符 | 描述 | SQL 映射示例 | MongoDB 映射示例 |
              |---|---|---|---|
              | $contains | 包含 | LIKE %?% | $regex |
              | $startsWith | 前缀匹配 | LIKE ?% | $regex |
              | $endsWith | 后缀匹配 | LIKE %? | $regex |
              3.4 逻辑操作符 (Logical)
              | 操作符 | 描述 | SQL 映射 | MongoDB 映射 |
              |---|---|---|---|
              | $and | 逻辑与 | (A AND B) | $and |
              | $or | 逻辑或 | (A OR B) | $or |
              | $not | 逻辑非 | NOT (A) | $not |
              3.5 特殊检查 (Special)
              | 操作符 | 描述 | SQL 映射 | MongoDB 映射 |
              |---|---|---|---|
              | $null | 是否为空 | IS NULL (true) / IS NOT NULL (false) | field: null |
              | $exist | 字段是否存在 | (通常用于 NoSQL) | $exists |
            2. 解析与执行流程 (Architecture)
              这是实现跨数据库的核心架构逻辑。
              阶段一:标准化 (Normalization Pass)
              在进入适配器之前,必须将所有“语法糖”转换为“标准 AST 结构”。这能极大简化后续适配器的编写难度。
              规则:
            • 将所有 key: value 转换为 key: { $eq: value }。
            • 将同级的所有 Key 合并入 $and 数组。
              输入:
              { "age": 18, "role": "admin" }

            标准化后输出 (Internal IR):
            {
            "$and": [
            { "age": { "$eq": 18 } },
            { "role": { "$eq": "admin" } }
            ]
            }

            阶段二:适配器转换 (Adapter Translation)
            使用 Visitor Pattern 遍历标准化后的对象。
            SQL Adapter 伪逻辑:

            • 遍历对象。
            • 遇到 $and/$or: 递归生成子 SQL,用 AND/OR 连接,并包裹括号。
            • 遇到字段 { age: { $gt: 18 } }:
              • 提取字段名 age (需做安全校验,防 SQL 注入)。
              • 提取操作符 $gt -> 映射为 >。
              • 提取值 18 -> 存入 Parameters 数组,SQL 中替换为占位符 ? 或 $1。
            1. 关联查询规范 (Relation / Join)
              这是 ObjectQL 的核心优势,利用 JSON 的嵌套特性表达 SQL JOIN。
              规则:
              如果一个 Key 对应的值是对象(且不是操作符对象),则视为关联查询。
              输入:
              {
              "department": {
              "name": { "$eq": "IT" }
              }
              }

            SQL Adapter 行为:

            • 检测到 department 是关联字段。
            • 自动执行 INNER JOIN department ON users.dept_id = department.id。
            • 添加 WHERE 条件:department.name = 'IT'。
            1. TypeScript 定义 (Interfaces)
              直接提供给前端使用的类型定义。
              // 基础标量类型
              type Scalar = string | number | boolean | Date | null;

            // 操作符定义
            type FilterOperators = {
            $eq?: T;
            $ne?: T;
            $in?: T[];
            $nin?: T[];
            $gt?: T; // 仅限 number/date
            $lt?: T;
            $contains?: string; // 仅限 string
            $startsWith?: string;
            $not?: FilterOperators;
            };

            // 递归过滤器定义
            export type Filter = {
            [K in keyof T]?:
            | T[K] // 隐式相等
            | FilterOperators<T[K]> // 显式操作符
            | Filter<T[K]>; // 关联表嵌套 (Join)
            } & {
            $and?: Filter[];
            $or?: Filter[];
            };

            1. 安全性与边界限制 (Security Guardrails)
              在实现解析器时,必须强制执行以下规则以防止恶意攻击:
            • 最大深度限制 (Max Depth): 防止深度嵌套导致的堆栈溢出(建议限制为 5-6 层)。
            • 字段白名单 (Field Whitelist): 解析器必须检查 Key 是否存在于定义的 Schema 中,禁止查询数据库中存在但未开放 API 的字段(如 password_hash, salt)。
            • 数组长度限制: $in 操作符的数组长度不得超过 1000(防止 SQL 性能问题)。
            • 禁止全表扫描: 强制要求查询必须包含索引字段(可选的高级配置)。
              下一步行动建议
            • 定义 Schema: 确定你的实体模型(Model)。
            • 编写 Normalizer: 实现阶段一的“标准化函数”,这一步是通用的。
            • 选择 ORM/QueryBuilder: 你的后端是用 TypeORM, Prisma, Knex 还是原生 SQL?这决定了 Adapter 的写法。

            Activity

            Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

            Metadata

            Metadata

            Labels

            No labels
            No labels

            Type

            No type

            Projects

            No projects

              Milestone

              No milestone

              Relationships

              None yet

              Development

              No branches or pull requests

              Issue actions

              , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
              Skip to content

              统一查询语言设计规范 (Unified Query DSL Specification) #50

              Description

              @hotlong

              这是一份基于行业最佳实践(参考 Prisma, Strapi, TypeORM, LoopBack)起草的通用查询语言(Generic Query DSL)设计规范文档。
              你可以直接将这份文档作为你团队内部的技术规格说明书(Spec)使用。
              统一查询语言设计规范 (Unified Query DSL Specification)
              版本: 1.0.0
              状态: 草案 (Draft)
              目标: 定义一套基于 JSON 的、跨数据库兼容的查询语法标准,用于前端与后端 API 的数据筛选交互。

              1. 核心设计原则
              • 声明式 (Declarative): 前端只描述“要什么数据”,不描述“如何查询数据库”。
              • 后端无关性 (Database Agnostic): 语法不包含特定数据库(如 SQL 或 Mongo)的专用指令。所有指令在后端通过适配器层(Adapter Layer)转换。
              • 类型安全 (Type Safety): 结构必须能被 TypeScript 静态推断。
              • 默认约定优于配置 (Convention over Configuration): 提供隐式写法以简化常见查询。
              1. 语法结构定义
                查询对象(Filter Object)是一个递归的树状结构。
                2.1 基础结构
                一个合法的查询对象由以下三种元素组成:
              • 隐式相等 (Implicit Equality): key: value
              • 显式操作符 (Explicit Operators): key: { $op: value }
              • 逻辑组合 (Logical Groups): $and, $or, $not
                2.2 完整示例
                {
                "where": {
                "status": "active", // 隐式相等 (AND)
                "age": { "$gte": 18 }, // 显式比较 (AND)
                "$or": [ // 逻辑分支
                { "role": "admin" },
                { "email": { "$contains": "@company.com" } }
                ],
                "profile": { // 关联查询 (Relation)
                "verified": true
                }
                }
                }
              1. 操作符标准 (Operator Standards)
                为了保证跨库兼容,必须严格限制支持的操作符清单。不要直接透传数据库指令。
                3.1 比较操作符 (Comparison)
                | 操作符 | 描述 | SQL 映射示例 | MongoDB 映射示例 | 数据类型限制 |
                |---|---|---|---|---|
                | $eq | 等于 (默认) | = | $eq | Any |
                | $ne | 不等于 | <> 或 != | $ne | Any |
                | $gt | 大于 | > | $gt | Number, Date |
                | $gte | 大于等于 | >= | $gte | Number, Date |
                | $lt | 小于 | < | $lt | Number, Date |
                | $lte | 小于等于 | <= | $lte | Number, Date |
                3.2 集合与区间 (Set & Range)
                | 操作符 | 描述 | SQL 映射示例 | MongoDB 映射示例 |
                |---|---|---|---|
                | $in | 在列表中 | IN (?, ?, ?) | $in: [...] |
                | $nin | 不在列表中 | NOT IN (...) | $nin: [...] |
                | $between | 区间 (闭合) | BETWEEN ? AND ? | $gte AND $lte |
                3.3 字符串专用 (String Specific)
                注意:此处需在后端处理大小写敏感(Case Sensitivity)配置。
                | 操作符 | 描述 | SQL 映射示例 | MongoDB 映射示例 |
                |---|---|---|---|
                | $contains | 包含 | LIKE %?% | $regex |
                | $startsWith | 前缀匹配 | LIKE ?% | $regex |
                | $endsWith | 后缀匹配 | LIKE %? | $regex |
                3.4 逻辑操作符 (Logical)
                | 操作符 | 描述 | SQL 映射 | MongoDB 映射 |
                |---|---|---|---|
                | $and | 逻辑与 | (A AND B) | $and |
                | $or | 逻辑或 | (A OR B) | $or |
                | $not | 逻辑非 | NOT (A) | $not |
                3.5 特殊检查 (Special)
                | 操作符 | 描述 | SQL 映射 | MongoDB 映射 |
                |---|---|---|---|
                | $null | 是否为空 | IS NULL (true) / IS NOT NULL (false) | field: null |
                | $exist | 字段是否存在 | (通常用于 NoSQL) | $exists |
              2. 解析与执行流程 (Architecture)
                这是实现跨数据库的核心架构逻辑。
                阶段一:标准化 (Normalization Pass)
                在进入适配器之前,必须将所有“语法糖”转换为“标准 AST 结构”。这能极大简化后续适配器的编写难度。
                规则:
              • 将所有 key: value 转换为 key: { $eq: value }。
              • 将同级的所有 Key 合并入 $and 数组。
                输入:
                { "age": 18, "role": "admin" }

              标准化后输出 (Internal IR):
              {
              "$and": [
              { "age": { "$eq": 18 } },
              { "role": { "$eq": "admin" } }
              ]
              }

              阶段二:适配器转换 (Adapter Translation)
              使用 Visitor Pattern 遍历标准化后的对象。
              SQL Adapter 伪逻辑:

              • 遍历对象。
              • 遇到 $and/$or: 递归生成子 SQL,用 AND/OR 连接,并包裹括号。
              • 遇到字段 { age: { $gt: 18 } }:
                • 提取字段名 age (需做安全校验,防 SQL 注入)。
                • 提取操作符 $gt -> 映射为 >。
                • 提取值 18 -> 存入 Parameters 数组,SQL 中替换为占位符 ? 或 $1。
              1. 关联查询规范 (Relation / Join)
                这是 ObjectQL 的核心优势,利用 JSON 的嵌套特性表达 SQL JOIN。
                规则:
                如果一个 Key 对应的值是对象(且不是操作符对象),则视为关联查询。
                输入:
                {
                "department": {
                "name": { "$eq": "IT" }
                }
                }

              SQL Adapter 行为:

              • 检测到 department 是关联字段。
              • 自动执行 INNER JOIN department ON users.dept_id = department.id。
              • 添加 WHERE 条件:department.name = 'IT'。
              1. TypeScript 定义 (Interfaces)
                直接提供给前端使用的类型定义。
                // 基础标量类型
                type Scalar = string | number | boolean | Date | null;

              // 操作符定义
              type FilterOperators = {
              $eq?: T;
              $ne?: T;
              $in?: T[];
              $nin?: T[];
              $gt?: T; // 仅限 number/date
              $lt?: T;
              $contains?: string; // 仅限 string
              $startsWith?: string;
              $not?: FilterOperators;
              };

              // 递归过滤器定义
              export type Filter = {
              [K in keyof T]?:
              | T[K] // 隐式相等
              | FilterOperators<T[K]> // 显式操作符
              | Filter<T[K]>; // 关联表嵌套 (Join)
              } & {
              $and?: Filter[];
              $or?: Filter[];
              };

              1. 安全性与边界限制 (Security Guardrails)
                在实现解析器时,必须强制执行以下规则以防止恶意攻击:
              • 最大深度限制 (Max Depth): 防止深度嵌套导致的堆栈溢出(建议限制为 5-6 层)。
              • 字段白名单 (Field Whitelist): 解析器必须检查 Key 是否存在于定义的 Schema 中,禁止查询数据库中存在但未开放 API 的字段(如 password_hash, salt)。
              • 数组长度限制: $in 操作符的数组长度不得超过 1000(防止 SQL 性能问题)。
              • 禁止全表扫描: 强制要求查询必须包含索引字段(可选的高级配置)。
                下一步行动建议
              • 定义 Schema: 确定你的实体模型(Model)。
              • 编写 Normalizer: 实现阶段一的“标准化函数”,这一步是通用的。
              • 选择 ORM/QueryBuilder: 你的后端是用 TypeORM, Prisma, Knex 还是原生 SQL?这决定了 Adapter 的写法。

              Activity

              Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

              Metadata

              Metadata

              Labels

              No labels
              No labels

              Type

              No type

              Projects

              No projects

                Milestone

                No milestone

                Relationships

                None yet

                Development

                No branches or pull requests

                Issue actions

                , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
                Skip to content

                统一查询语言设计规范 (Unified Query DSL Specification) #50

                Description

                @hotlong

                这是一份基于行业最佳实践(参考 Prisma, Strapi, TypeORM, LoopBack)起草的通用查询语言(Generic Query DSL)设计规范文档。
                你可以直接将这份文档作为你团队内部的技术规格说明书(Spec)使用。
                统一查询语言设计规范 (Unified Query DSL Specification)
                版本: 1.0.0
                状态: 草案 (Draft)
                目标: 定义一套基于 JSON 的、跨数据库兼容的查询语法标准,用于前端与后端 API 的数据筛选交互。

                1. 核心设计原则
                • 声明式 (Declarative): 前端只描述“要什么数据”,不描述“如何查询数据库”。
                • 后端无关性 (Database Agnostic): 语法不包含特定数据库(如 SQL 或 Mongo)的专用指令。所有指令在后端通过适配器层(Adapter Layer)转换。
                • 类型安全 (Type Safety): 结构必须能被 TypeScript 静态推断。
                • 默认约定优于配置 (Convention over Configuration): 提供隐式写法以简化常见查询。
                1. 语法结构定义
                  查询对象(Filter Object)是一个递归的树状结构。
                  2.1 基础结构
                  一个合法的查询对象由以下三种元素组成:
                • 隐式相等 (Implicit Equality): key: value
                • 显式操作符 (Explicit Operators): key: { $op: value }
                • 逻辑组合 (Logical Groups): $and, $or, $not
                  2.2 完整示例
                  {
                  "where": {
                  "status": "active", // 隐式相等 (AND)
                  "age": { "$gte": 18 }, // 显式比较 (AND)
                  "$or": [ // 逻辑分支
                  { "role": "admin" },
                  { "email": { "$contains": "@company.com" } }
                  ],
                  "profile": { // 关联查询 (Relation)
                  "verified": true
                  }
                  }
                  }
                1. 操作符标准 (Operator Standards)
                  为了保证跨库兼容,必须严格限制支持的操作符清单。不要直接透传数据库指令。
                  3.1 比较操作符 (Comparison)
                  | 操作符 | 描述 | SQL 映射示例 | MongoDB 映射示例 | 数据类型限制 |
                  |---|---|---|---|---|
                  | $eq | 等于 (默认) | = | $eq | Any |
                  | $ne | 不等于 | <> 或 != | $ne | Any |
                  | $gt | 大于 | > | $gt | Number, Date |
                  | $gte | 大于等于 | >= | $gte | Number, Date |
                  | $lt | 小于 | < | $lt | Number, Date |
                  | $lte | 小于等于 | <= | $lte | Number, Date |
                  3.2 集合与区间 (Set & Range)
                  | 操作符 | 描述 | SQL 映射示例 | MongoDB 映射示例 |
                  |---|---|---|---|
                  | $in | 在列表中 | IN (?, ?, ?) | $in: [...] |
                  | $nin | 不在列表中 | NOT IN (...) | $nin: [...] |
                  | $between | 区间 (闭合) | BETWEEN ? AND ? | $gte AND $lte |
                  3.3 字符串专用 (String Specific)
                  注意:此处需在后端处理大小写敏感(Case Sensitivity)配置。
                  | 操作符 | 描述 | SQL 映射示例 | MongoDB 映射示例 |
                  |---|---|---|---|
                  | $contains | 包含 | LIKE %?% | $regex |
                  | $startsWith | 前缀匹配 | LIKE ?% | $regex |
                  | $endsWith | 后缀匹配 | LIKE %? | $regex |
                  3.4 逻辑操作符 (Logical)
                  | 操作符 | 描述 | SQL 映射 | MongoDB 映射 |
                  |---|---|---|---|
                  | $and | 逻辑与 | (A AND B) | $and |
                  | $or | 逻辑或 | (A OR B) | $or |
                  | $not | 逻辑非 | NOT (A) | $not |
                  3.5 特殊检查 (Special)
                  | 操作符 | 描述 | SQL 映射 | MongoDB 映射 |
                  |---|---|---|---|
                  | $null | 是否为空 | IS NULL (true) / IS NOT NULL (false) | field: null |
                  | $exist | 字段是否存在 | (通常用于 NoSQL) | $exists |
                2. 解析与执行流程 (Architecture)
                  这是实现跨数据库的核心架构逻辑。
                  阶段一:标准化 (Normalization Pass)
                  在进入适配器之前,必须将所有“语法糖”转换为“标准 AST 结构”。这能极大简化后续适配器的编写难度。
                  规则:
                • 将所有 key: value 转换为 key: { $eq: value }。
                • 将同级的所有 Key 合并入 $and 数组。
                  输入:
                  { "age": 18, "role": "admin" }

                标准化后输出 (Internal IR):
                {
                "$and": [
                { "age": { "$eq": 18 } },
                { "role": { "$eq": "admin" } }
                ]
                }

                阶段二:适配器转换 (Adapter Translation)
                使用 Visitor Pattern 遍历标准化后的对象。
                SQL Adapter 伪逻辑:

                • 遍历对象。
                • 遇到 $and/$or: 递归生成子 SQL,用 AND/OR 连接,并包裹括号。
                • 遇到字段 { age: { $gt: 18 } }:
                  • 提取字段名 age (需做安全校验,防 SQL 注入)。
                  • 提取操作符 $gt -> 映射为 >。
                  • 提取值 18 -> 存入 Parameters 数组,SQL 中替换为占位符 ? 或 $1。
                1. 关联查询规范 (Relation / Join)
                  这是 ObjectQL 的核心优势,利用 JSON 的嵌套特性表达 SQL JOIN。
                  规则:
                  如果一个 Key 对应的值是对象(且不是操作符对象),则视为关联查询。
                  输入:
                  {
                  "department": {
                  "name": { "$eq": "IT" }
                  }
                  }

                SQL Adapter 行为:

                • 检测到 department 是关联字段。
                • 自动执行 INNER JOIN department ON users.dept_id = department.id。
                • 添加 WHERE 条件:department.name = 'IT'。
                1. TypeScript 定义 (Interfaces)
                  直接提供给前端使用的类型定义。
                  // 基础标量类型
                  type Scalar = string | number | boolean | Date | null;

                // 操作符定义
                type FilterOperators = {
                $eq?: T;
                $ne?: T;
                $in?: T[];
                $nin?: T[];
                $gt?: T; // 仅限 number/date
                $lt?: T;
                $contains?: string; // 仅限 string
                $startsWith?: string;
                $not?: FilterOperators;
                };

                // 递归过滤器定义
                export type Filter = {
                [K in keyof T]?:
                | T[K] // 隐式相等
                | FilterOperators<T[K]> // 显式操作符
                | Filter<T[K]>; // 关联表嵌套 (Join)
                } & {
                $and?: Filter[];
                $or?: Filter[];
                };

                1. 安全性与边界限制 (Security Guardrails)
                  在实现解析器时,必须强制执行以下规则以防止恶意攻击:
                • 最大深度限制 (Max Depth): 防止深度嵌套导致的堆栈溢出(建议限制为 5-6 层)。
                • 字段白名单 (Field Whitelist): 解析器必须检查 Key 是否存在于定义的 Schema 中,禁止查询数据库中存在但未开放 API 的字段(如 password_hash, salt)。
                • 数组长度限制: $in 操作符的数组长度不得超过 1000(防止 SQL 性能问题)。
                • 禁止全表扫描: 强制要求查询必须包含索引字段(可选的高级配置)。
                  下一步行动建议
                • 定义 Schema: 确定你的实体模型(Model)。
                • 编写 Normalizer: 实现阶段一的“标准化函数”,这一步是通用的。
                • 选择 ORM/QueryBuilder: 你的后端是用 TypeORM, Prisma, Knex 还是原生 SQL?这决定了 Adapter 的写法。

                Activity

                Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

                Metadata

                Metadata

                Labels

                No labels
                No labels

                Type

                No type

                Projects

                No projects

                  Milestone

                  No milestone

                  Relationships

                  None yet

                  Development

                  No branches or pull requests

                  Issue actions