mcpField
D
db-query

db-query

github.com/datex数据与数据库93 次安装
代码仓库

关于 db-query

db-query 是供 function-creator 等创作技能参考的 `$db` 查询指南,面向在 Datex Studio 中编写存储访问代码的开发者。它涵盖谓词 DSL、存储操作和模式变更审计,并明确自身只提供参考,不负责创建或推送组件。

主要功能

  1. 01说明 `$db` 谓词 DSL
  2. 02介绍读取、写入与更新操作
  3. 03检查字段和必填列约束
  4. 04指导存储模式变更审计
  5. 05涵盖流程数据源访问模式

适用场景

  1. 01为函数编写 `$db` 查询
  2. 02检查补丁是否包含必填字段
  3. 03排查存储字段改名的调用影响
  4. 04为网格或选择器配置流程数据源

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/datex/skills --skill db-query

安装后在 Claude Code 中输入/db-query确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

evals/
evals.json
references/
db.md
flow-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)。

参考资料

依赖项

  • 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:设置 + 上下文
  1. 如果父级技能(通常是 function-creator)尚未设置分支和连接,请按照 ../datex-studio-shared/branch-setup.md 进行选择。切勿假设分支 ID——请与用户确认。
  2. **确定正在访问的存储。**访问路径为 $db.<Package>.<storage_referenceName>:
    • <storage_referenceName> 与 -storage.json 文件名的主体部分一致(例如 widget_rule_storage)。
    • <Package> 是存储组件所属的包,通常是功能包(Acme),而不是默认的 Utilities。直接读取存储文件中的包声明;切勿根据功能文件夹名称推断。
  3. 确认调用方层级。$db 仅适用于函数层级。确认调用配置属于以下任一类型:函数(configurationTypeId: 9,-flow.json),或流类型数据源中的流槽位(-datasource.json 中 type: "flows"、配置类型为 6 或 19,且包含 getListFlow / getByKeysFlow / getFlow)。它不能在操作(-footprintFlow.json)中使用,也不会向 UI 组件公开。操作需要访问存储时,请将读写操作封装在函数中,并通过 $apis.<Package>.FootprintApi.extendedActions.<function> 调用。
  4. 读取存储架构——存储文件中的 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 中的规则编写调用。以下关键约束若遭违反,都会导致静默失败:

  1. 谓词 DSL 是链式 API,而非原生语法。 所有比较都必须使用 .equals(...) / .in([...]) / .isNull(),不能使用 === / !==。所有布尔组合都必须使用 .and(...) / .or(...),不能使用 && / ||。TypeScript 会接受错误的形式而不报错(列表达式对象的类型限制较宽松),因此此类错误会在运行时表现为翻译错误或结果集悄然出错。
  2. .update(id, patch) 直接接收 id 字符串。 它不是谓词回调签名。
  3. 补丁必须包含 required: true 的列。 平台会根据完整的列架构验证补丁;即使现有记录已填充该字段,遗漏必填列也会导致更新失败。
  4. 字段 id 必须与 objectTypeDef[].id 完全一致。 存储中的 snake_case 会贯穿每个谓词和补丁。
  5. 包访问权限正确。 调用方的包必须有权访问存储所属的包(同一个包,或声明了依赖项)。
  6. 必须包含游标终止调用。 每个 .where(...) 链都必须以 .toList() 结束(计数时使用 .count(),或使用变更调用)。未终止的游标不会执行。
  7. 结果行的本地类型必须全部设为可选(或不显式声明,交由类型推断)。$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 技能推送存储修改前,请执行以下操作:

  1. 调用 impact-analysis 技能,传入 $db.<Package>.<storage_referenceName>——它会分别枚举读取端和写入端的调用位置,并将多文件扫描留在本技能之外。
  2. 对于每个命中项,确认谓词和补丁中的字段 ID 仍与 objectTypeDef[] 的 id 完全匹配。重命名会使所有未更新的位置悄然出错(谓词访问未定义的列表达式,导致查询实质上失效)。
  3. 对于新增的 required: true 列,每个现有的 .update(id, patch) 调用方都必须开始回传该字段——读取后再打补丁将成为强制要求。这是最容易发生偏差的方向;请在存储修改落地前明确告知用户。

上线前检查清单

请完整检查 references/db.md 中的清单。快速版:

  1. 调用方层级。 此代码位于函数(configurationTypeId: 9)或流类型数据源内的流插槽中。不是操作。不是 UI。
  2. 谓词 DSL 使用链式调用。 每次比较都使用 .equals(...) / .in([...]) / .isNull(),而不是 === / !==。每次布尔组合都使用 .and(...) / .or(...),而不是 && / ||。
  3. .update(id, patch) 直接将 id 字符串作为第一个参数传入,而不是传入谓词回调。
  4. 补丁中会通过先读取、再应用补丁的方式回传 required: true 列。不得省略必需列。
  5. 字段名称与存储的 objectTypeDef[] id 完全一致。
  6. 程序包访问权限正确。 调用方的程序包有权访问存储所在的程序包。
  7. 游标链以终止方法结束。 每个 .where(...) 链都以 .toList()(或 .count()、或变更调用)结束;未终止的游标不会执行。
  8. 结果行的本地类型必须全部可选(或保留推断类型)。严格类型注解(包括 id: string)会导致导入失败,因为 $db 结果中的每个字段类型都是 T | undefined,与 OData 和流类型数据源一致。
  9. 如果这是基于 $db 的流类型数据源(由网格或选择器提供数据),请检查 references/flow-db-datasources.md → 上线前检查清单 中的附加清单:getQuery() 工厂、单个 .where 回调、将分页交由 $db 处理、(?i) 正则全文搜索、在顶层调用辅助流、在 getListFlow.inParams 中声明 $orderby / $filter、原样使用 snake_case 的规则、同步镜像四个注册数组、每列的 dynamicFilterControl 与类型匹配、mapRow 保持结果形状一致、同步五处行形状、同步 $orderby / $filter 的 oneOf 字面量、hub 挂载的 inParams 与网格的 inParams 匹配。
  10. 正在进行架构变更? 使用 $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 验证错误,请重新阅读此处的相关部分。

评论

支持 Markdown