撰写和调试 CQL
CQL 是 Croct 的类似英语的查询语言。相同的表达式语法驱动着受众定义、来自 SDK 和 HTTP API 的实时评估以及动态内容值。此处所有内容均来自 docs.croct.com;当未涵盖某部分时,请查阅实时文档(本文件底部)而非自行猜测。
工作流程
- 确定查询运行的位置:受众(必须为布尔值)、
evaluate/API(任意 JSON 值,最多 500 个字符)或动态值(结果必须符合属性类型,否则使用default)。 - 选择数据源:当前上下文(
page、cart、location、campaign、device)、存储的用户画像(user's ...、user's stats)、会话(session's ...)或历史记录(user has ...事件表达式,Scale 计划)。 - 检查数据是否已收集:任何标记为 需要集成 的内容(用户画像属性、购物车、兴趣、目标、登录状态)在应用发送之前永远不会匹配。
- 使用下面的速查表编写查询;在 context-and-data-types.md 中查找确切的属性。
- 运行陷阱检查清单,然后使用
croct.evaluate()或仪表盘预估器进行测试。
速查表
// 自然、技术性和混合风格可互换
user's score is greater than 90
user.score > 90
user's score > 90
| 需求 | 语法 |
|---|---|
| 属性 | user's address's city、user's stats' orders、property city of user's address、user.address.city |
| 特殊键 / 索引 | page.query['coupon-code']、list[0]、(user's interests)[0] |
| 选择器 | cart.items[1st]、[:first]、[:last(2)]、items[*].price、data[...].price |
| 字面量 | null、true、42、3.14(十进制)、3.14f、51%、"str"、'raw'、~/re/i、[1, 2]、["k": "v"] |
| 日期 | 无字面量:"2026-01-01" as date、"2026-01-01T12:00" as datetime;today + 1 day |
| 逻辑 | and or not(&& ` |
| 比较 | == != > >= < <=;1 < x < 3;is equal to X ignoring case |
| 字面量简写 | X is "value"、X is not 0(右侧必须是字面量) |
| 字符串 | starts with、ends with、matches "sub" / matches ~/re/i(区分大小写)、is like "sub"(不区分大小写) |
| 集合 | contains X、X is in [..]、contains all of [..]、contains either [..]、contains neither [..] |
| 存在性 | is defined / is known、is empty |
| 用户 / 会话 | user is returning、user is anonymous、user is identified、session is starting |
| 日期 | is before、is after、is at "12:00"、is between A and B [exclusive]、today's weekday is "monday" |
| 量词 | some x in C satisfies P、every、no、not every、at least 2 of any [distinct] x in C satisfies P |
| 宏 | count of、sum of、average of、min of、max of、first of、last of、keys of、length of、number of days between A and B、this month |
| 修饰符 | as number、rounded [down]、in title case、sorted descending、without duplicates、replacing "a" with "b"、today at "12:00" |
| 后备值 | user's name or else "Anonymous"(??) |
| 条件 | "A" if cond otherwise "B"、cond ? "A" : "B" |
| 变量 | let orders = user's stats' orders; orders >= 5 |
| 事件(Scale) | user has <event> [with prop value] [at least N times] [in last 7 days] |
| 注释 | // line、# line、/* block */ |
关键字和标识符不区分大小写;字符串字面量区分大小写。
上下文速览
| 变量 | 常用属性 |
|---|---|
user | id、firstName、email、age、company、jobTitle、interests、activities、stats' sessions、stats' orders、任意自定义属性 |
session | start、duration、landingPage、referrer、stats' pageviews、stats' orders、audiences、experiences、experiments、自定义属性 |
page(网页) | title、url、path、query、anchor、referrer |
location | continentCode、countryCode、countryName、stateCode/regionCode(state's code 的别名)、stateName、cityName、timeZone、tags |
device / browser | category(desktop、tablet、mobile、bot 等)、vendor、name、os;browser's name、type |
campaign | name、source、medium、content、term(UTM) |
cart | stage(browsing、shopping、checkout)、items、total、subtotal、coupon、discount |
context | timeZone、评估时传入的属性 |
now、today、yesterday、tomorrow | weekday、day、month、year、hour... |
最有用的模式
user is returning
user is not returning
session is starting
device's category is "mobile"
device's category is not "bot"
location's countryCode is "US"
location's state's code is in ["NY", "CA"]
location's cityName is "New York" and today's weekday is "monday"
campaign's source is "google" and campaign's medium is "cpc"
campaign's name matches "sale"
page's path is in ["/pricing", "/plans"]
page's query's newsletter is "daily-news"
page.query['coupon-code'] is defined
session's referrer matches "google"
session's landingPage starts with "https://example.com/promo"
session's duration > 5 minutes and session's stats' orders is 0
user's interests contains either ["marketing", "sales"]
user's age is between 18 and 30
user's stats' orders is 0
user's plan is "premium"
cart's stage is "checkout" and cart's total is greater than 100
some item in cart's items satisfies item's brand is "Apple"
(no item in cart's items satisfies item's name is like "smartphone") and user is returning
user has abandoned a cart at least 2 times
user has viewed a product with category "Shoes" in last 7 days
user has completed goal "demo-requested" last session
user's firstName or else "there"
更多按用例(活动、位置、技术、会话、用户画像、购物车、订单、值查询)划分的内容:references/recipes.md。
从代码进行评估
import croct from '@croct/plug';
const isReturning = await croct.evaluate<boolean>('user is returning').catch(() => false);
const inList = await croct.evaluate<boolean>(
"context's countries include location's countryName",
{attributes: {countries: ['United States', 'Canada']}}, // 读取为 `context's countries`
);
Next.js 服务端:使用 @croct/plug-next/server 中的 await evaluate(query) 或 await cql`user's persona is in ${roles}` 。React:useEvaluation(query)。HTTP:POST https://api.croct.io/client/web/evaluate,请求体为 {"query": "..."}。详细信息、属性限制、动态值和预估:references/evaluation.md。
陷阱与调试
| 症状 | 可能原因 | 修复 |
|---|---|---|
x is y 语法错误 | is 简写要求右侧是字面量 | 使用 x is equal to y 或 x == y |
location city is "Berlin" 语法错误 | 变量和属性之间缺少访问器 | 使用 location's cityName、property cityName of location 或 location.cityName |
比较 "1" > 0 或 session's start > "2026-01-01" 报错 | 关系运算符从不转换类型 | 强制转换:"1" as number > 0、... > "2026-01-01" as datetime,或使用 is after |
| 位置/代码从不匹配 | 大小写错误:代码是大写的("US"、"NA"、"CA"),且字符串区分大小写 | 使用大写、is like、is equal to ... ignoring case 或 ~/.../i |
matches "Sale" 未匹配到 "sale" | matches、starts with、ends with 区分大小写 | 使用 is like "sale" 或 matches ~/sale/i |
| 量词吞没了后续条件 | 量词谓词扩展到末尾 | 将整个量词括在圆括号中 |
a or b and c 行为奇怪 | and 绑定比 or 更紧密 | 添加括号 |
| 条件结果被错误地乘以其他值 | 条件是贪婪的 | 使用 (A if c otherwise B) * price |
| 用户画像/购物车/兴趣条件从不成立 | 需要集成的数据未发送 | 跟踪事件或设置属性(参见 croct-track-events 技能) |
X is greater than 30 和 X is not greater than 30 都为 false | 未定义的值:布尔值容错机制返回 false | 使用 X is defined and ... 或 X or else 0 |
user is identified 始终为 false | 登录/登出事件未跟踪 | 跟踪它们;否则用户始终是匿名的 |
cart.items[2nd]、first of、average of、min of 报错 | 缺少元素或集合为空 | 附加 or else null / or else 0 |
contains none of [] vs contains neither [] | 右侧为空:none 为 false,neither 为 true | 对于“无重叠”使用 contains neither |
混合 's 与括号报错 | 所有格/物主代词加上括号需要括号 | 使用 (cart's items)[1st] 或点表示法 cart.items[*] |
| 事件表达式不可用 | 此功能属于 Scale 计划 | 尽量使用用户画像/会话统计数据(user's stats' orders) |
| “预估不可用” | 使用了 page、当前上下文或 now/today/yesterday | 属于预期行为;受众在运行时仍有效 |
| 范围错误 | .. 范围最多 30 个项目 | 缩小范围 |
| 变量/参数名错误 | 变量不能被重新定义;名称必须是标识符 | 重命名;避免使用内置名称(user、page)和通用词汇 |
做法:
- 在供非开发者共享的受众中,优先使用明确的
is equal to、is greater than;当技术性风格读起来更顺畅时使用该风格。 - 使用
is defined或or else保护可选数据。 - 在
and链中,将开销低的条件放在前面(短路求值会跳过其余部分)。 - 使用
contains either/contains neither,而不是对contains all/contains none取反。
禁忌:
- 不要发明测试、变量或事件短语。例如
user is new或user has made purchase虽然出现在文档正文中,但不是文档化的构造;请使用user is not returning和user's stats' orders is not 0(或user has placed an order)。 - 不要将
location's countryName与代码比较;应使用countryCode来比较"US"。 - 不要在算术运算中依赖隐式日期转换:请使用
"2021-01-01" as date + 1 day。
参考
| 文件 | 何时阅读 |
|---|---|
| references/syntax.md | 词法规则、let、函数、字面量、访问器和选择器、优先级表、条件表达式、持续时间 |
| references/context-and-data-types.md | 每个变量和属性、可用性标志、枚举(设备类别、购物车阶段、大洲、位置标签)、转换 |
| references/expressions.md | 所有测试、运算符、容错机制、量词、宏、修饰符 |
| references/events.md | 事件表达式、条件运算符、计数量词、时间窗口、跟踪要求 |
| references/evaluation.md | SDK evaluate / cql / hooks、评估 API、上下文属性、动态值、受众预估 |
| references/recipes.md | 按用例划分的现成受众和值查询 |
相关技能:croct-integrate-sdk(SDK 设置)、croct-track-events(发送查询所需的事件和属性)、croct-use-api(HTTP API)、croct-model-content(内容定义中的动态值)、croct-personalize-and-test(使用受众的体验和实验)、croct-use-croct(概念和指导)。