全部文档
文档中心DeepModel功能DeepQL常用语法与使用技巧十、进阶

十、进阶

本节简要介绍窗口函数等进阶用法,需要时再结合官方语法或产品文档深入。

在「每行保留明细」的前提下,按某维度分组、排序,在组内计算排名、累计和、组内均值等,使用 over 定义窗口。

常用函数:

函数

说明

win::row_number()

组内行号(1, 2, 3, …)

win::rank()

排名,同值同排名且留空位

win::dense_rank()

密集排名,同值同排名不留空位

win::percent_rank()

组内相对排名(0~1)

聚合也可 over 窗口:如 sum(.字段) over (group by ... order by ...)

方式一:with 中定义窗口,再在 select 中 over win。按订单分组,对订单行按行金额排序并排名、组内金额合计。

Copy
with win as window (
    OrderLine
    group by .order
    order by .qty * .price desc
)
select OrderLine {
    line_no,
    product_name,
    qty,
    price,
    order: { order_no },
    rk := win::rank() over win,
    order_line_total := sum(.qty * .price) over win
}
order by .order.order_no then .qty * .price desc

方式二:在 select 中就地写 over。订单行按所属订单分组、按单价降序排名。

Copy
select OrderLine {
    line_no,
    product_name,
    price,
    order: { order_no },
    rk := win::rank() over (group by .order order by .price desc)
}
order by .order.order_no then .price desc

结果示意:

Copy
[
  { "line_no": 1, "product_name": "商品A", "price": 99.00, "order": { "order_no": "ORD001" }, "rk": 1 },
  { "line_no": 2, "product_name": "商品B", "price": 50.00, "order": { "order_no": "ORD001" }, "rk": 2 }
]

图查询是一种特殊的结果组织方式:最终输出包含两个固定 key——nodes(节点)和 edges(边),用于 UX 图表组件中图结构的数据源渲染。

适用场景:

  • 父子关系图(如组织架构)

  • 指标依赖图(如 KPI 拆解树)

  • 数据血缘图(如 ETL 上下游)

  • 任何可以表达为「节点 + 有向边」的关系

图查询的结果必须是一个对象,包含 nodesedges 两个数组字段:

字段

类型

说明

nodes

数组

节点列表

nodes[]._id

str

节点唯一标识(可以是 id 或业务主键),必填

nodes[].label

str

节点显示名称,必填

edges

数组

边列表

edges[].source

str

边的起点节点 _id必填

edges[].target

str

边的终点节点 _id必填

_idlabel 是图组件的默认字段。_id 必须在 nodes 中唯一,edges 的 source / target 值必须是 nodes 中已有的 _id

查询某个组织及其所有下级,构建组织架构图。这里用 eff_his_parent_org 计算链接表示层级关系(参见「八、层级与树形」中的 L"链接编码" 用法)。

Copy
with
    org := (
        select
            cal::idescendant(<Organization>"60020113", L"eff_his_parent_org")
    ),

select {
    nodes := org {
        _id := .code,
        label := .name,
    },

    -- 父指向子
    edges := (
        source := org.eff_his_parent_org.code,
        target := org.code,
    )
}

解读

  • cal::idescendant(...) 取指定组织及其所有后代,得到节点集合 org

  • nodes:对每个 org 取 code 作为 _idname 作为 label

  • edges:每个 org 的父级链接 eff_his_parent_org 的 code 作为 source,自身 code 作为 target——即「父→子」方向的边。根节点没有父级,自动不产生边。

结果示意:

Copy
[
  {
    "nodes": [
      { "_id": "60020354", "label": "HRBP部门" },
      { "_id": "60020905", "label": "办公室员工招聘" },
      { "_id": "60020238", "label": "人才招聘部门" },
      { "_id": "60020236", "label": "人才发展部门" }
    ],
    "edges": [
      { "source": "60020113", "target": "60020354" },
      { "source": "60020354", "target": "60020905" },
      { "source": "60020113", "target": "60020238" },
      { "source": "60020113", "target": "60020236" }
    ]
  }
]

