关于 db-query
db-query 是供 function-creator 等创作技能参考的 `$db` 查询指南,面向在 Datex Studio 中编写存储访问代码的开发者。它涵盖谓词 DSL、存储操作和模式变更审计,并明确自身只提供参考,不负责创建或推送组件。
主要功能
- 01说明 `$db` 谓词 DSL
- 02介绍读取、写入与更新操作
- 03检查字段和必填列约束
- 04指导存储模式变更审计
- 05涵盖流程数据源访问模式
适用场景
- 01为函数编写 `$db` 查询
- 02检查补丁是否包含必填字段
- 03排查存储字段改名的调用影响
- 04为网格或选择器配置流程数据源
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ datex/ skills --skill db-query安装后在 Claude Code 中输入/db-query确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
evals/evals.jsonreferences/db.mdflow-db-datasources.md详细介绍
$db 查询指南
$db 是函数层运行时句柄,用于读写持久化在云端的 Mongo 后端存储组件。谓词使用流式运算符 DSL,并会编译为服务端查询,而不是原生 JavaScript 求值。这是在编写 $db 代码前最重要的一点,因为原生运算符可以编译成功,却会在运行时悄然产生错误结果。
本技能仅供只读查阅:它负责 $db 的机制(谓词 DSL、运算符、模式变更审计),以及通过 $db 访问流数据源的子变体。它不会单独创建函数、数据源或存储组件——这些往返操作由调用本技能的父级创建技能负责(通常是 function-creator)。
另请参阅:
function-creator— 编写承载$db调用的后端流。db-query 负责谓词 DSL 和存储访问机制;function-creator 负责函数生命周期(dxs function generate/validate/upsert)。
参考资料
- ../datex-studio-shared/branch-setup.md — 分支/连接选择(各技能共用)
- references/db.md — 权威
$db参考:访问路径、API 表面、谓词 DSL、结果类型规则、隐式id列、先读取再修补的惯用模式 - references/flow-db-datasources.md — 流类型数据源(
type: "flows"、configurationTypeId: 6/19),其getListFlow从$db读取数据并提供给网格或选择器 — 分页、动态筛选/排序辅助流、四处注册镜像 - ../datex-studio-conventions/naming-conventions.md — 存储字段和动态筛选管线一律使用 snake_case 的规则
- ../datex-studio-runtime/runtime-globals.md —
$db与$flow、$apis、$flows、$datasources、$test并列 - ../datex-studio-runtime/calling-conventions.md — 说明为何操作无法访问
$db的层级矩阵 - ../storage-creator/references/storage.md — 存储组件结构、列描述符、
required: true的陷阱 - ../function-creator/references/functions.md — 调用方层级;封装
$db调用的函数体往返规则 - ../datasource-creator/references/odata-datasources.md — OData 数据源中相近的结果类型可选规则
- ../datasource-creator/references/flow-datasources.md — 流类型数据源结构和实体定义约定
依赖项
function-creator技能 — 典型的调用方。它负责函数的往返规则(dxs configuration get -O envelope.json→jq .json→ 编辑 →dxs configuration upsert),该函数的code字符串中包含$db调用。此技能是在该工作流内部调用的,而非独立使用。storage-creator技能 — 当谓词或补丁生效前需要添加、删除或重命名列时调用。datasource-creator技能 — 当$db调用位于流程类型数据源的getListFlow/getByKeysFlow/getFlow槽位时调用(参见references/flow-db-datasources.md中的网格/选择器底层模式)。grid-creator/selector-creator技能 — 当基于$db的流程数据源接入网格或选择器时,与datasource-creator一起调用(references/flow-db-datasources.md§ 8 中的四处注册镜像涵盖这两种组件)。impact-analysis技能 — 每当正在进行存储架构变更时调用。通过$db.<Package>.<storage_referenceName>反向追踪每个调用方,并将命中项分类为读侧或写侧,以便在架构变更生效前审查每个调用点。post-edit-verification/component-validator技能 — 父级创建器技能推送包含$db调用的函数/数据源/存储后调用。此技能本身不会推送任何内容。
CLI 生命周期
$db 是运行时 API,而非 Studio 配置类型,因此没有 dxs db 子命令,也无需对 $db 调用本身进行往返。你所做的所有更改都是调用 $db 的配置中的嵌入式 code 字符串:
| 调用方 | 配置类型 | 负责往返的技能 |
|---|---|---|
| 函数体 | function(configurationTypeId: 9,-flow.json) | function-creator |
| 流程类型数据源中的流程槽位 | datasource(configurationTypeId: 6 或 19,带有 type: "flows" 的 -datasource.json) | datasource-creator |
| 架构审查/列重命名 | storage(-storage.json) | storage-creator(调用方另需 impact-analysis) |
此技能将指导你正确编写谓词/补丁;父级创建器技能负责往返规则(get -O envelope.json → jq .json envelope.json > body.json → 编辑 → update)。完成谓词后,返回该技能进行推送。
工作流
[Phase 1: Setup + Context]
Follow branch-setup.md for branch/connection selection (if the
parent skill hasn't already)
|
Identify the storage:
- storage referenceName (matches *-storage.json filename stem)
- package the storage lives in ($db.<Package>.<storage>)
- caller tier (function-tier ONLY — never an action, never UI)
- schema (objectTypeDef[] field ids — predicates / patches
must match these exactly)
|
[Phase 2: Pick the $db operation]
Read vs mutate? Single-row vs list? Read-then-patch needed?
- read list -> .where(...).sort(...).take(n).toList()
- read single by id -> .where(r => r.id.equals(guid)).toList()
- insert one -> .add(record)
- insert batch -> .addMany(records)
- patch one -> .update(id, patch)
(read-then-patch if storage has
any `required: true` columns)
- delete matching -> .removeMany(predicate)
Consult references/db.md → API Surface before picking
|
[Phase 3: Author the predicate / patch]
Fluent DSL only — no native operators:
- .equals / .in / .isNull / .and / .or (NOT === / && / ||)
- .update(id, patch) takes id string DIRECTLY, not a callback
- Result rows are TypeScript-optional even for required columns
- Field ids must match storage objectTypeDef[].id exactly
Walk references/db.md → Pre-Flight Checklist before push
|
[Phase 4 (optional): Backing-datasource decision]
Is the $db call wrapping a grid or selector? If so:
- YES -> consult references/flow-db-datasources.md for the full
authoring rules (getQuery factory, dynamic filter /
order-by helper flows, snake_case-everywhere rule,
four-location registration mirror, $orderby / $filter
inParam declarations)
Invoke `datasource-creator` (+ `grid-creator` /
`selector-creator` for the consumer side) for the
round-trip
- NO -> the $db call lives directly inside a function body;
return to `function-creator` for the round-trip
|
Schema change in flight? -> invoke `impact-analysis` with
`$db.<Package>.<storage_referenceName>` BEFORE the parent
creator skill pushes the storage edit
阶段详情
阶段 1:设置 + 上下文
- 如果父级技能(通常是
function-creator)尚未设置分支和连接,请按照 ../datex-studio-shared/branch-setup.md 进行选择。切勿假设分支 ID——请与用户确认。 - **确定正在访问的存储。**访问路径为
$db.<Package>.<storage_referenceName>:<storage_referenceName>与-storage.json文件名的主体部分一致(例如widget_rule_storage)。<Package>是存储组件所属的包,通常是功能包(Acme),而不是默认的Utilities。直接读取存储文件中的包声明;切勿根据功能文件夹名称推断。
- 确认调用方层级。
$db仅适用于函数层级。确认调用配置属于以下任一类型:函数(configurationTypeId: 9,-flow.json),或流类型数据源中的流槽位(-datasource.json中type: "flows"、配置类型为6或19,且包含getListFlow/getByKeysFlow/getFlow)。它不能在操作(-footprintFlow.json)中使用,也不会向 UI 组件公开。操作需要访问存储时,请将读写操作封装在函数中,并通过$apis.<Package>.FootprintApi.extendedActions.<function>调用。 - 读取存储架构——存储文件中的
objectTypeDef[]数组列出了每一列的id(snake_case)、type、isCollection和required标记。谓词和补丁中引用的字段名称必须与这些id完全一致;架构中的字段重命名会导致所有未更新的调用点失效(参见阶段 4 的架构变更审查)。
阶段 2:选择 $db 操作
查阅 references/db.md → API Surface,并选择符合意图的操作:
| 意图 | API 调用 |
|---|---|
| 读取匹配行的列表 | .where(predicate).sort({...}).take(n).toList() |
| 按 id 读取单行 | .where(r => r.id.equals(guid)).toList()(或省略 .where 以读取“全部”) |
| 插入一条记录 | .add(record) |
| 批量插入 | .addMany(records) |
| 按 id 修补记录 | .update(id, patch)——第一个参数直接传入 id 字符串,而不是谓词回调 |
| 修补所有匹配行 | .updateMany(predicate, patch) |
| 原子认领 / 测试并设置单行 | .findOneAndModify(predicate, patch, { sort?, select?, returnDocument? })——唯一的 CAS 原语;没有 upsert 选项,无法执行“不存在时插入”(参见 references/db.md) |
| 按 id 删除单行 | .remove(id) |
| 删除匹配行 | .removeMany(predicate) |
| 统计匹配行 | .count()(终结操作——有关 getQuery() 工厂规则,请参阅 flow-db-datasources) |
.where(...) 会返回一个游标——只有在执行 .toList() 或 .count() 等终结操作时,才会访问存储。多个可链式调用的阶段(.where(...).sort(...).take(10).toList())会组合成一次服务端查询。
如果存储中有任何 required: true 列,且意图是修补,请规划一个先读取再修补的流程:先获取该行,再构建补丁,将必需字段与实际变更一并写入。请参阅 references/db.md → Read-then-patch for a storage with required: true columns。
阶段 3:编写谓词 / 补丁
根据 references/db.md 中的规则编写调用。以下关键约束若遭违反,都会导致静默失败:
- 谓词 DSL 是链式 API,而非原生语法。 所有比较都必须使用
.equals(...)/.in([...])/.isNull(),不能使用===/!==。所有布尔组合都必须使用.and(...)/.or(...),不能使用&&/||。TypeScript 会接受错误的形式而不报错(列表达式对象的类型限制较宽松),因此此类错误会在运行时表现为翻译错误或结果集悄然出错。 .update(id, patch)直接接收 id 字符串。 它不是谓词回调签名。- 补丁必须包含
required: true的列。 平台会根据完整的列架构验证补丁;即使现有记录已填充该字段,遗漏必填列也会导致更新失败。 - 字段 id 必须与
objectTypeDef[].id完全一致。 存储中的 snake_case 会贯穿每个谓词和补丁。 - 包访问权限正确。 调用方的包必须有权访问存储所属的包(同一个包,或声明了依赖项)。
- 必须包含游标终止调用。 每个
.where(...)链都必须以.toList()结束(计数时使用.count(),或使用变更调用)。未终止的游标不会执行。 - 结果行的本地类型必须全部设为可选(或不显式声明,交由类型推断)。
$db返回的每个字段类型都是T | undefined,即使required: true的列也是如此——平台的类型生成器不会将required传递到调用方的行类型。严格的本地类型注解会导致导入失败。OData 和 flow-type 数据源也有相同表现——在类型层面应将这三种结果形态一视同仁。
交还给父级 creator skill 前,请阅读 references/db.md → 预检清单。
第 4 阶段(可选):底层数据源决策
如果 $db 调用封装了网格或选择器,即它位于流程类型数据源(type: "flows"、configurationTypeId: 6 或 19)的 getListFlow / getByKeysFlow / getFlow 插槽中,则该模式除上述 $db 基本检查清单外还有其他规则:
getQuery()工厂——游标只能使用一次。将查询主体封装在工厂中,并为每个终结操作调用一次;切勿在.toList()和.count()之间复用游标。- 只使用一个
.where(r => …)回调,通过predicate = predicate.and(...)进行组合;切勿链式调用.where(...).where(...)。 - 将分页下推到
$db,使用.skip($skip).take($top).toList();通过Promise.all并行执行.count()。 - 不区分大小写的全文搜索使用
r.<field>.includes(\(?i)${needle}`)——切勿使用.toLowerCase().includes(...)`(DSL 中不存在该 JS 方法)。 - 动态筛选 / 排序来自
apply_storage_datasource_dynamic_filters_flow/apply_storage_datasource_dynamic_order_by_flow(Utilities 包);辅助函数的column名称会原样传递为r[filter.column],因此网格中每个列的dynamicFilter/dynamicOrderBy值都必须与存储的 snake_case 字段 ID 匹配(对于聚合后形状,还必须与传递到聚合行中的相同 snake_case 键匹配)。 - 所有内部标识符均使用 snake_case——流程的 inParams、outParam
objectTypeDef[].id、queryOptionsObjectTypeDef[].id、$orderby.column和$filter.operands.*Filters.column的oneOf.constantValue字面值、网格列id/source、动态注册项id/property、每列的dynamicOrderBy/dynamicFilter/dynamicFilterType.id。内部代码块中唯一保留的 camelCase 是外部选择器契约(warehouse_dd→warehouseId、owners_dd→projectId);请通过读取选择器自身的inParams[0].id进行确认。 - 五处实体架构声明必须保持同步(参见
references/flow-db-datasources.md§ 11 和规范的网格接线规则)。只在部分位置重命名字段虽能通过类型检查,运行时却会抛出“Property X not exists in entity definition”。 - 四处注册配置必须保持一致——
datasourceConfig.dynamicFilters / dynamicOrderBys≡datasources[0].dynamicFilters / dynamicOrderBys,并在datasources[0]上将allSelectedIsDynamicFilters/allSelectedIsDynamicOrderBys设为true。
完整演练和代码模板请参阅 references/flow-db-datasources.md。数据源本身(以及任何使用它的网格 / 选择器)的往返流程由 datasource-creator / grid-creator / selector-creator 负责;谓词主体正确后,请返回上级 creator 技能。
架构变更审计(跨领域)
存储架构变更(添加 / 删除 / 重命名列)时,每个调用方都可能需要修改。在上级 creator 技能推送存储修改前,请执行以下操作:
- 调用
impact-analysis技能,传入$db.<Package>.<storage_referenceName>——它会分别枚举读取端和写入端的调用位置,并将多文件扫描留在本技能之外。 - 对于每个命中项,确认谓词和补丁中的字段 ID 仍与
objectTypeDef[]的id完全匹配。重命名会使所有未更新的位置悄然出错(谓词访问未定义的列表达式,导致查询实质上失效)。 - 对于新增的
required: true列,每个现有的.update(id, patch)调用方都必须开始回传该字段——读取后再打补丁将成为强制要求。这是最容易发生偏差的方向;请在存储修改落地前明确告知用户。
上线前检查清单
请完整检查 references/db.md 中的清单。快速版:
- 调用方层级。 此代码位于函数(
configurationTypeId: 9)或流类型数据源内的流插槽中。不是操作。不是 UI。 - 谓词 DSL 使用链式调用。 每次比较都使用
.equals(...)/.in([...])/.isNull(),而不是===/!==。每次布尔组合都使用.and(...)/.or(...),而不是&&/||。 .update(id, patch)直接将 id 字符串作为第一个参数传入,而不是传入谓词回调。- 补丁中会通过先读取、再应用补丁的方式回传
required: true列。不得省略必需列。 - 字段名称与存储的
objectTypeDef[]id 完全一致。 - 程序包访问权限正确。 调用方的程序包有权访问存储所在的程序包。
- 游标链以终止方法结束。 每个
.where(...)链都以.toList()(或.count()、或变更调用)结束;未终止的游标不会执行。 - 结果行的本地类型必须全部可选(或保留推断类型)。严格类型注解(包括
id: string)会导致导入失败,因为$db结果中的每个字段类型都是T | undefined,与 OData 和流类型数据源一致。 - 如果这是基于
$db的流类型数据源(由网格或选择器提供数据),请检查 references/flow-db-datasources.md → 上线前检查清单 中的附加清单:getQuery()工厂、单个.where回调、将分页交由$db处理、(?i)正则全文搜索、在顶层调用辅助流、在getListFlow.inParams中声明$orderby/$filter、原样使用 snake_case 的规则、同步镜像四个注册数组、每列的dynamicFilterControl与类型匹配、mapRow保持结果形状一致、同步五处行形状、同步$orderby/$filter的 oneOf 字面量、hub 挂载的 inParams 与网格的 inParams 匹配。 - 正在进行架构变更? 使用
$db.<Package>.<storage_referenceName>调用impact-analysis,并在父级创建者技能推送存储变更前审核所有命中项。
常见错误
| 错误做法 | 修正方法 |
|---|---|
在 $db 谓词中使用 === / && / ` | |
将谓词回调作为第一个参数调用 .update(id, patch) | 函数签名要求直接传入 id 字符串。使用 .update(guid, { ... }),而不是 .update(r => r.id.equals(guid), { ... })。 |
从 .update 补丁中省略 required: true 列 | 平台会根据完整的列架构验证补丁;即使现有记录已填充该字段,调用也会失败。先读取再打补丁——回传每个必填字段。 |
将列补丁为 null 以清除其值 | 值为 null 的补丁键会被静默丢弃——该列保留旧值,且不会报错。请写入带类型的哨兵值(例如,在 epoch-ms 列中写入 0),并在读取时将其映射回 null。参见 references/db.md。 |
| 假设多行写入具有原子性 | $db 不支持事务。请对写入排序,确保任何崩溃交错都能自行修复(通过 .findOneAndModify 原子切换获胜项之前,先标记失败项),并确保当行已处于目标状态时,每次写入都不执行操作。 |
使用严格的本地类型(id: string)注释结果行 | $db 返回的每个字段类型都是 `T |
在操作(-footprintFlow.json)中使用 $db | 层级不匹配——$db 仅供函数层使用。请将读写操作封装在函数中,并在操作中通过 $apis.<Package>.FootprintApi.extendedActions.<function> 调用该函数。 |
忘记在 .where(...) 链末尾调用终结方法 | 游标在终结方法运行前不会执行。const cursor = $db.<...>.where(...); 这行代码会返回游标;还需要调用 .toList()(或 .count(),或执行变更操作)。 |
谓词/补丁中的字段 id 与存储的 objectTypeDef[].id 不匹配 | 不会报错——谓词访问的是未定义的列表达式,最终查询实际上会被折叠。请精确匹配 snake_case id。每次重命名后都运行 impact-analysis。 |
在 flow-datasource 绑定槽位中,在 .toList() 和 .count() 之间复用游标 | 终结方法会消耗游标;第二次调用不会执行,并返回过时或错误的数据。请将查询主体封装在 getQuery() 工厂中,并针对每个终结方法分别调用。参见 references/flow-db-datasources.md § 1。 |
在 flow-datasource 绑定槽位中链式调用 .where(...).where(...) | 每个链式 .where 都会创建一个新的游标阶段,组合方式不可预测。请在一个回调中使用 predicate = predicate.and(...) 组合完整谓词。参见 references/flow-db-datasources.md § 2。 |
使用 .toLowerCase().includes(...) 进行不区分大小写的全文匹配 | 列表达式不支持该 JS 方法;即使支持,匹配也会在获取数据后逐行通过 JS 求值。请使用 r.<field>.includes(\(?i)${needle}`)` 进行 Mongo 端不区分大小写的匹配。 |
| 在 flow-datasource 绑定槽位中将内部标识符改为 camelCase | 动态筛选循环会将 filter.column 原样传入 r[filter.column];camelCase 值无法匹配 snake_case 存储字段,导致谓词在运行时抛出异常。所有内部标识符都使用 snake_case;唯一保留 camelCase 的是外部选择器契约。 |
在高容量事实存储上构建 distinct-values *_dd 选择器 | 每次打开下拉菜单都会触发 .toList() 扫描整张表。默认使用 textBox 在可读字符串字段上筛选;selectBox 筛选仅用于有界参考存储。参见 references/flow-db-datasources.md § 8。 |
更改存储架构前跳过 impact-analysis | 重命名列或新增 required: true 会导致所有未更新的调用方出错,且不会发出提示。在存储修改落地前,使用 $db.<Package>.<storage_referenceName> 反向追踪。 |
编写使用 $db 的函数或数据源后,请调用 post-edit-verification(通过父级创建器技能);如果出现 $db 验证错误,请重新阅读此处的相关部分。 |