1. 衡石 HQL
衡石系统中自定义的计算过程描述语言为 Hengshi SENSE Query Language (HQL) ,用于衡石系统的前后端交互、API 数据请求中。
1.1. 语法规定
使用统一的 JSON 来描述各个计算过程. HQL 由多层调用的 HE (Hengshi Expression) 组成。下面是单个 HE 的语法结构。
HE 节点名 | 类型 | 是否必须 | 描述 |
---|---|---|---|
uid | STRING | 否 | 本节点的全局唯一标识符 |
kind | STRING | 是 | 节点类型,见HE状态说明 |
op | OBJECT | 是 | 不同的 HE 类型,对应不同的节点定义,见HE状态说明 |
args | HE 数组 | 否 | 当 kind 为 functions的时候,此项为必须的 |
type | STRING | 否 | 本节点输出的数据类型,可以是 string, number, integer, date, time, json, bool 中的一种 |
dataset | OBJECT | 否 | 本节点所在的数据集 id, 一般在 kind 为 field 的时候会指定字段所属的数据集 |
direction | STRING | 否 | 可选值为 asc 或者 desc,本节点用户排序的时候可以指定该字段,不指定的话,默认为 asc |
window | OBJECT | 否 | 指定本节点计算的窗口,只有 kind 为 function 时,该字段的值才有效,见HEWindow窗口说明 |
filter | HE 数组 | 否 | 计算的过滤条件,一般用户聚合计算的过滤 |
1.1.1. HE 节点类型说明
节点类型 | 节点描述 | 类型对应的 op |
---|---|---|
field | 描述字段 | 字段名 |
function | 描述函数 | 函数名 |
reference | 描述引用字段,一般用户图表的定义中 | 引用的名字 |
constant | 描述常量 | 常量的值 |
formula | 描述自定义的 HE 表达式 | 表达式的定义 |
array | 描述数组 | 数组 |
attr | 描述用户属性 | 用户属性的名字 |
param | 描述应用参数 | 应用参数的名字 |
dataset | 描述数据集 | 数据集的 id |
1.1.2. HE Window 窗口说明
窗口关键字 | 类型 | 描述 |
---|---|---|
partition | HE 数组 | 计算过程的划分依据,对应 sql 中的 partition by 关键字 |
order | HE 数组 | 计算过程的排序依据,对应 sql 中的 order by 关键字 |
1.1.3. 示例1:描述字段
kind 的值是 field, op 的值是字段名, dataset 的值是字段所在的数据集 id。下面的例子描述的是来自数据集 1 的 "test" 字段
{
"kind": "field",
"op": "test",
"dataset": 1
}
1.1.4. 示例2:描述常量
kind 的值是 constant, op 的值是常量值, type 的值是常量的类型。type 为非必需的。 下面的例子描述的是字符串类型常量值 'test'
{
"kind": "constant",
"op": "'test'"
}
下面的例子描述的是时间类型的常量值 '2000-01-01'
{
"kind": "constant",
"op": "'2000-01-01'",
"type": "date"
}
1.1.5. 示例3:描述函数
kind 的值是 function, op 的值是函数名或者操作符, args 是函数参数数组。 下面的例子是描述一个加法操作,数据集 2 的字段 salesNum + 2000。
{
"kind": "function",
"op": "+",
"args": [
{
"kind": "field",
"op": "salesNum",
"dataset": 2
},
{
"kind": "constant",
"op": 2000
}
]
}
下面的例子是描述聚合函数 sum 的使用,计算 数据集 2 的 salesNum 字段列的求和。
{
"kind": "function",
"op": "sum",
"args": [
{
"kind": "field",
"op": "salesNum",
"dataset": 2
}
]
}
下面的例子是描述窗口函数 avg 的使用,以 数据集 2 的 location 字段为分组,计算 数据集 2 的 salesNum 字段列的平均值。
{
"kind": "function",
"op": "avg",
"args": [
{
"kind": "field",
"op": "salesNum",
"dataset": 2
}
],
"window": {
"partition": [
{
"kind": "field",
"op": "location",
"dataset": 2
}
]
}
}
下面的例子是描述窗口函数 rank 的使用,根据数据集 2 的 saleNum 字段求和的降序对数据集 2 的 location 字段排名。
{
"kind": "function",
"op": "avg",
"args": [
{
"kind": "field",
"op": "location",
"dataset": 2
}
],
"window": {
"order": [
{
"kind": "function",
"op": "sum",
"args": [{
"kind": "field",
"op": "saleNum",
"dataset": 2
}],
"direction": "desc"
}
]
}
}
下面的例子是描述带 filter 条件的聚合函数 sum 的使用,计算所有 location 为"北京"或者"上海"的条件下, 数据集 2 的 salesNum 字段列的求和。
{
"kind": "function",
"op": "sum",
"args": [
{
"kind": "field",
"op": "salesNum",
"dataset": 2
}
],
"filter": [
{
"kind": "function",
"op": "in",
"args":[
{
"kind": "field",
"op": "location",
"dataset": 2
},
{
"kind": "constant",
"op": ["北京", "上海"]
}
]
}
]
}
1.1.6. 示例4:描述表达式
kind 的值是 formula, op 的值是表达式。函数表达式的使用请参照函数文档。 HE 函数的描述都可以转成HE 表达式,例子中也对应了上面函数描述的例子。下面是HE 以及对应 表达式表述的定义。
用途 | HE | 表达式 |
---|---|---|
字段 | {“kind”: “field”, “op”:”字段名”} | {字段名} |
数据集的字段 | {“kind”: “field”, “op”:”字段名”, "dataset":"数据集id"} | .{字段名} |
用户属性 | {“kind”: “attr”, “op”:”用户属性名”} | {{$用户属性}} |
应用参数 | {“kind”: “param”, “op”:”应用参数名”} | {{%应用参数}} |
函数 | {“kind”: “function”, “op”:”函数名”, "args":["arg1", "arg2"]} | 函数名(arg1, arg2) |
窗口函数 | {“kind”: “function”, “op”:”函数名”, "args":["arg1", "arg2"],"window":{"partition":["分组"],"order":["排序"]}} | 函数名(arg1, arg2) over(partition by "分组" order by "排序") |
带过滤函数 | {“kind”: “function”, “op”:”函数名”, "args":["arg1", "arg2"],"filter":["filter1","filter2"]} | 函数名(arg1, arg2) filter(where "filter1" and "filter2") |
下面的例子描述的是连接数据集 1 的 firstname 列 和 数据集 1 的 lastname 列。
{
"kind": "formula",
"op": "concat({{1}}.{firtname}, {{1}}.{lastname})"
}
下面的例子描述的是比较 数据集 1 的 salesNum 字段 和 应用参数的基准值。
{
"kind": "formula",
"op": "{{1}}.{salesNum} > {{%基准值}}"
}
下面的例子是描述窗口函数 avg 的使用,以 数据集 2 的 location 字段为分组,计算 数据集 2 的 salesNum 字段列的平均值。
{
"kind": "formula",
"op": "avg({{2}}.{salesNum}) over(partition by {{2}}.{location})"
}
下面的例子是描述窗口函数 rank 的使用,根据数据集 2 的 saleNum 字段求和的降序对数据集 2 的 location 字段排名。
{
"kind": "formula",
"op": "rank({{2}}.{location}) over(order by sum({{2}}.{salesNum}) desc)"
}
下面的例子是描述带 filter 条件的聚合函数 sum 的使用,计算所有 location 为"北京"或者"上海"的条件下, 数据集 2 的 salesNum 字段列的求和。
{
"kind": "formula",
"op": "sum({{2}}.{salesNum}) filter(where in({{2}}.{location}, ['北京','上海']))"
}
1.2. 使用场景1: 图表的定义
在衡石系统中,定义一个图表由两部分组成,一部分是 HE 的定义,用于获取数据;另一部分是格式的定义,用于展示。这里我们只关注用于获取数据的 HE 部分。 下面是一个简单的图表示例,HE 的计算组合在一起,描述了业务:以 region_name 分组,计算组内 region_id 的求和,结果按照 region_name 的升序展示。
{
"options": {
"axes": [
{
"args": [
{
"op": "region_name",
"kind": "field",
"dataset": 7
}
],
"op": "group",
"uid": "u_edbee8adba68e26a_2",
"kind": "function"
},
{
"op": "sum",
"kind": "function",
"args": [
{
"op": "region_id",
"kind": "field",
"dataset": 7
}
],
"uid": "u_9f886f9cb7bdf1d2_2"
}
],
"sort": [
{
"op": "u_edbee8adba68e26a_2",
"kind": "reference",
"direction": "asc"
}
],
"limit": 1000
}
}
1.3. 使用场景2: url 过滤
访问衡石系统的共享链接时,可以在共享链接后面添加 url 过滤条件,用于数据的权限控制。这个过滤条件也是由 HE 组成的。 下面这个过滤表示过滤 location 为北京的数据,它会作用于所有的图表上。
where=[{"kind":"formula","op":"{location}='北京"}]
下面这个过滤表示过滤数据集 1 的 location 字段为北京的数据,它会作用于所有与数据集1 由关联的图表上。
where=[{"kind":"formula","op":"{1}.{location}='北京"}]
1.4. 使用场景3: 数据集的新增字段和指标
在衡石系统的数据集里新建字段、新建指标都是通过写 HE 表达式实现的。