把订单头和订单行的从属关系可视化为图:

Copy
with
    orders := (select Order filter .status = 'confirmed'),
    lines := orders.<order[is OrderLine],

select {
    nodes := (
        -- 订单头节点
        (select orders {
            _id := <str>.id,
            label := .order_no,
        })
        union
        -- 订单行节点
        (select lines {
            _id := <str>.id,
            label := .product_name,
        })
    ),

    edges := (
        select lines {
            source := <str>.order.id,
            target := <str>.id,
        }
    )
}

当节点来自多种对象类型时,用 union 合并节点集合。每种类型分别定义 _idlabel 即可。

技巧

说明

_id 选择

优先用业务主键(code),无主键时用 <str>.id 转为字符串

边的方向

根据业务语义决定:「父→子」或「子→父」、「上游→下游」等

多类型节点

union 合并不同对象的节点集合

层级遍历

配合 cal::idescendant / cal::iancestor 获取递归节点集

过滤范围

先 filter 限定起始节点,再递归展开,避免全量查询

附加字段

nodes 和 edges 中可以加自定义字段(如 typecolor),图组件会忽略不识别的字段但不报错

用业务主键值或 UUID 直接「转换」为对象实例:<对象类型>'主键值'。语法简洁,适合已知主键时快速取对象。

Copy
-- 用业务主键直接取对象,继续取属性
select (<Order>'ORD001').order_date
Copy
-- 用 UUID 取对象
select (<Order><uuid>'550e8400-e29b-41d4-a716-446655440000') {
    order_no,
    order_date,
    status
}

也可以在 with 中赋值后复用:

Copy
with target := (<Customer>'C001')

select Order {
    order_no,
    order_date
}
filter .customer = target

同样是「按主键取一条对象」,有两种写法:

Copy
-- 写法 A:Cast 转换(简洁)
select (<Order>'ORD001') { order_no, order_date, status }

-- 写法 B:select + filter(安全)
select Order { order_no, order_date, status }
filter .order_no = 'ORD001'

Cast <Order>'主键值'

select … filter

语法

简洁,一行搞定

标准 select 语法

主键不存在时

抛出异常(报错)

返回空集(不报错)

返回基数

确定为单条(One)

可能为零条或多条(Set)

适用场景

确定数据存在时(如关联引用、已知 ID)

不确定是否存在、需要容错时

选择建议:在计算字段、关联引用等确定主键存在的场景用 Cast,更简洁;在用户输入、外部参数等不确定是否存在的场景用 select + filter,更安全。

DeepModel 中的枚举属性底层用 str 类型 + one_of 约束实现——也就是说,属性存储的只是枚举的编码(如 'submitted''rejected'),而枚举的中文描述(如「评估中」、「未采纳」)保存在元数据中,查询时需要额外获取。

Copy
dm::get_enum_info(模块编码, 对象编码, 属性编码)

返回一个 JSON 对象,key 为枚举编码,value 为该枚举值的多语言描述。可以用枚举编码做下标取出对应的描述。

假设 Requirement 对象有枚举属性 req_status(值为 editing / submitted / rejected 等),要查询每条记录的状态描述:

Copy
with enum_mapping := dm::get_enum_info('appzauoyn184', 'Requirement', 'req_status')

select Requirement {
    req_name,
    req_status,
    req_status_desc := <str>enum_mapping[.req_status]['zh-cn']
}

结果示意:

req_name

req_status

req_status_desc

req2

submitted

评估中

req5

rejected

未采纳

req6

editing

需求提案

req7

submitted

评估中

解读

  • dm::get_enum_info(...) 返回的 JSON 结构类似:{"editing": {"zh-cn": "需求提案", "en": "Proposal"}, "submitted": {"zh-cn": "评估中", ...}, ...}

  • enum_mapping[.req_status] 用当前记录的状态编码做下标,取出该枚举值的描述对象

  • ['zh-cn'] 再取中文描述

  • 最外层 <str> 将 JSON 值转为字符串

