CDP的核心能力就是通过用户选定的筛选规则帮助用户选出数据库中满足该规则的用户。为了实现这一目标,CDP指定制定了一套DSL(领域特定语言)来描述规则用户选定的筛选规则。如果直接使用CDP的前端界面,用户并不需要了解和学习这套DSL,前端会完成从可视化界面配置到DSL的转换工作。
然而,随着用户需求的不断变化,只从CDP用户界面进行规则配置已经不能满足所有用户。因此,CDP决定将圈选功能以openApi的形式开放出来,以满足用户的定制化需求。由于openApi给用户使用的是简化版的dsl,使用户能快速创建分群,所以通过该方式创建的分群是没办法在界面上回显的。
本文档介绍DSL的编写规范,需要调用openApi的用户需要按照文档要求编写对应的DSL,传给CDP进行圈选。
DSL最终以json的形式呈现和传递,基本格式如下:
filters字段是一个数组,里面可以嵌套基本格式的filter,也可以包含下文具体的查询如标签,分群,属性等。filters里至少应该包含一个具体的查询,否则多层基本格式的filter嵌套没有意义
operator字段表示filters之间的关系,支持And或Or两种
{ "filters": [], "operator": "And" }
按照标签的数据类型,可以将标签查询DSL分为以下几类
字段名称 | 值类型 | 是否必传 | 描述 |
---|---|---|---|
tagId | Int | 是 | 标签id |
value | List[String] | 是 | 对应的值 |
operator | String | 是 | 对应的操作符,此处支持传 hasAny(数组中包含选择的任何一个值), hasAll(数组中包含选择的所有值), arrayNot(数组中不包含选择的任何值) |
举例,下面是一个字符串数组标签,操作符是hasAny
{ "tagId": 104, "operator": "hasAny", "value": [ "足球", "篮球,足球,乒乓球", "篮球" ] }
字段名称 | 值类型 | 是否必传 | 描述 |
---|---|---|---|
tagId | Int | 是 | 标签id |
value | Decimal | 是 | 对应的值,可以是int,double,float |
operator | String | 是 | 对应的操作符,支持=, >, >=, <, <=, 表示数组中有任意数值满足即可 |
{ "tagId": 38, "operator": "=", "value": 2 }
字段名称 | 值类型 | 是否必传 | 描述 |
---|---|---|---|
tagId | Int | 是 | 标签id |
value | Decimal | 是 | 对应的值,可以是int,double,float |
operator | String | 是 | 对应的操作符,支持=, >, >=, <, <=, != |
{ "tagId": 218, "operator": "!=", "value": 1123 }
字段名称 | 值类型 | 是否必传 | 描述 |
---|---|---|---|
tagId | Int | 是 | 标签id |
value | List[String] | 是 | 对应的值列表 |
operator | String | 是 | 对应的操作符,此处支持传 in(包含), notIn(不包含),globalNotIn(当前系统可用id减去过滤条件对应的id) |
{ "tagId": 17, "operator": "in", "value": [ "山东", "郑州", "青岛" ] }
字段名称 | 值类型 | 是否必传 | 描述 |
---|---|---|---|
tagId | Int | 是 | 标签id |
value | 是 | 对应的值 | |
operator | String | 是 | 对应的操作符,in(包含) |
{ "tagId": 108, "operator": "in", "value": { "startTime": "2023-08-02", "endTime": "2023-08-04", "type": "Range" } }
上述各种查询json中,都涉及到了一个可选字段时间范围*PeriodFilter。*这个字段用来指定定义的查询逻辑的时间条件。有以下几类用法
表示距今天往前一段时间或者叫过去多久之内
字段名称 | 值类型 | 是否必传 | 描述 |
---|---|---|---|
interval | String | 是 | 时间段的单位,可以传 Day, Month, Year |
last | Int | 是 | 时间段的长度 |
todayIncluded | boolean | 是 | 是否包含今日 |
type | String | 是 | 时间筛选的类型,此处固定传Last |
{ "last": 1, "interval": "Day", "type": "Last", "todayIncluded": false }
字段名称 | 值类型 | 是否必传 | 描述 |
---|---|---|---|
startTime | String | 是 | 时间范围的开始时间 |
endTime | String | 是 | 时间范围的结束时间 |
type | String | 否 | 类型,此处固定传Range |
{ "startTime": "2023-08-01", "endTime": "2023-08-06", "type": "Range" }
分群查询结构如下:
segId表示分群id
not字段表示筛选人群不属于(true)或属于(false)这个人群包
{ "segId": 1000022, "not": false }
按照属性的数据类型,可以将标签查询DSL分为以下几类
字段名称 | 值类型 | 是否必传 | 描述 |
---|---|---|---|
dataSourceId | Int | 是 | 该属性来源的数据源id |
field | String | 是 | 在该数据源表中,这个属性对应的字段名称 |
value | Int | 是 | 属性筛选对应的值 |
operator | String | 是 | 对应的操作符,支持=, >, >=, <, <=表示数组中有任意数值满足即可 |
columnId | Int | 否 | 属性的列id |
{ "operator": ">=", "dataSourceId": 33, "columnId": 405, "field": "nianling", "value": 1 }
字段名称 | 值类型 | 是否必传 | 描述 |
---|---|---|---|
dataSourceId | Int | 是 | 该属性来源的数据源id |
field | String | 是 | 在该数据源表中,这个属性对应的字段名称 |
value | List[String] | 否 | 属性筛选对应的值 |
operator | String | 是 | 对应的操作符,可以传:hasAny(数组中包含选择的任何一个值), hasAll(数组中包含选择的所有值), arrayNot(不包含) |
originType | String | 否 | 原始类型 |
columnId | Int | 否 | 属性的列id |
{ "operator": "hasAll", "dataSourceId": 27, "columnId": 289, "field": "array_string", "value": [ "足球", "篮球", "乒乓球" ] }
字段名称 | 值类型 | 是否必传 | 描述 |
---|---|---|---|
dataSourceId | Int | 是 | 该属性来源的数据源id |
field | String | 是 | 在该数据源表中,这个属性对应的字段名称 |
value | List[String] | 是 | 属性筛选对应的值 |
operator | String | 是 | 对应的操作符,此处支持传 in(包含), notIn(不包含) |
columnId | Int | 是 | 属性的列id |
{ "operator": "in", "dataSourceId": 27, "columnId": 313, "field": "name", "value": [ "张9929", "张9928" ] }
字段名称 | 值类型 | 是否必传 | 描述 |
---|---|---|---|
dataSourceId | Int | 是 | 该属性来源的数据源id |
field | String | 是 | 在该数据源表中,这个属性对应的字段名称 |
value | bigint | 是 | 属性筛选对应的值 |
operator | String | 是 | 对应的操作符,可以传:=, >, >=, <, <=, != |
columnId | Int | 否 | 属性的列id |
{ "operator": ">=", "dataSourceId": 33, "columnId": 405, "field": "nianling", "value": 1 }
字段名称 | 值类型 | 是否必传 | 描述 |
---|---|---|---|
dataSourceId | Int | 是 | 该属性来源的数据源id |
field | String | 是 | 在该数据源表中,这个属性对应的字段名称 |
value | 是 | 属性筛选对应的值 | |
operator | String | 是 | 对应的操作符,可以传:in(包含) |
columnId | Int | 否 | 属性的列id |
{ "operator": "in", "dataSourceId": 27, "columnId": 307, "field": "birthday", "value": { "startTime": "2023-08-01", "endTime": "2023-08-05", "type": "Range" } }
行为支撑筛选出行为表中某段时间做过某件事若干次的用户,具体dsl结构如下:
字段名称 | 值类型 | 是否必传 | 描述 |
---|---|---|---|
operator | String | 是 | 表示筛选做过(Done)或未做过(NotDone) |
aggregate | 否 | 数据聚合的方式(只有Done才需要传) | |
eventDatasourceId | Int | 是 | 行为数据源id |
eventName | String | 是 | 行为名称 |
period | 是 | 行为发生的时间范围 | |
eventId | Int | 是 | 行为id |
{ "operator": "Done", "aggregate": { "condition": { "operator": ">", "value": 1 }, "method": "Count" }, "eventDatasourceId": 21, "eventName": "bav2b_click", "period": { "startTime": "2023-10-01", "endTime": "2023-10-08", "type": "Range" }, "eventId": 228 }
{ "operator": "NotDone", "eventDatasourceId": 21, "eventName": "bav2b_click", "period": { "startTime": "2023-10-01", "endTime": "2023-10-08", "type": "Range" }, "eventId": 228 }
字段名称 | 值类型 | 是否必传 | 描述 |
---|---|---|---|
condition | 是 | 对聚合结果的筛选条件 | |
method | String | 否 | 数据聚合的方式(只有Done才需要传,目前只支持Count) |
字段名称 | 值类型 | 是否必传 | 描述 |
---|---|---|---|
oprator | String | 是 | 操作符,支持=, >, >=, <, <= |
value | Int | 是 | 筛选条件的数值 |
{ "dataSourceId": 27, "paramCondition": { "logic": "And", "paramFilters": [ { "paramName": "money", "value": 1, "operator": "in", "columnId": 321 } ] }, "period": { "dateWithTime": false, "interval": "Day", "last": 1, "todayIncluded": false, "type": "Last" } }
字段名称 | 值类型 | 是否必传 | 描述 |
---|---|---|---|
dataSourceId | int | 是 | 明细数据源id |
paramCondition | 是 | 参数过滤条件 | |
period | 是 | 明细数据的时间范围 |
字段名称 | 值类型 | 是否必传 | 描述 |
---|---|---|---|
logic | String | 是 | 筛选条件的关联关系,And或Or |
paramFilters | 是 | 筛选条件列表 |
字段名称 | 值类型 | 是否必传 | 描述 |
---|---|---|---|
paramName | int | 是 | 明细数据字段名称 |
value | int,string | 是 | 筛选对应的值 |
operator | string | 是 | 筛选执行的逻辑,支持in,notIn(不论是String还是int类型的数据,此处都只支持这两种操作符) |
colunmId | int | 是 | 该筛选字段在明细表中的列id |
给出如下一个例子,表示筛选出同时满足标签,分群和属性查询的人
{ "filters": [ { "tagId": 356, "operator": "in", "value": [ "兴趣" ] }, { "operator": "in", "dataSourceId": 24, "columnId": 353, "field": "city", "value": [ "黄山", "青岛" ] }, { "segId": 1000022, "not": false, "operator": "SegFilter" } ], "operator": "And" }
下面这个例子嵌套了两层,表示满足标签查询或满足分群和属性的人
{ "filters": [ { "tagId": 356, "operator": "in", "value": [ "兴趣" ] }, { "filters": [ { "operator": "in", "dataSourceId": 24, "columnId": 353, "field": "city", "value": [ "黄山", "青岛" ] }, { "segId": 1000022, "not": false, "operator": "SegFilter" } ], "operator": "And" } ], "operator": "Or" }
三层嵌套表示,但表达的逻辑和一层一致
{ "filters": [ { "tagId": 356, "operator": "in", "value": [ "兴趣" ] }, { "filters": [ { "operator": "in", "dataSourceId": 24, "columnId": 353, "field": "city", "value": [ "黄山", "青岛" ] }, { "filters": [ { "segId": 1000022, "not": false, "operator": "SegFilter" } ], "operator": "And" } ], "operator": "And" } ], "operator": "And" }
为保证圈选执行效率,不建议编写过于复杂,包含过多条件的dsl,具体推荐如下配置,如果超出可能引发clickhouse集群出现问题