ObjectQL
/api/v1/data/*、视图、报表和 AI 工具使用的结构化查询格式。
ObjectQL
ObjectQL 是数据引擎消费的 JSON 查询格式。它是 /api/v1/data/:object 接受的内容、视图编译的目标、报表的序列化形式,以及 AI query_data 工具产生的内容。
两种形态:
- 简单列表 —— 将
where、orderBy、limit、expand等作为查询字符串参数传给GET /api/v1/data/:object。 - 高级查询 —— 将完整查询体 POST 到
/api/v1/data/:object/query(groupBy和aggregations必需)。
查询形状
{
object: string, // 必填 —— 目标对象名
fields?: string[], // 投影(默认:所有可见字段)
where?: FilterCondition, // 见 Filters
orderBy?: SortNode[], // [{ field, order: 'asc' | 'desc' }]
limit?: number, // 页大小
offset?: number, // offset 分页
cursor?: string, // cursor 分页(优先)
expand?: string[], // 批量解析关系字段
joins?: JoinNode[], // 显式 join(inner | left | right | full)
groupBy?: (string | GroupByNode)[],
aggregations?: AggregationNode[], // sum/avg/count/min/max/...
having?: FilterCondition, // 聚合后过滤
distinct?: boolean
}Schema 源码:packages/spec/src/data/query.zod.ts。
过滤器
where 是条件树。字段操作符以 $ 为前缀;逻辑组合器($and、$or、$not)位于顶层。
比较
| 操作符 | 示例 | 说明 |
|---|---|---|
$eq | { status: { $eq: "open" } } | 相等 |
$ne | { priority: { $ne: "low" } } | 不等 |
$gt、$gte、$lt、$lte | { amount: { $gte: 1000 } } | 比较 |
所有比较操作符也接受字段引用用于跨字段比较:{ end_at: { $gt: { $field: "start_at" } } }。
集合与区间
| 操作符 | 示例 |
|---|---|
$in | { status: { $in: ["new","open"] } } |
$nin | { owner_id: { $nin: ["u_1","u_2"] } } |
$between | { created_at: { $between: ["2026-01-01","2026-02-01"] } } |
字符串
| 操作符 | 示例 | 说明 |
|---|---|---|
$contains | { subject: { $contains: "refund" } } | 大小写不敏感 |
$notContains | { notes: { $notContains: "test" } } | |
$startsWith | { email: { $startsWith: "@" } } | |
$endsWith | { email: { $endsWith: "@acme.com" } } |
Null 与存在性
| 操作符 | 示例 |
|---|---|
$null | { closed_at: { $null: true } } |
$exists | { external_id: { $exists: false } } |
逻辑组合器
{
"$and": [
{ "status": { "$eq": "open" } },
{
"$or": [
{ "priority": { "$in": ["high", "urgent"] } },
{ "due_at": { "$lt": { "$cel": "now()" } } }
]
}
]
}CEL 表达式可通过 { "$cel": "..." } 嵌入 —— 对服务器在查询时求值的"相对当前时间"过滤器有用。
排序
"orderBy": [
{ "field": "priority", "order": "desc" },
{ "field": "created_at", "order": "asc" }
]查询字符串简写:?orderBy=-priority,created_at。
分页
Cursor(推荐)。 响应中包含 nextCursor;下次请求时作为 cursor 传回。
GET /api/v1/data/ticket?limit=50&orderBy=-created_at
→ { items: [...], nextCursor: "eyJ..." }
GET /api/v1/data/ticket?limit=50&cursor=eyJ...Offset。 更简单但在大表上劣化。
GET /api/v1/data/ticket?limit=50&offset=200运行时通过 ObjectSpec.maxPageSize 限制每个对象的 limit(默认 200)。
关系 —— expand
expand 批量解析外键以避免 N+1。
{
"object": "support_ticket",
"expand": ["assignee", "customer.account"],
"limit": 20
}返回的每条 ticket 中 assignee 和 customer.account 被物化为嵌套对象,而非仅 ID。
Join
用于元数据图之外的临时 join:
"joins": [
{ "type": "left", "object": "user", "as": "u", "on": "assignee_id = u.id" }
]type:inner | left | right | full。被 join 的表通过 as 别名在 where、orderBy 和 aggregations 中可用。
聚合
仅 POST /api/v1/data/:object/query。
{
"object": "order",
"where": { "status": { "$ne": "cancelled" } },
"groupBy": [
"customer_id",
{ "field": "created_at", "dateGranularity": "month" }
],
"aggregations": [
{ "function": "sum", "field": "amount", "alias": "total_sales" },
{ "function": "count", "alias": "order_count" }
],
"having": { "total_sales": { "$gt": 10000 } },
"orderBy": [{ "field": "total_sales", "order": "desc" }],
"limit": 25
}函数: count、sum、avg、min、max、count_distinct、array_agg、string_agg。
日期粒度(用于时间分桶 group-by):
day | week | month | quarter | year。
Distinct
{ "object": "ticket", "fields": ["status"], "distinct": true }搜索
对 searchable: true 字段的全文搜索通过 GET /api/v1/search?q=...&object=ticket 暴露。每对象评分规则在对象 spec 上配置。
ObjectQL 出现的位置
GET /api/v1/data/:object—— 查询字符串形式POST /api/v1/data/:object/query—— 完整请求体,支持聚合- 视图定义(
filter、sort)—— 编译为 ObjectQL - 报表 —— 序列化为 ObjectQL
- AI
query_data工具 —— 为审批队列生成 ObjectQL 请求体
参见
- REST API —— 消费 ObjectQL 的端点
- 字段类型 —— 可查询的内容
- CEL ——
$cel过滤器内使用的表达式语言 @objectstack/spec/data/query.zod.ts—— 权威 schema