注意:dm::get_enum_info 的第一个参数是模块编码(DeepModel 的应用模块标识),不是对象名。

DeepModel 中的文件属性底层用 multi json 实现——本质是 JSON 类型,但可以为多值(一个字段存多个文件)。每个文件是一个 JSON 对象,包含 fileNamefileSizefileUrl 等字段。

Copy
select Order {
    order_no,
    -- attachment 是 multi json,每个元素是一个文件的 JSON 对象
    attachment_names := .attachment['fileName']
}

如果一个订单附了 2 个文件,.attachment 就是 2 个 JSON 对象的集合,.attachment['fileName'] 得到 2 个文件名。

Copy
select Order {
    order_no,
    -- 收集为数组
    file_list := array_agg(<str>.attachment['fileName']),
    -- 拼接为一个字符串
    file_names := to_str(array_agg(<str>.attachment['fileName']), '、')
}

结果示意:

Copy
[
  {
    "order_no": "ORD001",
    "file_list": ["合同.pdf", "报价单.xlsx"],
    "file_names": "合同.pdf、报价单.xlsx"
  }
]

要点

说明

底层类型

multi json,即多值 JSON

单文件 vs 多文件

都是 multi,单文件时集合只有 1 个元素

常用字段

fileName(文件名)、fileSize(大小)、fileUrl(地址)等

取值方式

.attachment['fileName'],下标取 JSON 字段

转字符串

<str> 强转,JSON 取出的值默认是 json 类型

在实际开发中,经常需要取对象或关联对象的业务主键来做过滤、比较、输出。但不同对象的主键字段名各不相同——有的叫 code,有的叫 order_no,有的叫 xx_id,每次都要翻找对象结构确认字段名,非常繁琐。

为了解决这个高频痛点,DeepQL 扩展了一个特殊语法:.__bk__(business key),代表对象的业务主键。执行时会自动替换为该对象实际的主键字段名。

Copy
-- .__bk__ 会自动替换为 Order 的业务主键字段(如 order_no)
select Order {
    __bk__,
    order_date,
    status
}

等价于:

Copy
select Order {
    order_no,
    order_date,
    status
}

不需要记住主键叫什么,直接用 .__bk__ 过滤:

Copy
select Order { * }
filter .__bk__ = 'ORD001'

取关联对象的主键也一样——通过链接「点」到 .__bk__

Copy
select OrderLine {
    product_name,
    qty,
    price,
    order_pk    := .order.__bk__,
    customer_pk := .order.customer.__bk__
}

执行时 .order.__bk__ 替换为 .order.order_no.order.customer.__bk__ 替换为 .order.customer.code——你不需要知道 Order 的主键是 order_no 还是 Customer 的主键是 code

场景

写法

输出当前对象主键

pk := .__bk__

按主键过滤

filter .__bk__ = 'xxx'

取链接对象的主键

link_pk := .link.__bk__

多级链接取主键

.order.customer.__bk__

分组时取主键做维度

.key.customer.__bk__(需先在 elements 上引用)

__bk__ 是纯语法糖——执行前由引擎替换为实际字段名,不影响性能。好处是:换了主键字段名也不用改查询语句,降低维护成本。

  • assert(条件, message?):条件为 false 时报错,可带自定义 message。

  • win::mode(集合):取集合中出现次数最多的值(众数),多个时随机取一。

  • ROLLUP / CUBE:在 group 的 by 中做多维度汇总,见「六、分组与汇总」中的详细说明。

需要完整语法或更多函数时,可查阅 DeepModel 参考文档。

回到顶部

咨询热线

400-821-9199

我们使用 ChatGPT,基于文档中心的内容以及对话上下文回答您的问题。

ctrl+Enter to send