@docyrus/docyrus 0.0.19 → 0.0.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agent-loader.js +37 -3
- package/agent-loader.js.map +2 -2
- package/main.js +498 -93
- package/main.js.map +4 -4
- package/package.json +14 -4
- package/resources/chrome-tools/browser-content.js +103 -0
- package/resources/chrome-tools/browser-cookies.js +35 -0
- package/resources/chrome-tools/browser-eval.js +53 -0
- package/resources/chrome-tools/browser-hn-scraper.js +108 -0
- package/resources/chrome-tools/browser-nav.js +44 -0
- package/resources/chrome-tools/browser-pick.js +162 -0
- package/resources/chrome-tools/browser-screenshot.js +34 -0
- package/resources/chrome-tools/browser-start.js +86 -0
- package/resources/pi-agent/extensions/answer.ts +532 -0
- package/resources/pi-agent/extensions/context.ts +578 -0
- package/resources/pi-agent/extensions/control.ts +1779 -0
- package/resources/pi-agent/extensions/diff.ts +218 -0
- package/resources/pi-agent/extensions/files.ts +199 -0
- package/resources/pi-agent/extensions/loop.ts +446 -0
- package/resources/pi-agent/extensions/multi-edit.ts +835 -0
- package/resources/pi-agent/extensions/notify.ts +88 -0
- package/resources/pi-agent/extensions/pi-mcp-adapter/CHANGELOG.md +192 -0
- package/resources/pi-agent/extensions/pi-mcp-adapter/LICENSE +21 -0
- package/resources/pi-agent/extensions/pi-mcp-adapter/README.md +296 -0
- package/resources/pi-agent/extensions/pi-mcp-adapter/app-bridge.bundle.js +67 -0
- package/resources/pi-agent/extensions/pi-mcp-adapter/cli.js +108 -0
- package/resources/pi-agent/extensions/pi-mcp-adapter/commands.ts +211 -0
- package/resources/pi-agent/extensions/pi-mcp-adapter/config.ts +227 -0
- package/resources/pi-agent/extensions/pi-mcp-adapter/consent-manager.ts +64 -0
- package/resources/pi-agent/extensions/pi-mcp-adapter/direct-tools.ts +301 -0
- package/resources/pi-agent/extensions/pi-mcp-adapter/errors.ts +219 -0
- package/resources/pi-agent/extensions/pi-mcp-adapter/glimpse-ui.ts +80 -0
- package/resources/pi-agent/extensions/pi-mcp-adapter/host-html-template.ts +427 -0
- package/resources/pi-agent/extensions/pi-mcp-adapter/index.ts +232 -0
- package/resources/pi-agent/extensions/pi-mcp-adapter/init.ts +319 -0
- package/resources/pi-agent/extensions/pi-mcp-adapter/lifecycle.ts +93 -0
- package/resources/pi-agent/extensions/pi-mcp-adapter/logger.ts +169 -0
- package/resources/pi-agent/extensions/pi-mcp-adapter/mcp-panel.ts +713 -0
- package/resources/pi-agent/extensions/pi-mcp-adapter/metadata-cache.ts +191 -0
- package/resources/pi-agent/extensions/pi-mcp-adapter/npx-resolver.ts +419 -0
- package/resources/pi-agent/extensions/pi-mcp-adapter/oauth-handler.ts +56 -0
- package/resources/pi-agent/extensions/pi-mcp-adapter/package.json +85 -0
- package/resources/pi-agent/extensions/pi-mcp-adapter/paths.ts +29 -0
- package/resources/pi-agent/extensions/pi-mcp-adapter/proxy-modes.ts +635 -0
- package/resources/pi-agent/extensions/pi-mcp-adapter/resource-tools.ts +17 -0
- package/resources/pi-agent/extensions/pi-mcp-adapter/server-manager.ts +330 -0
- package/resources/pi-agent/extensions/pi-mcp-adapter/state.ts +41 -0
- package/resources/pi-agent/extensions/pi-mcp-adapter/tool-metadata.ts +144 -0
- package/resources/pi-agent/extensions/pi-mcp-adapter/tool-registrar.ts +46 -0
- package/resources/pi-agent/extensions/pi-mcp-adapter/types.ts +367 -0
- package/resources/pi-agent/extensions/pi-mcp-adapter/ui-resource-handler.ts +145 -0
- package/resources/pi-agent/extensions/pi-mcp-adapter/ui-server.ts +623 -0
- package/resources/pi-agent/extensions/pi-mcp-adapter/ui-session.ts +384 -0
- package/resources/pi-agent/extensions/pi-mcp-adapter/ui-stream-types.ts +89 -0
- package/resources/pi-agent/extensions/pi-mcp-adapter/utils.ts +75 -0
- package/resources/pi-agent/extensions/prompt-editor.ts +1315 -0
- package/resources/pi-agent/extensions/prompt-url-widget.ts +158 -0
- package/resources/pi-agent/extensions/redraws.ts +24 -0
- package/resources/pi-agent/extensions/review.ts +2160 -0
- package/resources/pi-agent/extensions/todos.ts +2076 -0
- package/resources/pi-agent/extensions/tps.ts +47 -0
- package/resources/pi-agent/extensions/whimsical.ts +474 -0
- package/resources/pi-agent/prompts/coder-system.md +106 -0
- package/resources/pi-agent/skills/changelog-generator/SKILL.md +425 -0
- package/resources/pi-agent/skills/docyrus-chrome-devtools-cli/SKILL.md +80 -0
- package/resources/pi-agent/skills/docyrus-platform/SKILL.md +71 -0
- package/resources/pi-agent/skills/docyrus-platform/references/ai-capabilities.md +43 -0
- package/resources/pi-agent/skills/docyrus-platform/references/auth-and-multi-tenancy.md +35 -0
- package/resources/pi-agent/skills/docyrus-platform/references/automation-and-workflows.md +30 -0
- package/resources/pi-agent/skills/docyrus-platform/references/core-building-blocks.md +53 -0
- package/resources/pi-agent/skills/{docyrus-api-dev → docyrus-platform}/references/data-source-query-guide.md +32 -28
- package/resources/pi-agent/skills/docyrus-platform/references/developer-tools.md +28 -0
- package/resources/pi-agent/skills/docyrus-platform/references/docyrus-cli-usage.md +554 -0
- package/resources/pi-agent/skills/{docyrus-api-dev → docyrus-platform}/references/formula-design-guide-llm.md +15 -23
- package/resources/pi-agent/skills/docyrus-platform/references/integrations-and-events.md +60 -0
- package/resources/pi-agent/skills/docyrus-platform/references/platform-services.md +58 -0
- package/resources/pi-agent/skills/docyrus-platform/references/querying-and-data-operations.md +27 -0
- package/resources/pi-agent/prompts/coder-append-system.md +0 -19
- package/resources/pi-agent/skills/docyrus-ai/SKILL.md +0 -28
- package/resources/pi-agent/skills/docyrus-api-dev/SKILL.md +0 -161
- package/resources/pi-agent/skills/docyrus-api-dev/references/api-client.md +0 -349
- package/resources/pi-agent/skills/docyrus-api-dev/references/authentication.md +0 -238
- package/resources/pi-agent/skills/docyrus-api-dev/references/query-and-formulas.md +0 -592
- package/resources/pi-agent/skills/docyrus-api-doctor/SKILL.md +0 -70
- package/resources/pi-agent/skills/docyrus-api-doctor/references/checklist-details.md +0 -588
- package/resources/pi-agent/skills/docyrus-app-dev/SKILL.md +0 -159
- package/resources/pi-agent/skills/docyrus-app-dev/references/api-client-and-auth.md +0 -275
- package/resources/pi-agent/skills/docyrus-app-dev/references/collections-and-patterns.md +0 -352
- package/resources/pi-agent/skills/docyrus-app-dev/references/data-source-query-guide.md +0 -2059
- package/resources/pi-agent/skills/docyrus-app-dev/references/formula-design-guide-llm.md +0 -320
- package/resources/pi-agent/skills/docyrus-app-dev/references/query-guide.md +0 -525
- package/resources/pi-agent/skills/docyrus-app-ui-design/SKILL.md +0 -466
- package/resources/pi-agent/skills/docyrus-app-ui-design/references/component-selection-guide.md +0 -602
- package/resources/pi-agent/skills/docyrus-app-ui-design/references/icon-usage-guide.md +0 -463
- package/resources/pi-agent/skills/docyrus-app-ui-design/references/preferred-components-catalog.md +0 -242
- package/resources/pi-agent/skills/docyrus-apps/SKILL.md +0 -54
- package/resources/pi-agent/skills/docyrus-architect/SKILL.md +0 -174
- package/resources/pi-agent/skills/docyrus-architect/references/custom-query-guide.md +0 -410
- package/resources/pi-agent/skills/docyrus-architect/references/data-source-query-guide.md +0 -2059
- package/resources/pi-agent/skills/docyrus-architect/references/formula-design-guide-llm.md +0 -320
- package/resources/pi-agent/skills/docyrus-architect/references/formula-reference.md +0 -145
- package/resources/pi-agent/skills/docyrus-auth/SKILL.md +0 -100
- package/resources/pi-agent/skills/docyrus-cli-app/SKILL.md +0 -279
- package/resources/pi-agent/skills/docyrus-cli-app/references/cli-manifest.md +0 -532
- package/resources/pi-agent/skills/docyrus-cli-app/references/list-query-examples.md +0 -248
- package/resources/pi-agent/skills/docyrus-curl/SKILL.md +0 -32
- package/resources/pi-agent/skills/docyrus-discover/SKILL.md +0 -63
- package/resources/pi-agent/skills/docyrus-ds/SKILL.md +0 -95
- package/resources/pi-agent/skills/docyrus-env/SKILL.md +0 -21
- package/resources/pi-agent/skills/docyrus-studio/SKILL.md +0 -369
- package/resources/pi-agent/skills/docyrus-tui/SKILL.md +0 -15
|
@@ -1,2059 +0,0 @@
|
|
|
1
|
-
# Data Source Query Guide
|
|
2
|
-
|
|
3
|
-
Comprehensive reference for querying data sources using the `ZodSelectQueryPayload` schema. This document covers every parameter, operator, and feature with detailed examples.
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## Table of Contents
|
|
8
|
-
|
|
9
|
-
1. [Overview](#overview)
|
|
10
|
-
2. [Query Payload Structure](#query-payload-structure)
|
|
11
|
-
3. [Common Parameters](#common-parameters)
|
|
12
|
-
4. [Columns](#columns)
|
|
13
|
-
5. [Filters](#filters)
|
|
14
|
-
6. [Filter Keyword](#filter-keyword)
|
|
15
|
-
7. [Order By](#order-by)
|
|
16
|
-
8. [Pagination (limit / offset)](#pagination)
|
|
17
|
-
9. [Calculations (Aggregations)](#calculations)
|
|
18
|
-
10. [Formulas](#formulas)
|
|
19
|
-
11. [Pivot](#pivot)
|
|
20
|
-
12. [Child Queries](#child-queries)
|
|
21
|
-
13. [Expand](#expand)
|
|
22
|
-
14. [Query Mode](#query-mode)
|
|
23
|
-
15. [Distinct Columns](#distinct-columns)
|
|
24
|
-
16. [Full Count](#full-count)
|
|
25
|
-
17. [Cursor-Based Sync](#cursor-based-sync)
|
|
26
|
-
18. [Filter Operators Reference](#filter-operators-reference)
|
|
27
|
-
19. [Allowed Functions Reference](#allowed-functions-reference)
|
|
28
|
-
20. [Allowed Aggregates Reference](#allowed-aggregates-reference)
|
|
29
|
-
21. [Allowed Cast Types](#allowed-cast-types)
|
|
30
|
-
22. [Complete Examples](#complete-examples)
|
|
31
|
-
|
|
32
|
-
---
|
|
33
|
-
|
|
34
|
-
## Overview
|
|
35
|
-
|
|
36
|
-
All data source reads go through a unified select query payload. The payload is validated by `ZodSelectQueryPayload` (defined in `libs/shared/src/database/schemas.ts`). It supports:
|
|
37
|
-
|
|
38
|
-
- **Column selection** with relation expansion, aliasing, spread, and functions
|
|
39
|
-
- **Filtering** with nested AND/OR groups, dozens of operators, and relation field filtering
|
|
40
|
-
- **Keyword search** via full-text search
|
|
41
|
-
- **Sorting** by one or more fields with direction
|
|
42
|
-
- **Pagination** with limit/offset
|
|
43
|
-
- **Aggregations** (count, sum, avg, min, max, etc.) with grouping
|
|
44
|
-
- **Formulas** — computed virtual columns (block/AST-based)
|
|
45
|
-
- **Pivot** — advanced cross-tab grouping with date range series and matrix CTEs
|
|
46
|
-
- **Child queries** — fetch related child records as nested JSON arrays
|
|
47
|
-
- **Field expansion** — automatically expand relation/user/enum fields
|
|
48
|
-
|
|
49
|
-
---
|
|
50
|
-
|
|
51
|
-
## Query Payload Structure
|
|
52
|
-
|
|
53
|
-
The full `ZodSelectQueryPayload` type:
|
|
54
|
-
|
|
55
|
-
```typescript
|
|
56
|
-
interface ISelectQueryParams {
|
|
57
|
-
// --- Identity ---
|
|
58
|
-
dataSourceId?: string | null;
|
|
59
|
-
dataSourceFullSlug?: string | null;
|
|
60
|
-
connectionId?: string | null;
|
|
61
|
-
connectionAccountId?: string | null;
|
|
62
|
-
parentRecord?: Record<string, any> | null;
|
|
63
|
-
|
|
64
|
-
// --- Filtering ---
|
|
65
|
-
filters?: IQueryFilterGroup | null;
|
|
66
|
-
filterKeyword?: string | null;
|
|
67
|
-
|
|
68
|
-
// --- Column Selection ---
|
|
69
|
-
columns?: string | null;
|
|
70
|
-
distinctColumns?: string[] | null;
|
|
71
|
-
|
|
72
|
-
// --- Computed Columns ---
|
|
73
|
-
formulas?: Record<string, ISelectQueryFormula> | null;
|
|
74
|
-
|
|
75
|
-
// --- Aggregation ---
|
|
76
|
-
calculations?: ISelectQueryCalculationRule[] | null;
|
|
77
|
-
showGroupSummaries?: boolean;
|
|
78
|
-
|
|
79
|
-
// --- Sorting ---
|
|
80
|
-
orderBy?: string | ISelectQueryOrderBy | ISelectQueryOrderBy[];
|
|
81
|
-
|
|
82
|
-
// --- Pagination ---
|
|
83
|
-
limit?: number; // default: 100
|
|
84
|
-
offset?: number; // default: 0
|
|
85
|
-
|
|
86
|
-
// --- Expansion ---
|
|
87
|
-
expandTypes?: ("user" | "enum" | "relation")[] | null;
|
|
88
|
-
expand?: string[] | null;
|
|
89
|
-
|
|
90
|
-
// --- Misc ---
|
|
91
|
-
queryMode?: "OLTP" | "OLAP" | "EXPORT";
|
|
92
|
-
fullCount?: boolean;
|
|
93
|
-
cursorDateStart?: string | null;
|
|
94
|
-
cursorDateEnd?: string | null;
|
|
95
|
-
|
|
96
|
-
// --- Advanced ---
|
|
97
|
-
childQueries?: Record<string, IQueryChildQueryParams> | null;
|
|
98
|
-
pivot?: {
|
|
99
|
-
matrix: ISelectPivotMatrixQuery[];
|
|
100
|
-
hideEmptyRows?: boolean;
|
|
101
|
-
orderBy?: string | ISelectQueryOrderBy | ISelectQueryOrderBy[];
|
|
102
|
-
limit?: number;
|
|
103
|
-
} | null;
|
|
104
|
-
}
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
---
|
|
108
|
-
|
|
109
|
-
## Common Parameters
|
|
110
|
-
|
|
111
|
-
These parameters identify which data source to query.
|
|
112
|
-
|
|
113
|
-
| Parameter | Type | Description |
|
|
114
|
-
|---|---|---|
|
|
115
|
-
| `dataSourceId` | `string \| null` | UUID of the data source |
|
|
116
|
-
| `dataSourceFullSlug` | `string \| null` | Full slug in `appSlug_slug` format (e.g. `crm_account`) |
|
|
117
|
-
| `connectionId` | `string \| null` | Connection ID for external data sources |
|
|
118
|
-
| `connectionAccountId` | `string \| null` | Account ID for the connection |
|
|
119
|
-
| `parentRecord` | `object \| null` | Parent record context for relation/formula resolution |
|
|
120
|
-
|
|
121
|
-
> **Note:** You need either `dataSourceId` or `dataSourceFullSlug` to identify the target data source.
|
|
122
|
-
|
|
123
|
-
---
|
|
124
|
-
|
|
125
|
-
## Columns
|
|
126
|
-
|
|
127
|
-
**Parameter:** `columns` — `string | null`
|
|
128
|
-
|
|
129
|
-
Use a comma-separated list of field slugs to select specific columns.
|
|
130
|
-
|
|
131
|
-
### Rules
|
|
132
|
-
|
|
133
|
-
- Use `()` to select specific columns from a related record (`field-relation`, `field-select`, `field-userSelect` type fields).
|
|
134
|
-
- Use `...` (spread operator) to flatten related columns into the root object. **Always flatten related columns when fetching data for charts** to avoid object nesting and parsing overhead.
|
|
135
|
-
- Use `:` to alias a column. The alias goes on the left side (e.g. `tn:task_name`).
|
|
136
|
-
- Use `@` to apply a pre-defined function. **Always use with an alias** (e.g. `name:upper@account_name`).
|
|
137
|
-
- **Do not** use aggregation functions (count, sum, etc.) via `@` syntax — use `calculations` instead.
|
|
138
|
-
|
|
139
|
-
### Basic Selection
|
|
140
|
-
|
|
141
|
-
```
|
|
142
|
-
"columns": "task_name, created_on, record_owner"
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
### Aliasing with `:`
|
|
146
|
-
|
|
147
|
-
Use `:` to give a column an alias (shorter name in the result).
|
|
148
|
-
|
|
149
|
-
```
|
|
150
|
-
"columns": "ra:related_account"
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
**Result:**
|
|
154
|
-
|
|
155
|
-
```json
|
|
156
|
-
[
|
|
157
|
-
{
|
|
158
|
-
"ra": {
|
|
159
|
-
"id": "uuid",
|
|
160
|
-
"name": "account name"
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
]
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
### Relation Expansion with `()`
|
|
167
|
-
|
|
168
|
-
Use parentheses to select specific columns from a related record. Works with `field-relation`, `field-select`, and `field-userSelect` type fields.
|
|
169
|
-
|
|
170
|
-
```
|
|
171
|
-
"columns": "task_name, related_account(name:account_name, phone:account_phone)"
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
**Result:**
|
|
175
|
-
|
|
176
|
-
```json
|
|
177
|
-
[
|
|
178
|
-
{
|
|
179
|
-
"task_name": "Task Name",
|
|
180
|
-
"related_account": {
|
|
181
|
-
"name": "Account Name",
|
|
182
|
-
"phone": "05556668899"
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
]
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
### Spread Operator `...`
|
|
189
|
-
|
|
190
|
-
Use the spread operator to flatten selected columns from a related record into the root object (no nesting).
|
|
191
|
-
|
|
192
|
-
```
|
|
193
|
-
"columns": "task_name, ...related_account(account_name, phone:account_phone)"
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
**Result:**
|
|
197
|
-
|
|
198
|
-
```json
|
|
199
|
-
[
|
|
200
|
-
{
|
|
201
|
-
"task_name": "Task Name",
|
|
202
|
-
"account_name": "Account Name",
|
|
203
|
-
"phone": "05556668899"
|
|
204
|
-
}
|
|
205
|
-
]
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
### Functions with `@`
|
|
209
|
-
|
|
210
|
-
Use `@` to apply a pre-defined function to a column, specified as `<function>@<field>`.
|
|
211
|
-
|
|
212
|
-
```
|
|
213
|
-
"columns": "task_name, ...related_account(an:upper@account_name, ap:account_phone)"
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
**Result:**
|
|
217
|
-
|
|
218
|
-
```json
|
|
219
|
-
[
|
|
220
|
-
{
|
|
221
|
-
"task_name": "Task Name",
|
|
222
|
-
"an": "ACCOUNT NAME",
|
|
223
|
-
"ap": "05556668899"
|
|
224
|
-
}
|
|
225
|
-
]
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
### Special Date/DateTime Formulas for Aggregations
|
|
229
|
-
|
|
230
|
-
Use the `@` symbol with special date formulas to format date intervals. These are typically used with a date interval filter to group data for a specific period. Values outside the current period are grouped as `"OLDER"` and `"UPCOMING"`.
|
|
231
|
-
|
|
232
|
-
**Format:** `<formula>@<date_or_datetime_field>`
|
|
233
|
-
|
|
234
|
-
| Formula | Description | Example |
|
|
235
|
-
| ------- | ----------- | ------- |
|
|
236
|
-
| `hours_of_today` | Groups by hour for today | `hours_of_today@created_on` |
|
|
237
|
-
| `days_of_week` | Groups by day for the current week | `days_of_week@created_on` |
|
|
238
|
-
| `days_of_month` | Groups by day for the current month | `days_of_month@created_on` |
|
|
239
|
-
| `weeks_of_month` | Groups by week number for the current month | `weeks_of_month@created_on` |
|
|
240
|
-
| `weeks_of_quarter` | Groups by week number for the current quarter | `weeks_of_quarter@created_on` |
|
|
241
|
-
| `months_of_quarter` | Groups by month for the current quarter | `months_of_quarter@created_on` |
|
|
242
|
-
| `months_of_year` | Groups by month for the current year (YYYY-MM) | `months_of_year@created_on` |
|
|
243
|
-
| `quarters_of_year` | Groups by quarter for the current year (YYYY-Q) | `quarters_of_year@created_on` |
|
|
244
|
-
|
|
245
|
-
### Column Syntax with `to_char` Function
|
|
246
|
-
|
|
247
|
-
Use `to_char` with brackets `[]` for date formatting:
|
|
248
|
-
|
|
249
|
-
```
|
|
250
|
-
"columns": "day:to_char[DD/MM/YYYY]@created_on"
|
|
251
|
-
```
|
|
252
|
-
|
|
253
|
-
This formats the `created_on` field as `DD/MM/YYYY` and aliases it as `day`.
|
|
254
|
-
|
|
255
|
-
---
|
|
256
|
-
|
|
257
|
-
## Filters
|
|
258
|
-
|
|
259
|
-
**Parameter:** `filters` — `IQueryFilterGroup | null`
|
|
260
|
-
|
|
261
|
-
Filters use a recursive group structure with combinators (`and` / `or`) and rules.
|
|
262
|
-
|
|
263
|
-
> **Tip:** If you are asked to find records that contain a specific string, prefer using `filterKeyword` instead of `filters` for that filter. `filterKeyword` performs full-text search across all searchable fields.
|
|
264
|
-
|
|
265
|
-
### Filter Group Structure
|
|
266
|
-
|
|
267
|
-
```typescript
|
|
268
|
-
interface IQueryFilterGroup {
|
|
269
|
-
rules: (IQueryFilterRule | IQueryFilterGroup)[];
|
|
270
|
-
combinator?: "and" | "or"; // default: "and"
|
|
271
|
-
not?: boolean; // negate the entire group
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
interface IQueryFilterRule {
|
|
275
|
-
field?: string;
|
|
276
|
-
operator: IFilterOperator;
|
|
277
|
-
value?: any;
|
|
278
|
-
filterType?: QueryFilterType | null;
|
|
279
|
-
}
|
|
280
|
-
```
|
|
281
|
-
|
|
282
|
-
### Filter Types (for value casting)
|
|
283
|
-
|
|
284
|
-
| FilterType | Use For |
|
|
285
|
-
|---|---|
|
|
286
|
-
| `NUMERIC` | Number fields |
|
|
287
|
-
| `ALPHA` | Text/string fields |
|
|
288
|
-
| `BOOL` | Boolean fields |
|
|
289
|
-
| `DATE` | Date fields |
|
|
290
|
-
| `TIME` | Time fields |
|
|
291
|
-
| `DATETIME` | DateTime fields |
|
|
292
|
-
| `MULTISELECT` | Multi-select fields |
|
|
293
|
-
| `LIST` | List fields |
|
|
294
|
-
| `RELATION` | Relation fields |
|
|
295
|
-
| `OWNER` | Owner/user fields |
|
|
296
|
-
| `FOLLOWER` | Follower fields |
|
|
297
|
-
| `APPROVAL` | Approval fields |
|
|
298
|
-
|
|
299
|
-
### Example: Basic AND Filter
|
|
300
|
-
|
|
301
|
-
```json
|
|
302
|
-
{
|
|
303
|
-
"filters": {
|
|
304
|
-
"combinator": "and",
|
|
305
|
-
"rules": [
|
|
306
|
-
{
|
|
307
|
-
"field": "task_status",
|
|
308
|
-
"operator": "=",
|
|
309
|
-
"value": 1
|
|
310
|
-
},
|
|
311
|
-
{
|
|
312
|
-
"field": "priority",
|
|
313
|
-
"operator": ">=",
|
|
314
|
-
"value": 3
|
|
315
|
-
}
|
|
316
|
-
]
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
```
|
|
320
|
-
|
|
321
|
-
### Example: Nested AND + OR
|
|
322
|
-
|
|
323
|
-
Filter records created between two dates, AND where either email is empty OR phone is not empty:
|
|
324
|
-
|
|
325
|
-
```json
|
|
326
|
-
{
|
|
327
|
-
"filters": {
|
|
328
|
-
"combinator": "and",
|
|
329
|
-
"rules": [
|
|
330
|
-
{
|
|
331
|
-
"field": "created_on",
|
|
332
|
-
"operator": "between",
|
|
333
|
-
"value": ["2025-10-01", "2025-11-01"]
|
|
334
|
-
},
|
|
335
|
-
{
|
|
336
|
-
"combinator": "or",
|
|
337
|
-
"rules": [
|
|
338
|
-
{
|
|
339
|
-
"field": "email",
|
|
340
|
-
"operator": "empty"
|
|
341
|
-
},
|
|
342
|
-
{
|
|
343
|
-
"field": "phone",
|
|
344
|
-
"operator": "not empty"
|
|
345
|
-
}
|
|
346
|
-
]
|
|
347
|
-
}
|
|
348
|
-
]
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
```
|
|
352
|
-
|
|
353
|
-
### Example: Filtering by Related Record's Field (String Match)
|
|
354
|
-
|
|
355
|
-
Use `filterKeyword` when searching for a specific substring across all searchable fields:
|
|
356
|
-
|
|
357
|
-
```json
|
|
358
|
-
{
|
|
359
|
-
"filterKeyword": "John",
|
|
360
|
-
"columns": "id, name, email"
|
|
361
|
-
}
|
|
362
|
-
```
|
|
363
|
-
|
|
364
|
-
Alternatively, use `rel_{{relation_field_slug}}/{{field_slug}}` with `like` operator to filter by a specific related field:
|
|
365
|
-
|
|
366
|
-
```json
|
|
367
|
-
{
|
|
368
|
-
"filters": {
|
|
369
|
-
"combinator": "and",
|
|
370
|
-
"rules": [
|
|
371
|
-
{
|
|
372
|
-
"field": "rel_client/name",
|
|
373
|
-
"operator": "like",
|
|
374
|
-
"value": "John"
|
|
375
|
-
}
|
|
376
|
-
]
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
```
|
|
380
|
-
|
|
381
|
-
### Example: Filtering by Related Record's Field
|
|
382
|
-
|
|
383
|
-
Use the `rel_{{relation_field_slug}}/{{field_slug}}` syntax to filter by a parent/related table's field:
|
|
384
|
-
|
|
385
|
-
```json
|
|
386
|
-
{
|
|
387
|
-
"filters": {
|
|
388
|
-
"combinator": "and",
|
|
389
|
-
"rules": [
|
|
390
|
-
{
|
|
391
|
-
"field": "task_status",
|
|
392
|
-
"operator": "in",
|
|
393
|
-
"value": [1, 2, 3]
|
|
394
|
-
},
|
|
395
|
-
{
|
|
396
|
-
"field": "rel_client/account_status",
|
|
397
|
-
"operator": "=",
|
|
398
|
-
"value": 2
|
|
399
|
-
}
|
|
400
|
-
]
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
```
|
|
404
|
-
|
|
405
|
-
### Example: Negated Filter Group
|
|
406
|
-
|
|
407
|
-
```json
|
|
408
|
-
{
|
|
409
|
-
"filters": {
|
|
410
|
-
"combinator": "and",
|
|
411
|
-
"not": true,
|
|
412
|
-
"rules": [
|
|
413
|
-
{
|
|
414
|
-
"field": "status",
|
|
415
|
-
"operator": "=",
|
|
416
|
-
"value": "archived"
|
|
417
|
-
}
|
|
418
|
-
]
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
```
|
|
422
|
-
|
|
423
|
-
### Example: Date Shortcut Operators
|
|
424
|
-
|
|
425
|
-
```json
|
|
426
|
-
{
|
|
427
|
-
"filters": {
|
|
428
|
-
"rules": [
|
|
429
|
-
{
|
|
430
|
-
"field": "created_on",
|
|
431
|
-
"operator": "this_month"
|
|
432
|
-
}
|
|
433
|
-
]
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
```
|
|
437
|
-
|
|
438
|
-
### Example: User-Related Operators
|
|
439
|
-
|
|
440
|
-
```json
|
|
441
|
-
{
|
|
442
|
-
"filters": {
|
|
443
|
-
"rules": [
|
|
444
|
-
{
|
|
445
|
-
"field": "record_owner",
|
|
446
|
-
"operator": "active_user"
|
|
447
|
-
}
|
|
448
|
-
]
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
```
|
|
452
|
-
|
|
453
|
-
### Example: X Days Operators
|
|
454
|
-
|
|
455
|
-
```json
|
|
456
|
-
{
|
|
457
|
-
"filters": {
|
|
458
|
-
"rules": [
|
|
459
|
-
{
|
|
460
|
-
"field": "due_date",
|
|
461
|
-
"operator": "in_next_x_days",
|
|
462
|
-
"value": 7
|
|
463
|
-
}
|
|
464
|
-
]
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
```
|
|
468
|
-
|
|
469
|
-
---
|
|
470
|
-
|
|
471
|
-
## Filter Keyword
|
|
472
|
-
|
|
473
|
-
**Parameter:** `filterKeyword` — `string | null`
|
|
474
|
-
|
|
475
|
-
Performs a full-text search across all searchable fields.
|
|
476
|
-
|
|
477
|
-
```json
|
|
478
|
-
{
|
|
479
|
-
"filterKeyword": "John Doe",
|
|
480
|
-
"columns": "id, name, email"
|
|
481
|
-
}
|
|
482
|
-
```
|
|
483
|
-
|
|
484
|
-
---
|
|
485
|
-
|
|
486
|
-
## Order By
|
|
487
|
-
|
|
488
|
-
**Parameter:** `orderBy` — `string | ISelectQueryOrderBy | ISelectQueryOrderBy[]`
|
|
489
|
-
|
|
490
|
-
Use comma-separated field and direction pairs to sort data.
|
|
491
|
-
|
|
492
|
-
### String Format
|
|
493
|
-
|
|
494
|
-
```json
|
|
495
|
-
{
|
|
496
|
-
"orderBy": "created_on DESC"
|
|
497
|
-
}
|
|
498
|
-
```
|
|
499
|
-
|
|
500
|
-
Multiple fields:
|
|
501
|
-
|
|
502
|
-
```json
|
|
503
|
-
{
|
|
504
|
-
"orderBy": "firstname ASC, lastname DESC"
|
|
505
|
-
}
|
|
506
|
-
```
|
|
507
|
-
|
|
508
|
-
### Object Format
|
|
509
|
-
|
|
510
|
-
```json
|
|
511
|
-
{
|
|
512
|
-
"orderBy": {
|
|
513
|
-
"field": "created_on",
|
|
514
|
-
"direction": "desc"
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
```
|
|
518
|
-
|
|
519
|
-
### Array Format
|
|
520
|
-
|
|
521
|
-
```json
|
|
522
|
-
{
|
|
523
|
-
"orderBy": [
|
|
524
|
-
{ "field": "firstname", "direction": "asc" },
|
|
525
|
-
{ "field": "lastname", "direction": "desc" }
|
|
526
|
-
]
|
|
527
|
-
}
|
|
528
|
-
```
|
|
529
|
-
|
|
530
|
-
### Sorting by Related Field
|
|
531
|
-
|
|
532
|
-
Use parentheses to sort by a field of a related table:
|
|
533
|
-
|
|
534
|
-
```json
|
|
535
|
-
{
|
|
536
|
-
"orderBy": "relation_field_slug(field_name DESC), id ASC"
|
|
537
|
-
}
|
|
538
|
-
```
|
|
539
|
-
|
|
540
|
-
---
|
|
541
|
-
|
|
542
|
-
## Pagination
|
|
543
|
-
|
|
544
|
-
### `limit`
|
|
545
|
-
|
|
546
|
-
**Type:** `number` (positive integer)
|
|
547
|
-
**Default:** `100`
|
|
548
|
-
|
|
549
|
-
Maximum number of records to return.
|
|
550
|
-
|
|
551
|
-
### `offset`
|
|
552
|
-
|
|
553
|
-
**Type:** `number` (non-negative integer)
|
|
554
|
-
**Default:** `0`
|
|
555
|
-
|
|
556
|
-
Number of records to skip for pagination.
|
|
557
|
-
|
|
558
|
-
### Example
|
|
559
|
-
|
|
560
|
-
```json
|
|
561
|
-
{
|
|
562
|
-
"columns": "id, name",
|
|
563
|
-
"limit": 25,
|
|
564
|
-
"offset": 50,
|
|
565
|
-
"orderBy": "created_on DESC"
|
|
566
|
-
}
|
|
567
|
-
```
|
|
568
|
-
|
|
569
|
-
This fetches records 51–75 (page 3 with 25 per page).
|
|
570
|
-
|
|
571
|
-
---
|
|
572
|
-
|
|
573
|
-
## Calculations
|
|
574
|
-
|
|
575
|
-
**Parameter:** `calculations` — `ISelectQueryCalculationRule[] | null`
|
|
576
|
-
|
|
577
|
-
Use calculations to group and aggregate data.
|
|
578
|
-
|
|
579
|
-
### Calculation Rule Structure
|
|
580
|
-
|
|
581
|
-
```typescript
|
|
582
|
-
interface ISelectQueryCalculationRule {
|
|
583
|
-
func: string; // "count" | "sum" | "avg" | "min" | "max" | "jsonb_agg" | "json_agg" | "array_agg"
|
|
584
|
-
field: string; // field/column to aggregate
|
|
585
|
-
name?: string; // alias for the result column
|
|
586
|
-
isDistinct?: boolean; // aggregate unique values only (default: false)
|
|
587
|
-
minValue?: number; // aggregate values greater than this
|
|
588
|
-
maxValue?: number; // aggregate values less than this
|
|
589
|
-
numberType?: "bigint" | "int" | "decimal"; // result number type
|
|
590
|
-
}
|
|
591
|
-
```
|
|
592
|
-
|
|
593
|
-
### Rules
|
|
594
|
-
|
|
595
|
-
- Always use the `id` field for counting records.
|
|
596
|
-
- Use the aggregated field's slug for other functions (sum, avg, etc.).
|
|
597
|
-
- Skip `numberType` unless it is specifically required.
|
|
598
|
-
- Use `name` to alias the calculation result column (keep it short).
|
|
599
|
-
- **Do not** use `distinctColumns` together with `calculations`. Prefer `calculations` to aggregate data.
|
|
600
|
-
|
|
601
|
-
### Example: Count per Group
|
|
602
|
-
|
|
603
|
-
Count open tasks per user:
|
|
604
|
-
|
|
605
|
-
```json
|
|
606
|
-
{
|
|
607
|
-
"columns": "record_owner(name)",
|
|
608
|
-
"calculations": [
|
|
609
|
-
{
|
|
610
|
-
"field": "id",
|
|
611
|
-
"func": "count",
|
|
612
|
-
"name": "count_of_open_tasks"
|
|
613
|
-
}
|
|
614
|
-
],
|
|
615
|
-
"filters": {
|
|
616
|
-
"combinator": "and",
|
|
617
|
-
"rules": [
|
|
618
|
-
{
|
|
619
|
-
"field": "task_status",
|
|
620
|
-
"operator": "=",
|
|
621
|
-
"value": 1
|
|
622
|
-
}
|
|
623
|
-
]
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
```
|
|
627
|
-
|
|
628
|
-
**Result:**
|
|
629
|
-
|
|
630
|
-
```json
|
|
631
|
-
[
|
|
632
|
-
{
|
|
633
|
-
"record_owner": {
|
|
634
|
-
"name": "User Name"
|
|
635
|
-
},
|
|
636
|
-
"count_of_open_tasks": 10
|
|
637
|
-
}
|
|
638
|
-
]
|
|
639
|
-
```
|
|
640
|
-
|
|
641
|
-
### Example: Distinct Count
|
|
642
|
-
|
|
643
|
-
Count unique emails:
|
|
644
|
-
|
|
645
|
-
```json
|
|
646
|
-
{
|
|
647
|
-
"calculations": [
|
|
648
|
-
{
|
|
649
|
-
"field": "email",
|
|
650
|
-
"func": "count",
|
|
651
|
-
"name": "unique_emails",
|
|
652
|
-
"isDistinct": true
|
|
653
|
-
}
|
|
654
|
-
]
|
|
655
|
-
}
|
|
656
|
-
```
|
|
657
|
-
|
|
658
|
-
**Result:**
|
|
659
|
-
|
|
660
|
-
```json
|
|
661
|
-
[
|
|
662
|
-
{
|
|
663
|
-
"unique_emails": 10
|
|
664
|
-
}
|
|
665
|
-
]
|
|
666
|
-
```
|
|
667
|
-
|
|
668
|
-
### Example: Multiple Aggregations
|
|
669
|
-
|
|
670
|
-
```json
|
|
671
|
-
{
|
|
672
|
-
"columns": "category",
|
|
673
|
-
"calculations": [
|
|
674
|
-
{
|
|
675
|
-
"field": "id",
|
|
676
|
-
"func": "count",
|
|
677
|
-
"name": "total"
|
|
678
|
-
},
|
|
679
|
-
{
|
|
680
|
-
"field": "amount",
|
|
681
|
-
"func": "sum",
|
|
682
|
-
"name": "totalAmount"
|
|
683
|
-
},
|
|
684
|
-
{
|
|
685
|
-
"field": "amount",
|
|
686
|
-
"func": "avg",
|
|
687
|
-
"name": "avgAmount"
|
|
688
|
-
},
|
|
689
|
-
{
|
|
690
|
-
"field": "amount",
|
|
691
|
-
"func": "min",
|
|
692
|
-
"name": "minAmount"
|
|
693
|
-
},
|
|
694
|
-
{
|
|
695
|
-
"field": "amount",
|
|
696
|
-
"func": "max",
|
|
697
|
-
"name": "maxAmount"
|
|
698
|
-
}
|
|
699
|
-
]
|
|
700
|
-
}
|
|
701
|
-
```
|
|
702
|
-
|
|
703
|
-
### `showGroupSummaries`
|
|
704
|
-
|
|
705
|
-
**Type:** `boolean`
|
|
706
|
-
**Default:** `false`
|
|
707
|
-
|
|
708
|
-
When `true` and aggregation is used, includes group summary rows in the output.
|
|
709
|
-
|
|
710
|
-
---
|
|
711
|
-
|
|
712
|
-
## Formulas
|
|
713
|
-
|
|
714
|
-
**Parameter:** `formulas` — `Record<string, ISelectQueryFormula> | null`
|
|
715
|
-
|
|
716
|
-
Formulas are virtual computed columns injected into `SELECT` queries at build time. Keys are the formula names (used as column aliases), values are formula definitions.
|
|
717
|
-
|
|
718
|
-
There are two block formula formats:
|
|
719
|
-
|
|
720
|
-
### 1. Block Inline Formula
|
|
721
|
-
|
|
722
|
-
AST-based formula that compiles to an inline SQL expression. Uses a block tree with `kind` discriminator.
|
|
723
|
-
|
|
724
|
-
```typescript
|
|
725
|
-
interface IQueryBlockInlineFormulaSchema {
|
|
726
|
-
alias?: string;
|
|
727
|
-
inputs: IQueryFormulaBlock[]; // exactly 1 root block
|
|
728
|
-
}
|
|
729
|
-
```
|
|
730
|
-
|
|
731
|
-
**Example: Simple Division**
|
|
732
|
-
|
|
733
|
-
```json
|
|
734
|
-
{
|
|
735
|
-
"columns": "id, name, basic_formula",
|
|
736
|
-
"formulas": {
|
|
737
|
-
"basic_formula": {
|
|
738
|
-
"inputs": [{
|
|
739
|
-
"kind": "math",
|
|
740
|
-
"op": "/",
|
|
741
|
-
"inputs": [
|
|
742
|
-
{ "kind": "column", "name": "balance" },
|
|
743
|
-
{ "kind": "literal", "literal": 100 }
|
|
744
|
-
]
|
|
745
|
-
}]
|
|
746
|
-
}
|
|
747
|
-
}
|
|
748
|
-
}
|
|
749
|
-
```
|
|
750
|
-
|
|
751
|
-
> SQL: `("t0"."balance" / $1) as "basic_formula"`
|
|
752
|
-
|
|
753
|
-
### 2. Block Subquery Formula
|
|
754
|
-
|
|
755
|
-
Compiles to a correlated subquery against a child data source.
|
|
756
|
-
|
|
757
|
-
```typescript
|
|
758
|
-
interface IQueryBlockSubqueryFormulaSchema {
|
|
759
|
-
alias?: string;
|
|
760
|
-
inputs: IQueryFormulaBlock[];
|
|
761
|
-
from: string; // child table full slug
|
|
762
|
-
with: string | Record<string, string>; // join condition(s)
|
|
763
|
-
filters?: IQueryFilterGroup;
|
|
764
|
-
}
|
|
765
|
-
```
|
|
766
|
-
|
|
767
|
-
**Example: Count Child Records**
|
|
768
|
-
|
|
769
|
-
```json
|
|
770
|
-
{
|
|
771
|
-
"columns": "id, name, children_count",
|
|
772
|
-
"formulas": {
|
|
773
|
-
"children_count": {
|
|
774
|
-
"from": "app_child_table",
|
|
775
|
-
"with": "parent_field",
|
|
776
|
-
"inputs": [{
|
|
777
|
-
"kind": "aggregate",
|
|
778
|
-
"name": "count",
|
|
779
|
-
"inputs": []
|
|
780
|
-
}]
|
|
781
|
-
}
|
|
782
|
-
}
|
|
783
|
-
}
|
|
784
|
-
```
|
|
785
|
-
|
|
786
|
-
> SQL: `(SELECT count(*) FROM "schema"."child_table" AS "t0_child" WHERE "t0_child"."parent_field" = "t0"."id") AS "children_count"`
|
|
787
|
-
|
|
788
|
-
**Example: Subquery with Filters**
|
|
789
|
-
|
|
790
|
-
```json
|
|
791
|
-
{
|
|
792
|
-
"formulas": {
|
|
793
|
-
"active_children": {
|
|
794
|
-
"from": "app_child_table",
|
|
795
|
-
"with": "parent_id",
|
|
796
|
-
"filters": {
|
|
797
|
-
"rules": [
|
|
798
|
-
{ "field": "status", "operator": "=", "value": "active" }
|
|
799
|
-
]
|
|
800
|
-
},
|
|
801
|
-
"inputs": [{
|
|
802
|
-
"kind": "aggregate",
|
|
803
|
-
"name": "count",
|
|
804
|
-
"inputs": []
|
|
805
|
-
}]
|
|
806
|
-
}
|
|
807
|
-
}
|
|
808
|
-
}
|
|
809
|
-
```
|
|
810
|
-
|
|
811
|
-
**Example: Multi-Field Subquery Join**
|
|
812
|
-
|
|
813
|
-
```json
|
|
814
|
-
{
|
|
815
|
-
"formulas": {
|
|
816
|
-
"related_sum": {
|
|
817
|
-
"from": "app_child",
|
|
818
|
-
"with": {
|
|
819
|
-
"child_field1": "parent_field1",
|
|
820
|
-
"child_field2": "parent_field2"
|
|
821
|
-
},
|
|
822
|
-
"inputs": [{
|
|
823
|
-
"kind": "aggregate",
|
|
824
|
-
"name": "sum",
|
|
825
|
-
"inputs": [{ "kind": "column", "name": "amount" }]
|
|
826
|
-
}]
|
|
827
|
-
}
|
|
828
|
-
}
|
|
829
|
-
}
|
|
830
|
-
```
|
|
831
|
-
|
|
832
|
-
**Example: Compatibility Wrapper**
|
|
833
|
-
|
|
834
|
-
Block subquery formulas can also be wrapped under an `expression` key:
|
|
835
|
-
|
|
836
|
-
```json
|
|
837
|
-
{
|
|
838
|
-
"formulas": {
|
|
839
|
-
"children_count": {
|
|
840
|
-
"expression": {
|
|
841
|
-
"from": "app_child_table",
|
|
842
|
-
"with": "parent_field",
|
|
843
|
-
"inputs": [{
|
|
844
|
-
"kind": "aggregate",
|
|
845
|
-
"name": "count",
|
|
846
|
-
"distinct": true,
|
|
847
|
-
"inputs": [{ "kind": "column", "name": "id" }]
|
|
848
|
-
}]
|
|
849
|
-
}
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
}
|
|
853
|
-
```
|
|
854
|
-
|
|
855
|
-
### Block Formula Kinds Reference
|
|
856
|
-
|
|
857
|
-
Every block has a `kind` discriminator and optional `tz` (timezone) and `cast` (type cast) properties.
|
|
858
|
-
|
|
859
|
-
#### `literal` — Static Values
|
|
860
|
-
|
|
861
|
-
```json
|
|
862
|
-
{ "kind": "literal", "literal": "Hello World" }
|
|
863
|
-
{ "kind": "literal", "literal": 42 }
|
|
864
|
-
{ "kind": "literal", "literal": true }
|
|
865
|
-
{ "kind": "literal", "literal": null }
|
|
866
|
-
{ "kind": "literal", "literal": ["active", "pending", "approved"] }
|
|
867
|
-
```
|
|
868
|
-
|
|
869
|
-
#### `column` — Table Column Reference
|
|
870
|
-
|
|
871
|
-
```json
|
|
872
|
-
{ "kind": "column", "name": "fullname" }
|
|
873
|
-
{ "kind": "column", "name": ["col1", "col2"] }
|
|
874
|
-
```
|
|
875
|
-
|
|
876
|
-
#### `builtin` — SQL Constants
|
|
877
|
-
|
|
878
|
-
```json
|
|
879
|
-
{ "kind": "builtin", "name": "current_date" }
|
|
880
|
-
{ "kind": "builtin", "name": "now" }
|
|
881
|
-
```
|
|
882
|
-
|
|
883
|
-
Allowed values: `current_date`, `current_time`, `current_timestamp`, `now`
|
|
884
|
-
|
|
885
|
-
#### `function` — SQL Function Calls
|
|
886
|
-
|
|
887
|
-
```json
|
|
888
|
-
{
|
|
889
|
-
"kind": "function",
|
|
890
|
-
"name": "concat",
|
|
891
|
-
"inputs": [
|
|
892
|
-
{ "kind": "literal", "literal": "Hello " },
|
|
893
|
-
{ "kind": "column", "name": "fullname" }
|
|
894
|
-
]
|
|
895
|
-
}
|
|
896
|
-
```
|
|
897
|
-
|
|
898
|
-
> Only whitelisted functions are allowed (see [Allowed Functions Reference](#allowed-functions-reference)).
|
|
899
|
-
|
|
900
|
-
#### `extract` — Date Part Extraction
|
|
901
|
-
|
|
902
|
-
```json
|
|
903
|
-
{
|
|
904
|
-
"kind": "extract",
|
|
905
|
-
"part": "month",
|
|
906
|
-
"inputs": [{ "kind": "column", "name": "created_on" }]
|
|
907
|
-
}
|
|
908
|
-
```
|
|
909
|
-
|
|
910
|
-
> SQL: `extract(month from "t0"."created_on")`
|
|
911
|
-
|
|
912
|
-
Parts: `year`, `month`, `day`, `hour`, `minute`, `second`
|
|
913
|
-
|
|
914
|
-
#### `aggregate` — Aggregate Functions
|
|
915
|
-
|
|
916
|
-
```json
|
|
917
|
-
{ "kind": "aggregate", "name": "count", "inputs": [] }
|
|
918
|
-
```
|
|
919
|
-
|
|
920
|
-
> SQL: `count(*)`
|
|
921
|
-
|
|
922
|
-
```json
|
|
923
|
-
{
|
|
924
|
-
"kind": "aggregate",
|
|
925
|
-
"name": "count",
|
|
926
|
-
"distinct": true,
|
|
927
|
-
"inputs": [{ "kind": "column", "name": "product_code" }]
|
|
928
|
-
}
|
|
929
|
-
```
|
|
930
|
-
|
|
931
|
-
> SQL: `count(distinct "t0"."product_code")`
|
|
932
|
-
|
|
933
|
-
Allowed aggregates: `count`, `sum`, `avg`, `min`, `max`, `jsonb_agg`, `json_agg`, `array_agg`
|
|
934
|
-
|
|
935
|
-
#### `math` — Arithmetic Operations
|
|
936
|
-
|
|
937
|
-
```json
|
|
938
|
-
{
|
|
939
|
-
"kind": "math",
|
|
940
|
-
"op": "*",
|
|
941
|
-
"inputs": [
|
|
942
|
-
{ "kind": "column", "name": "quantity" },
|
|
943
|
-
{ "kind": "column", "name": "unit_price" }
|
|
944
|
-
]
|
|
945
|
-
}
|
|
946
|
-
```
|
|
947
|
-
|
|
948
|
-
> SQL: `("t0"."quantity" * "t0"."unit_price")`
|
|
949
|
-
|
|
950
|
-
Operators: `+`, `-`, `*`, `/`, `%`
|
|
951
|
-
Requires at least 2 operands. For 3+: `((a op b) op c)`.
|
|
952
|
-
|
|
953
|
-
#### `case` — Conditional Expressions
|
|
954
|
-
|
|
955
|
-
```json
|
|
956
|
-
{
|
|
957
|
-
"kind": "case",
|
|
958
|
-
"cases": [{
|
|
959
|
-
"when": {
|
|
960
|
-
"kind": "compare",
|
|
961
|
-
"op": ">",
|
|
962
|
-
"left": { "kind": "column", "name": "price" },
|
|
963
|
-
"right": { "kind": "literal", "literal": 100 }
|
|
964
|
-
},
|
|
965
|
-
"then": { "kind": "literal", "literal": "expensive" }
|
|
966
|
-
}],
|
|
967
|
-
"else": { "kind": "literal", "literal": "cheap" }
|
|
968
|
-
}
|
|
969
|
-
```
|
|
970
|
-
|
|
971
|
-
> SQL: `case when "t0"."price" > $1 then $2 else $3 end`
|
|
972
|
-
|
|
973
|
-
#### `compare` — Comparison Operations
|
|
974
|
-
|
|
975
|
-
```json
|
|
976
|
-
{
|
|
977
|
-
"kind": "compare",
|
|
978
|
-
"op": "in",
|
|
979
|
-
"left": { "kind": "column", "name": "status" },
|
|
980
|
-
"right": { "kind": "literal", "literal": ["active", "pending"] }
|
|
981
|
-
}
|
|
982
|
-
```
|
|
983
|
-
|
|
984
|
-
> SQL: `"t0"."status" in ($1, $2)`
|
|
985
|
-
|
|
986
|
-
Operators: `=`, `!=`, `<>`, `>`, `<`, `>=`, `<=`, `like`, `ilike`, `in`, `not in`, `not_in`
|
|
987
|
-
|
|
988
|
-
#### `boolean` — Logical Operations
|
|
989
|
-
|
|
990
|
-
```json
|
|
991
|
-
{
|
|
992
|
-
"kind": "boolean",
|
|
993
|
-
"op": "and",
|
|
994
|
-
"inputs": [
|
|
995
|
-
{
|
|
996
|
-
"kind": "compare", "op": ">",
|
|
997
|
-
"left": { "kind": "column", "name": "price" },
|
|
998
|
-
"right": { "kind": "literal", "literal": 100 }
|
|
999
|
-
},
|
|
1000
|
-
{
|
|
1001
|
-
"kind": "compare", "op": "ilike",
|
|
1002
|
-
"left": { "kind": "column", "name": "name" },
|
|
1003
|
-
"right": { "kind": "literal", "literal": "%pro%" }
|
|
1004
|
-
}
|
|
1005
|
-
]
|
|
1006
|
-
}
|
|
1007
|
-
```
|
|
1008
|
-
|
|
1009
|
-
> SQL: `(("t0"."price" > $1) and ("t0"."name" ilike $2))`
|
|
1010
|
-
|
|
1011
|
-
Operators: `and`, `or`, `not`
|
|
1012
|
-
|
|
1013
|
-
### Block Formula: Type Casting
|
|
1014
|
-
|
|
1015
|
-
Any block can include a `cast` property:
|
|
1016
|
-
|
|
1017
|
-
```json
|
|
1018
|
-
{ "kind": "column", "name": "price", "cast": "decimal" }
|
|
1019
|
-
```
|
|
1020
|
-
|
|
1021
|
-
> SQL: `("t0"."price")::decimal`
|
|
1022
|
-
|
|
1023
|
-
### Block Formula: Timezone Handling
|
|
1024
|
-
|
|
1025
|
-
Any block can include a `tz` property:
|
|
1026
|
-
|
|
1027
|
-
```json
|
|
1028
|
-
{ "kind": "function", "name": "now", "tz": "UTC" }
|
|
1029
|
-
```
|
|
1030
|
-
|
|
1031
|
-
> SQL: `now() at time zone $1`
|
|
1032
|
-
|
|
1033
|
-
### Advanced Formula Examples
|
|
1034
|
-
|
|
1035
|
-
**Nested Functions with Aggregates:** Round the sum of (quantity × unit_price)
|
|
1036
|
-
|
|
1037
|
-
```json
|
|
1038
|
-
{
|
|
1039
|
-
"formulas": {
|
|
1040
|
-
"rounded_total": {
|
|
1041
|
-
"alias": "rounded_total",
|
|
1042
|
-
"inputs": [{
|
|
1043
|
-
"kind": "function",
|
|
1044
|
-
"name": "round",
|
|
1045
|
-
"inputs": [
|
|
1046
|
-
{
|
|
1047
|
-
"kind": "aggregate",
|
|
1048
|
-
"name": "sum",
|
|
1049
|
-
"inputs": [{
|
|
1050
|
-
"kind": "math",
|
|
1051
|
-
"op": "*",
|
|
1052
|
-
"inputs": [
|
|
1053
|
-
{ "kind": "column", "name": "quantity" },
|
|
1054
|
-
{ "kind": "column", "name": "unit_price" }
|
|
1055
|
-
]
|
|
1056
|
-
}]
|
|
1057
|
-
},
|
|
1058
|
-
{ "kind": "literal", "literal": 2 }
|
|
1059
|
-
]
|
|
1060
|
-
}]
|
|
1061
|
-
}
|
|
1062
|
-
}
|
|
1063
|
-
}
|
|
1064
|
-
```
|
|
1065
|
-
|
|
1066
|
-
> SQL: `round(sum(("t0"."quantity" * "t0"."unit_price")), $1) as "rounded_total"`
|
|
1067
|
-
|
|
1068
|
-
**CASE with Boolean Logic:** Categorize rows
|
|
1069
|
-
|
|
1070
|
-
```json
|
|
1071
|
-
{
|
|
1072
|
-
"formulas": {
|
|
1073
|
-
"category": {
|
|
1074
|
-
"inputs": [{
|
|
1075
|
-
"kind": "case",
|
|
1076
|
-
"cases": [{
|
|
1077
|
-
"when": {
|
|
1078
|
-
"kind": "boolean",
|
|
1079
|
-
"op": "and",
|
|
1080
|
-
"inputs": [
|
|
1081
|
-
{
|
|
1082
|
-
"kind": "compare", "op": ">",
|
|
1083
|
-
"left": { "kind": "column", "name": "price" },
|
|
1084
|
-
"right": { "kind": "literal", "literal": 100 }
|
|
1085
|
-
},
|
|
1086
|
-
{
|
|
1087
|
-
"kind": "compare", "op": "ilike",
|
|
1088
|
-
"left": { "kind": "column", "name": "name" },
|
|
1089
|
-
"right": { "kind": "literal", "literal": "%pro%" }
|
|
1090
|
-
}
|
|
1091
|
-
]
|
|
1092
|
-
},
|
|
1093
|
-
"then": { "kind": "literal", "literal": "premium" }
|
|
1094
|
-
}],
|
|
1095
|
-
"else": { "kind": "literal", "literal": "standard" }
|
|
1096
|
-
}]
|
|
1097
|
-
}
|
|
1098
|
-
}
|
|
1099
|
-
}
|
|
1100
|
-
```
|
|
1101
|
-
|
|
1102
|
-
**Null Handling with COALESCE:**
|
|
1103
|
-
|
|
1104
|
-
```json
|
|
1105
|
-
{
|
|
1106
|
-
"formulas": {
|
|
1107
|
-
"safe_desc": {
|
|
1108
|
-
"inputs": [{
|
|
1109
|
-
"kind": "function",
|
|
1110
|
-
"name": "coalesce",
|
|
1111
|
-
"inputs": [
|
|
1112
|
-
{ "kind": "column", "name": "description" },
|
|
1113
|
-
{ "kind": "literal", "literal": "No description" }
|
|
1114
|
-
]
|
|
1115
|
-
}]
|
|
1116
|
-
}
|
|
1117
|
-
}
|
|
1118
|
-
}
|
|
1119
|
-
```
|
|
1120
|
-
|
|
1121
|
-
**Timezone Conversion:**
|
|
1122
|
-
|
|
1123
|
-
```json
|
|
1124
|
-
{
|
|
1125
|
-
"formulas": {
|
|
1126
|
-
"local_time": {
|
|
1127
|
-
"inputs": [{
|
|
1128
|
-
"kind": "function",
|
|
1129
|
-
"name": "to_char",
|
|
1130
|
-
"inputs": [
|
|
1131
|
-
{ "kind": "function", "name": "now", "tz": "UTC" },
|
|
1132
|
-
{ "kind": "literal", "literal": "YYYY-MM-DD HH24:MI:SS" }
|
|
1133
|
-
]
|
|
1134
|
-
}]
|
|
1135
|
-
}
|
|
1136
|
-
}
|
|
1137
|
-
}
|
|
1138
|
-
```
|
|
1139
|
-
|
|
1140
|
-
> SQL: `to_char(now() at time zone $1, $2)`
|
|
1141
|
-
|
|
1142
|
-
---
|
|
1143
|
-
|
|
1144
|
-
## Pivot
|
|
1145
|
-
|
|
1146
|
-
**Parameter:** `pivot` — `{ matrix, hideEmptyRows?, orderBy?, limit? } | null`
|
|
1147
|
-
|
|
1148
|
-
Use `pivot` to perform advanced cross-tab grouping queries with aggregations. Each matrix object is executed as a CTE query. All CTEs are cross-joined to create a full matrix, then the main data is left-joined. This ensures all combinations appear in results, even when no matching records exist.
|
|
1149
|
-
|
|
1150
|
-
### Pivot Structure
|
|
1151
|
-
|
|
1152
|
-
```typescript
|
|
1153
|
-
interface IPivot {
|
|
1154
|
-
matrix: ISelectPivotMatrixQuery[];
|
|
1155
|
-
hideEmptyRows?: boolean;
|
|
1156
|
-
orderBy?: string | ISelectQueryOrderBy | ISelectQueryOrderBy[];
|
|
1157
|
-
limit?: number;
|
|
1158
|
-
}
|
|
1159
|
-
|
|
1160
|
-
interface ISelectPivotMatrixQuery {
|
|
1161
|
-
using: string; // field in main query to join the CTE on
|
|
1162
|
-
columns: string; // columns to select (supports alias, spread, functions)
|
|
1163
|
-
spread?: boolean; // spread jsonb columns as separate columns
|
|
1164
|
-
filters?: IQueryFilterGroup;
|
|
1165
|
-
limit?: number;
|
|
1166
|
-
dateRange?: {
|
|
1167
|
-
interval: "day" | "week" | "month" | "year" | "hour" | "minute" | "second";
|
|
1168
|
-
increment?: number; // number of intervals to increment (default: 1)
|
|
1169
|
-
min: string; // minimum datetime value (ISO format)
|
|
1170
|
-
max: string; // maximum datetime value (ISO format)
|
|
1171
|
-
};
|
|
1172
|
-
}
|
|
1173
|
-
```
|
|
1174
|
-
|
|
1175
|
-
### How Pivot Works
|
|
1176
|
-
|
|
1177
|
-
1. Each `matrix` entry generates a CTE (Common Table Expression):
|
|
1178
|
-
- If `dateRange` is provided, a date range series is generated
|
|
1179
|
-
- Otherwise, records are fetched from the related data source
|
|
1180
|
-
2. All CTEs are cross-joined to create the full cartesian product (matrix)
|
|
1181
|
-
3. The main query data is left-joined to the matrix
|
|
1182
|
-
4. This ensures all combinations appear, even with zero matching records
|
|
1183
|
-
|
|
1184
|
-
### Example: Orders per Day, per User, per Status
|
|
1185
|
-
|
|
1186
|
-
```json
|
|
1187
|
-
{
|
|
1188
|
-
"columns": "...order_status(orderStatus:name)",
|
|
1189
|
-
"pivot": {
|
|
1190
|
-
"matrix": [
|
|
1191
|
-
{
|
|
1192
|
-
"using": "created_on",
|
|
1193
|
-
"columns": "day:to_char[DD/MM/YYYY]@created_on",
|
|
1194
|
-
"dateRange": {
|
|
1195
|
-
"interval": "day",
|
|
1196
|
-
"min": "2025-09-01T00:00:00Z",
|
|
1197
|
-
"max": "2025-09-02T00:00:00Z"
|
|
1198
|
-
},
|
|
1199
|
-
"spread": true
|
|
1200
|
-
},
|
|
1201
|
-
{
|
|
1202
|
-
"using": "record_owner",
|
|
1203
|
-
"columns": "userName:name",
|
|
1204
|
-
"spread": true,
|
|
1205
|
-
"filters": {
|
|
1206
|
-
"combinator": "and",
|
|
1207
|
-
"rules": [
|
|
1208
|
-
{
|
|
1209
|
-
"field": "primary_role",
|
|
1210
|
-
"operator": "=",
|
|
1211
|
-
"value": "1cdefd30-9f6d-4c7e-94c9-5b8a7e1c9f31"
|
|
1212
|
-
}
|
|
1213
|
-
]
|
|
1214
|
-
}
|
|
1215
|
-
}
|
|
1216
|
-
]
|
|
1217
|
-
},
|
|
1218
|
-
"calculations": [
|
|
1219
|
-
{
|
|
1220
|
-
"field": "id",
|
|
1221
|
-
"func": "count",
|
|
1222
|
-
"name": "total"
|
|
1223
|
-
},
|
|
1224
|
-
{
|
|
1225
|
-
"field": "amount",
|
|
1226
|
-
"func": "sum",
|
|
1227
|
-
"name": "totalSold"
|
|
1228
|
-
}
|
|
1229
|
-
]
|
|
1230
|
-
}
|
|
1231
|
-
```
|
|
1232
|
-
|
|
1233
|
-
**What each matrix entry does:**
|
|
1234
|
-
|
|
1235
|
-
1. **First matrix** (`using: "created_on"`): Creates a date range series from 2025-09-01 to 2025-09-02 with `day` intervals. Even if there are no orders on a given day, that day still appears in results.
|
|
1236
|
-
2. **Second matrix** (`using: "record_owner"`): Fetches users filtered by role. Even if a user has no orders on a day, they still appear in the cross-join.
|
|
1237
|
-
|
|
1238
|
-
**Result:**
|
|
1239
|
-
|
|
1240
|
-
```json
|
|
1241
|
-
[
|
|
1242
|
-
{
|
|
1243
|
-
"userName": "User 1",
|
|
1244
|
-
"orderStatus": 1,
|
|
1245
|
-
"day": "01/09/2025",
|
|
1246
|
-
"total": 10,
|
|
1247
|
-
"totalSold": 3000
|
|
1248
|
-
},
|
|
1249
|
-
{
|
|
1250
|
-
"userName": "User 2",
|
|
1251
|
-
"orderStatus": 5,
|
|
1252
|
-
"day": "01/09/2025",
|
|
1253
|
-
"total": 5,
|
|
1254
|
-
"totalSold": 1500
|
|
1255
|
-
},
|
|
1256
|
-
{
|
|
1257
|
-
"userName": "User 1",
|
|
1258
|
-
"orderStatus": 1,
|
|
1259
|
-
"day": "02/09/2025",
|
|
1260
|
-
"total": 10,
|
|
1261
|
-
"totalSold": 3000
|
|
1262
|
-
},
|
|
1263
|
-
{
|
|
1264
|
-
"userName": "User 2",
|
|
1265
|
-
"orderStatus": 3,
|
|
1266
|
-
"day": "02/09/2025",
|
|
1267
|
-
"total": 5,
|
|
1268
|
-
"totalSold": 1500
|
|
1269
|
-
}
|
|
1270
|
-
]
|
|
1271
|
-
```
|
|
1272
|
-
|
|
1273
|
-
### Date Range Intervals
|
|
1274
|
-
|
|
1275
|
-
| Interval | Description |
|
|
1276
|
-
|---|---|
|
|
1277
|
-
| `day` | Generate one row per day |
|
|
1278
|
-
| `week` | Generate one row per week |
|
|
1279
|
-
| `month` | Generate one row per month |
|
|
1280
|
-
| `year` | Generate one row per year |
|
|
1281
|
-
| `hour` | Generate one row per hour |
|
|
1282
|
-
| `minute` | Generate one row per minute |
|
|
1283
|
-
| `second` | Generate one row per second |
|
|
1284
|
-
|
|
1285
|
-
### Pivot Options
|
|
1286
|
-
|
|
1287
|
-
| Option | Type | Description |
|
|
1288
|
-
|---|---|---|
|
|
1289
|
-
| `hideEmptyRows` | `boolean` | Don't include rows where no matching data exists |
|
|
1290
|
-
| `orderBy` | `string \| object \| array` | Sort the pivot results |
|
|
1291
|
-
| `limit` | `number` | Maximum number of pivot result rows (default: 1000) |
|
|
1292
|
-
|
|
1293
|
-
---
|
|
1294
|
-
|
|
1295
|
-
## Child Queries
|
|
1296
|
-
|
|
1297
|
-
**Parameter:** `childQueries` — `Record<string, IQueryChildQueryParams> | null`
|
|
1298
|
-
|
|
1299
|
-
Use `childQueries` to fetch related records from a child data source as a nested JSON array for each parent record. This is similar to a `LEFT JOIN` but returns results as an aggregated JSON array in a single column.
|
|
1300
|
-
|
|
1301
|
-
### Child Query Structure
|
|
1302
|
-
|
|
1303
|
-
```typescript
|
|
1304
|
-
interface IQueryChildQueryParams {
|
|
1305
|
-
from: string; // child data source slug in "appSlug_slug" format
|
|
1306
|
-
using: string; // field in the child DS that references the parent record
|
|
1307
|
-
columns?: string | null; // comma-separated columns to select from child
|
|
1308
|
-
filters?: IQueryFilterGroup; // optional filters on child records
|
|
1309
|
-
calculations?: ISelectQueryCalculationRule[]; // optional aggregations
|
|
1310
|
-
orderBy?: string | ISelectQueryOrderBy | ISelectQueryOrderBy[];
|
|
1311
|
-
limit?: number; // max child records per parent (default: 100)
|
|
1312
|
-
}
|
|
1313
|
-
```
|
|
1314
|
-
|
|
1315
|
-
### Example: Clients with Their Matters
|
|
1316
|
-
|
|
1317
|
-
```json
|
|
1318
|
-
{
|
|
1319
|
-
"columns": "id, name, matters",
|
|
1320
|
-
"childQueries": {
|
|
1321
|
-
"matters": {
|
|
1322
|
-
"from": "attornaid_matter",
|
|
1323
|
-
"using": "client",
|
|
1324
|
-
"columns": "name",
|
|
1325
|
-
"filters": {
|
|
1326
|
-
"rules": [
|
|
1327
|
-
{ "field": "created_on", "operator": "<", "value": "2025-12-01" }
|
|
1328
|
-
]
|
|
1329
|
-
}
|
|
1330
|
-
}
|
|
1331
|
-
}
|
|
1332
|
-
}
|
|
1333
|
-
```
|
|
1334
|
-
|
|
1335
|
-
**Result:**
|
|
1336
|
-
|
|
1337
|
-
```json
|
|
1338
|
-
[
|
|
1339
|
-
{
|
|
1340
|
-
"id": "uuid",
|
|
1341
|
-
"name": "Client Name",
|
|
1342
|
-
"matters": [
|
|
1343
|
-
{ "name": "Matter 1" },
|
|
1344
|
-
{ "name": "Matter 2" }
|
|
1345
|
-
]
|
|
1346
|
-
}
|
|
1347
|
-
]
|
|
1348
|
-
```
|
|
1349
|
-
|
|
1350
|
-
### Key Rules
|
|
1351
|
-
|
|
1352
|
-
- The child query key (e.g. `"matters"`) must also appear in the parent's `columns` string.
|
|
1353
|
-
- `from` uses `appSlug_slug` format (e.g. `"attornaid_matter"`).
|
|
1354
|
-
- `using` is the field in the **child** data source that references the parent record's `id`.
|
|
1355
|
-
- All parent query parameters (`columns`, `filters`, `calculations`, `orderBy`, `limit`) are supported within child queries.
|
|
1356
|
-
|
|
1357
|
-
### Example: Products with Recent Orders (limited, sorted)
|
|
1358
|
-
|
|
1359
|
-
```json
|
|
1360
|
-
{
|
|
1361
|
-
"columns": "id, product_name, recent_orders",
|
|
1362
|
-
"childQueries": {
|
|
1363
|
-
"recent_orders": {
|
|
1364
|
-
"from": "shop_order_item",
|
|
1365
|
-
"using": "product",
|
|
1366
|
-
"columns": "order_date, quantity, total_price",
|
|
1367
|
-
"orderBy": "order_date DESC",
|
|
1368
|
-
"limit": 5,
|
|
1369
|
-
"filters": {
|
|
1370
|
-
"rules": [
|
|
1371
|
-
{ "field": "order_date", "operator": "last_30_days" }
|
|
1372
|
-
]
|
|
1373
|
-
}
|
|
1374
|
-
}
|
|
1375
|
-
}
|
|
1376
|
-
}
|
|
1377
|
-
```
|
|
1378
|
-
|
|
1379
|
-
### Example: Child Query with Aggregations
|
|
1380
|
-
|
|
1381
|
-
```json
|
|
1382
|
-
{
|
|
1383
|
-
"columns": "id, name, order_stats",
|
|
1384
|
-
"childQueries": {
|
|
1385
|
-
"order_stats": {
|
|
1386
|
-
"from": "shop_order",
|
|
1387
|
-
"using": "customer",
|
|
1388
|
-
"calculations": [
|
|
1389
|
-
{ "field": "id", "func": "count", "name": "total_orders" },
|
|
1390
|
-
{ "field": "amount", "func": "sum", "name": "total_spent" }
|
|
1391
|
-
]
|
|
1392
|
-
}
|
|
1393
|
-
}
|
|
1394
|
-
}
|
|
1395
|
-
```
|
|
1396
|
-
|
|
1397
|
-
---
|
|
1398
|
-
|
|
1399
|
-
## Expand
|
|
1400
|
-
|
|
1401
|
-
### `expandTypes` (Deprecated)
|
|
1402
|
-
|
|
1403
|
-
**Type:** `("user" | "enum" | "relation")[] | null`
|
|
1404
|
-
|
|
1405
|
-
Automatically expand all columns of the specified field types. Replaced by `expand`.
|
|
1406
|
-
|
|
1407
|
-
```json
|
|
1408
|
-
{
|
|
1409
|
-
"expandTypes": ["user", "relation"]
|
|
1410
|
-
}
|
|
1411
|
-
```
|
|
1412
|
-
|
|
1413
|
-
### `expand`
|
|
1414
|
-
|
|
1415
|
-
**Type:** `string[] | null`
|
|
1416
|
-
|
|
1417
|
-
List of specific field slugs to expand. Expanded fields return their full object representation instead of just the ID/value.
|
|
1418
|
-
|
|
1419
|
-
```json
|
|
1420
|
-
{
|
|
1421
|
-
"expand": ["record_owner", "related_account", "status"]
|
|
1422
|
-
}
|
|
1423
|
-
```
|
|
1424
|
-
|
|
1425
|
-
---
|
|
1426
|
-
|
|
1427
|
-
## Query Mode
|
|
1428
|
-
|
|
1429
|
-
**Parameter:** `queryMode` — `"OLTP" | "OLAP" | "EXPORT"`
|
|
1430
|
-
**Default:** `"OLTP"`
|
|
1431
|
-
|
|
1432
|
-
| Mode | Description |
|
|
1433
|
-
|---|---|
|
|
1434
|
-
| `OLTP` | Standard transactional queries. Default. Lower limits for interactive use. |
|
|
1435
|
-
| `OLAP` | Analytical queries. Allows larger result sets. |
|
|
1436
|
-
| `EXPORT` | Export mode. Highest limits for bulk data extraction. |
|
|
1437
|
-
|
|
1438
|
-
---
|
|
1439
|
-
|
|
1440
|
-
## Distinct Columns
|
|
1441
|
-
|
|
1442
|
-
**Parameter:** `distinctColumns` — `string[] | null`
|
|
1443
|
-
|
|
1444
|
-
List of columns to deduplicate results on. Use only for simple queries when you need exactly one deterministic row per group and the winner is defined by a simple `ORDER BY`.
|
|
1445
|
-
|
|
1446
|
-
> **Important:** Do **not** use `distinctColumns` together with `calculations`. Prefer `calculations` to aggregate data.
|
|
1447
|
-
|
|
1448
|
-
### Example: Last Invoice Date per Client
|
|
1449
|
-
|
|
1450
|
-
```json
|
|
1451
|
-
{
|
|
1452
|
-
"columns": "...client(client_name:name), invoice_date",
|
|
1453
|
-
"distinctColumns": ["client"],
|
|
1454
|
-
"orderBy": "invoice_date DESC"
|
|
1455
|
-
}
|
|
1456
|
-
```
|
|
1457
|
-
|
|
1458
|
-
### Example: Deduplicate by Email
|
|
1459
|
-
|
|
1460
|
-
```json
|
|
1461
|
-
{
|
|
1462
|
-
"columns": "email, name",
|
|
1463
|
-
"distinctColumns": ["email"]
|
|
1464
|
-
}
|
|
1465
|
-
```
|
|
1466
|
-
|
|
1467
|
-
---
|
|
1468
|
-
|
|
1469
|
-
## Full Count
|
|
1470
|
-
|
|
1471
|
-
**Parameter:** `fullCount` — `boolean`
|
|
1472
|
-
|
|
1473
|
-
When `true`, returns the total count of records matching the filters using a window function, alongside the paginated results.
|
|
1474
|
-
|
|
1475
|
-
```json
|
|
1476
|
-
{
|
|
1477
|
-
"columns": "id, name",
|
|
1478
|
-
"limit": 10,
|
|
1479
|
-
"offset": 0,
|
|
1480
|
-
"fullCount": true
|
|
1481
|
-
}
|
|
1482
|
-
```
|
|
1483
|
-
|
|
1484
|
-
---
|
|
1485
|
-
|
|
1486
|
-
## Cursor-Based Sync
|
|
1487
|
-
|
|
1488
|
-
| Parameter | Type | Description |
|
|
1489
|
-
|---|---|---|
|
|
1490
|
-
| `cursorDateStart` | `string \| null` | ISO datetime cursor start for data sync |
|
|
1491
|
-
| `cursorDateEnd` | `string \| null` | ISO datetime cursor end for data sync |
|
|
1492
|
-
|
|
1493
|
-
Used for incremental data synchronization, fetching only records modified within the cursor window.
|
|
1494
|
-
|
|
1495
|
-
```json
|
|
1496
|
-
{
|
|
1497
|
-
"cursorDateStart": "2025-10-01T00:00:00Z",
|
|
1498
|
-
"cursorDateEnd": "2025-10-02T00:00:00Z"
|
|
1499
|
-
}
|
|
1500
|
-
```
|
|
1501
|
-
|
|
1502
|
-
---
|
|
1503
|
-
|
|
1504
|
-
## Filter Operators Reference
|
|
1505
|
-
|
|
1506
|
-
### Basic Comparison
|
|
1507
|
-
|
|
1508
|
-
| Operator | Description | Value Type |
|
|
1509
|
-
|---|---|---|
|
|
1510
|
-
| `=` | Equals | any |
|
|
1511
|
-
| `!=` | Not equals | any |
|
|
1512
|
-
| `<>` | Not equals (alias) | any |
|
|
1513
|
-
| `>` | Greater than | number/date |
|
|
1514
|
-
| `<` | Less than | number/date |
|
|
1515
|
-
| `>=` | Greater than or equal | number/date |
|
|
1516
|
-
| `<=` | Less than or equal | number/date |
|
|
1517
|
-
| `between` | Between two values | `[min, max]` |
|
|
1518
|
-
|
|
1519
|
-
### Text Search
|
|
1520
|
-
|
|
1521
|
-
| Operator | Description | Value Type |
|
|
1522
|
-
|---|---|---|
|
|
1523
|
-
| `like` | Pattern match (case-sensitive) | string with `%` wildcards |
|
|
1524
|
-
| `not like` | Negated pattern match | string with `%` wildcards |
|
|
1525
|
-
| `starts with` | Starts with value | string |
|
|
1526
|
-
| `ends with` | Ends with value | string |
|
|
1527
|
-
|
|
1528
|
-
### Collection
|
|
1529
|
-
|
|
1530
|
-
| Operator | Description | Value Type |
|
|
1531
|
-
|---|---|---|
|
|
1532
|
-
| `in` | Value is in list | array |
|
|
1533
|
-
| `not in` | Value is not in list | array |
|
|
1534
|
-
| `not_in` | Alias for `not in` | array |
|
|
1535
|
-
| `exists` | Record exists | — |
|
|
1536
|
-
| `contains any` | Contains any of the values | array |
|
|
1537
|
-
| `contains all` | Contains all of the values | array |
|
|
1538
|
-
| `not contains` | Does not contain | any |
|
|
1539
|
-
|
|
1540
|
-
### Null/Empty Checks
|
|
1541
|
-
|
|
1542
|
-
| Operator | Description | Value Type |
|
|
1543
|
-
|---|---|---|
|
|
1544
|
-
| `is` | Is value | any |
|
|
1545
|
-
| `is not` | Is not value | any |
|
|
1546
|
-
| `empty` | Field is empty/null | — |
|
|
1547
|
-
| `not empty` | Field is not empty/null | — |
|
|
1548
|
-
| `null` | Field is null | — |
|
|
1549
|
-
| `not null` | Field is not null | — |
|
|
1550
|
-
|
|
1551
|
-
### Boolean
|
|
1552
|
-
|
|
1553
|
-
| Operator | Description | Value Type |
|
|
1554
|
-
|---|---|---|
|
|
1555
|
-
| `true` | Field is true | — |
|
|
1556
|
-
| `false` | Field is false | — |
|
|
1557
|
-
|
|
1558
|
-
### User-Related
|
|
1559
|
-
|
|
1560
|
-
| Operator | Description |
|
|
1561
|
-
|---|---|
|
|
1562
|
-
| `active_user` | Field equals the current logged-in user |
|
|
1563
|
-
| `not_active_user` | Field does not equal the current user |
|
|
1564
|
-
| `in_active_user_scope` | Field is within active user's scope |
|
|
1565
|
-
| `not_in_active_user_scope` | Field is outside active user's scope |
|
|
1566
|
-
| `in_role` | User has specified role |
|
|
1567
|
-
| `not_in_role` | User does not have specified role |
|
|
1568
|
-
| `in_team` | User is in specified team |
|
|
1569
|
-
| `not_in_team` | User is not in specified team |
|
|
1570
|
-
| `in_active_user_team` | User is in active user's team |
|
|
1571
|
-
| `not_in_active_user_team` | User is not in active user's team |
|
|
1572
|
-
| `in_unit` | User is in specified org unit |
|
|
1573
|
-
| `not_in_unit` | User is not in specified org unit |
|
|
1574
|
-
| `in_sub_unit` | User is in sub-unit |
|
|
1575
|
-
| `not_in_sub_unit` | User is not in sub-unit |
|
|
1576
|
-
|
|
1577
|
-
### Record Sharing
|
|
1578
|
-
|
|
1579
|
-
| Operator | Description |
|
|
1580
|
-
|---|---|
|
|
1581
|
-
| `shared_to_me` | Record is shared to the current user |
|
|
1582
|
-
|
|
1583
|
-
### Follower-Related
|
|
1584
|
-
|
|
1585
|
-
| Operator | Description |
|
|
1586
|
-
|---|---|
|
|
1587
|
-
| `contains_active_user` | Followers contain the active user |
|
|
1588
|
-
| `not_contains_active_user` | Followers do not contain the active user |
|
|
1589
|
-
| `contains_member_of_active_user_team` | Followers contain a member of active user's team |
|
|
1590
|
-
|
|
1591
|
-
### Date Shortcuts
|
|
1592
|
-
|
|
1593
|
-
| Operator | Description |
|
|
1594
|
-
|---|---|
|
|
1595
|
-
| `today` | Is today |
|
|
1596
|
-
| `tomorrow` | Is tomorrow |
|
|
1597
|
-
| `yesterday` | Is yesterday |
|
|
1598
|
-
| `last_7_days` | Within last 7 days |
|
|
1599
|
-
| `last_15_days` | Within last 15 days |
|
|
1600
|
-
| `last_30_days` | Within last 30 days |
|
|
1601
|
-
| `last_60_days` | Within last 60 days |
|
|
1602
|
-
| `last_90_days` | Within last 90 days |
|
|
1603
|
-
| `last_120_days` | Within last 120 days |
|
|
1604
|
-
| `next_7_days` | Within next 7 days |
|
|
1605
|
-
| `next_15_days` | Within next 15 days |
|
|
1606
|
-
| `next_30_days` | Within next 30 days |
|
|
1607
|
-
| `next_60_days` | Within next 60 days |
|
|
1608
|
-
| `next_90_days` | Within next 90 days |
|
|
1609
|
-
| `next_120_days` | Within next 120 days |
|
|
1610
|
-
| `last_week` | During last week |
|
|
1611
|
-
| `this_week` | During this week |
|
|
1612
|
-
| `next_week` | During next week |
|
|
1613
|
-
| `last_month` | During last month |
|
|
1614
|
-
| `this_month` | During this month |
|
|
1615
|
-
| `next_month` | During next month |
|
|
1616
|
-
| `before_today` | Before today |
|
|
1617
|
-
| `after_today` | After today |
|
|
1618
|
-
| `last_year` | During last year |
|
|
1619
|
-
| `this_year` | During this year |
|
|
1620
|
-
| `next_year` | During next year |
|
|
1621
|
-
| `first_quarter` | During Q1 of current year |
|
|
1622
|
-
| `second_quarter` | During Q2 of current year |
|
|
1623
|
-
| `third_quarter` | During Q3 of current year |
|
|
1624
|
-
| `fourth_quarter` | During Q4 of current year |
|
|
1625
|
-
| `last_3_months` | Within last 3 months |
|
|
1626
|
-
| `last_6_months` | Within last 6 months |
|
|
1627
|
-
|
|
1628
|
-
### Dynamic Date Operators (require value)
|
|
1629
|
-
|
|
1630
|
-
| Operator | Value | Description |
|
|
1631
|
-
|---|---|---|
|
|
1632
|
-
| `x_days_ago` | number | Exactly X days ago |
|
|
1633
|
-
| `x_days_later` | number | Exactly X days later |
|
|
1634
|
-
| `before_last_x_days` | number | Before the last X days |
|
|
1635
|
-
| `in_last_x_days` | number | Within the last X days |
|
|
1636
|
-
| `after_last_x_days` | number | After the last X days |
|
|
1637
|
-
| `in_next_x_days` | number | Within the next X days |
|
|
1638
|
-
|
|
1639
|
-
---
|
|
1640
|
-
|
|
1641
|
-
## Allowed Functions Reference
|
|
1642
|
-
|
|
1643
|
-
### Postgres Functions
|
|
1644
|
-
|
|
1645
|
-
| Category | Functions |
|
|
1646
|
-
|---|---|
|
|
1647
|
-
| **String** | `length`, `lower`, `upper`, `substr`, `replace`, `concat`, `trim`, `ltrim`, `rtrim`, `btrim`, `split_part`, `initcap`, `reverse`, `strpos`, `lpad`, `rpad` |
|
|
1648
|
-
| **Number** | `abs`, `ceil`, `floor`, `round`, `sqrt`, `power`, `mod`, `gcd`, `lcm`, `exp`, `ln`, `log`, `log10`, `log1p`, `pi`, `sign`, `width_bucket`, `trunc`, `greatest`, `least` |
|
|
1649
|
-
| **Date/Time** | `now`, `age`, `clock_timestamp`, `date_part`, `date_trunc`, `extract`, `isfinite`, `justify_days`, `justify_hours`, `make_date`, `make_time`, `make_timestamp`, `make_timestamptz`, `timeofday`, `to_timestamp`, `to_char`, `to_date`, `to_time` |
|
|
1650
|
-
| **Utility** | `coalesce` |
|
|
1651
|
-
| **JSON/JSONB** | `jsonb_array_length`, `jsonb_extract_path`, `jsonb_extract_path_text`, `jsonb_object_keys`, `jsonb_build_object`, `json_build_object`, `jsonb_agg`, `json_agg`, `array_agg`, `array_to_json`, `row_to_json` |
|
|
1652
|
-
| **Internal** | `noselect`, `anyvalue` |
|
|
1653
|
-
|
|
1654
|
-
### Postgres Literals (used as raw SQL)
|
|
1655
|
-
|
|
1656
|
-
`current_date`, `current_time`, `current_timestamp`
|
|
1657
|
-
|
|
1658
|
-
---
|
|
1659
|
-
|
|
1660
|
-
## Allowed Aggregates Reference
|
|
1661
|
-
|
|
1662
|
-
Supported aggregate functions:
|
|
1663
|
-
|
|
1664
|
-
| Aggregate | Description |
|
|
1665
|
-
|---|---|
|
|
1666
|
-
| `count` | Count of rows/values |
|
|
1667
|
-
| `sum` | Sum of values |
|
|
1668
|
-
| `avg` | Average of values |
|
|
1669
|
-
| `min` | Minimum value |
|
|
1670
|
-
| `max` | Maximum value |
|
|
1671
|
-
| `jsonb_agg` | Aggregate values as JSONB array |
|
|
1672
|
-
| `json_agg` | Aggregate values as JSON array |
|
|
1673
|
-
| `array_agg` | Aggregate values as PostgreSQL array |
|
|
1674
|
-
|
|
1675
|
-
---
|
|
1676
|
-
|
|
1677
|
-
## Allowed Cast Types
|
|
1678
|
-
|
|
1679
|
-
Valid types for the `cast` property in block formulas and `numberType` in calculations:
|
|
1680
|
-
|
|
1681
|
-
`int`, `int[]`, `int2`, `int2[]`, `int4`, `int4[]`, `int8`, `int8[]`, `bigint`, `bigint[]`, `real`, `real[]`, `float`, `float[]`, `float4`, `float4[]`, `float8`, `float8[]`, `numeric`, `numeric[]`, `double`, `double[]`, `decimal`, `decimal[]`, `money`, `money[]`, `timestamp`, `timestamp[]`, `timestamptz`, `timestamptz[]`, `date`, `date[]`, `time`, `time[]`, `interval`, `interval[]`, `bool`, `bool[]`, `boolean`, `boolean[]`, `uuid`, `uuid[]`, `text`, `text[]`
|
|
1682
|
-
|
|
1683
|
-
---
|
|
1684
|
-
|
|
1685
|
-
## Complete Examples
|
|
1686
|
-
|
|
1687
|
-
### Example 1: Full-Featured Select Query
|
|
1688
|
-
|
|
1689
|
-
Fetch tasks with filters, sorting, pagination, and relation expansion:
|
|
1690
|
-
|
|
1691
|
-
```json
|
|
1692
|
-
{
|
|
1693
|
-
"dataSourceFullSlug": "crm_task",
|
|
1694
|
-
"columns": "id, task_name, ...record_owner(owner_name:name, owner_email:email), ...related_account(account_name:name)",
|
|
1695
|
-
"filters": {
|
|
1696
|
-
"combinator": "and",
|
|
1697
|
-
"rules": [
|
|
1698
|
-
{ "field": "task_status", "operator": "in", "value": [1, 2] },
|
|
1699
|
-
{ "field": "due_date", "operator": "in_next_x_days", "value": 7 },
|
|
1700
|
-
{ "field": "record_owner", "operator": "in_active_user_team" }
|
|
1701
|
-
]
|
|
1702
|
-
},
|
|
1703
|
-
"orderBy": "due_date ASC, task_name ASC",
|
|
1704
|
-
"limit": 50,
|
|
1705
|
-
"offset": 0,
|
|
1706
|
-
"fullCount": true
|
|
1707
|
-
}
|
|
1708
|
-
```
|
|
1709
|
-
|
|
1710
|
-
### Example 2: Aggregation Dashboard
|
|
1711
|
-
|
|
1712
|
-
Monthly sales report grouped by category:
|
|
1713
|
-
|
|
1714
|
-
```json
|
|
1715
|
-
{
|
|
1716
|
-
"dataSourceFullSlug": "shop_order",
|
|
1717
|
-
"columns": "months_of_year@created_on, ...category(cat:name)",
|
|
1718
|
-
"calculations": [
|
|
1719
|
-
{ "field": "id", "func": "count", "name": "order_count" },
|
|
1720
|
-
{ "field": "total_amount", "func": "sum", "name": "revenue" },
|
|
1721
|
-
{ "field": "total_amount", "func": "avg", "name": "avg_order" }
|
|
1722
|
-
],
|
|
1723
|
-
"filters": {
|
|
1724
|
-
"rules": [
|
|
1725
|
-
{ "field": "created_on", "operator": "this_year" },
|
|
1726
|
-
{ "field": "order_status", "operator": "!=", "value": "cancelled" }
|
|
1727
|
-
]
|
|
1728
|
-
},
|
|
1729
|
-
"orderBy": "months_of_year@created_on ASC"
|
|
1730
|
-
}
|
|
1731
|
-
```
|
|
1732
|
-
|
|
1733
|
-
### Example 3: Pivot — Weekly Sales by Salesperson
|
|
1734
|
-
|
|
1735
|
-
```json
|
|
1736
|
-
{
|
|
1737
|
-
"dataSourceFullSlug": "shop_order",
|
|
1738
|
-
"columns": "...order_status(status_name:name)",
|
|
1739
|
-
"pivot": {
|
|
1740
|
-
"matrix": [
|
|
1741
|
-
{
|
|
1742
|
-
"using": "created_on",
|
|
1743
|
-
"columns": "week:to_char[IYYY-IW]@created_on",
|
|
1744
|
-
"dateRange": {
|
|
1745
|
-
"interval": "week",
|
|
1746
|
-
"min": "2025-01-01T00:00:00Z",
|
|
1747
|
-
"max": "2025-03-31T23:59:59Z"
|
|
1748
|
-
},
|
|
1749
|
-
"spread": true
|
|
1750
|
-
},
|
|
1751
|
-
{
|
|
1752
|
-
"using": "salesperson",
|
|
1753
|
-
"columns": "sp_name:name",
|
|
1754
|
-
"spread": true
|
|
1755
|
-
}
|
|
1756
|
-
],
|
|
1757
|
-
"orderBy": "week ASC"
|
|
1758
|
-
},
|
|
1759
|
-
"calculations": [
|
|
1760
|
-
{ "field": "id", "func": "count", "name": "deals" },
|
|
1761
|
-
{ "field": "amount", "func": "sum", "name": "revenue" }
|
|
1762
|
-
]
|
|
1763
|
-
}
|
|
1764
|
-
```
|
|
1765
|
-
|
|
1766
|
-
### Example 4: Child Queries — Customers with Orders and Tickets
|
|
1767
|
-
|
|
1768
|
-
```json
|
|
1769
|
-
{
|
|
1770
|
-
"dataSourceFullSlug": "crm_customer",
|
|
1771
|
-
"columns": "id, name, email, recent_orders, open_tickets",
|
|
1772
|
-
"childQueries": {
|
|
1773
|
-
"recent_orders": {
|
|
1774
|
-
"from": "shop_order",
|
|
1775
|
-
"using": "customer",
|
|
1776
|
-
"columns": "id, order_date, total_amount, ...status(status_label:name)",
|
|
1777
|
-
"orderBy": "order_date DESC",
|
|
1778
|
-
"limit": 10,
|
|
1779
|
-
"filters": {
|
|
1780
|
-
"rules": [
|
|
1781
|
-
{ "field": "order_date", "operator": "last_90_days" }
|
|
1782
|
-
]
|
|
1783
|
-
}
|
|
1784
|
-
},
|
|
1785
|
-
"open_tickets": {
|
|
1786
|
-
"from": "support_ticket",
|
|
1787
|
-
"using": "customer",
|
|
1788
|
-
"columns": "id, subject, priority, created_on",
|
|
1789
|
-
"orderBy": "created_on DESC",
|
|
1790
|
-
"limit": 5,
|
|
1791
|
-
"filters": {
|
|
1792
|
-
"rules": [
|
|
1793
|
-
{ "field": "status", "operator": "!=", "value": "closed" }
|
|
1794
|
-
]
|
|
1795
|
-
}
|
|
1796
|
-
}
|
|
1797
|
-
},
|
|
1798
|
-
"filters": {
|
|
1799
|
-
"rules": [
|
|
1800
|
-
{ "field": "status", "operator": "=", "value": "active" }
|
|
1801
|
-
]
|
|
1802
|
-
},
|
|
1803
|
-
"limit": 25
|
|
1804
|
-
}
|
|
1805
|
-
```
|
|
1806
|
-
|
|
1807
|
-
### Example 5: Formulas — Computed Columns with Subquery
|
|
1808
|
-
|
|
1809
|
-
Fetch accounts with an inline profit margin formula and a subquery counting active deals:
|
|
1810
|
-
|
|
1811
|
-
```json
|
|
1812
|
-
{
|
|
1813
|
-
"dataSourceFullSlug": "crm_account",
|
|
1814
|
-
"columns": "id, name, profit_margin, active_deals",
|
|
1815
|
-
"formulas": {
|
|
1816
|
-
"profit_margin": {
|
|
1817
|
-
"inputs": [{
|
|
1818
|
-
"kind": "math",
|
|
1819
|
-
"op": "*",
|
|
1820
|
-
"inputs": [
|
|
1821
|
-
{
|
|
1822
|
-
"kind": "math",
|
|
1823
|
-
"op": "/",
|
|
1824
|
-
"inputs": [
|
|
1825
|
-
{
|
|
1826
|
-
"kind": "math",
|
|
1827
|
-
"op": "-",
|
|
1828
|
-
"inputs": [
|
|
1829
|
-
{ "kind": "column", "name": "revenue" },
|
|
1830
|
-
{ "kind": "column", "name": "cost" }
|
|
1831
|
-
]
|
|
1832
|
-
},
|
|
1833
|
-
{ "kind": "column", "name": "revenue", "cast": "decimal" }
|
|
1834
|
-
]
|
|
1835
|
-
},
|
|
1836
|
-
{ "kind": "literal", "literal": 100 }
|
|
1837
|
-
]
|
|
1838
|
-
}]
|
|
1839
|
-
},
|
|
1840
|
-
"active_deals": {
|
|
1841
|
-
"from": "crm_deal",
|
|
1842
|
-
"with": "account",
|
|
1843
|
-
"filters": {
|
|
1844
|
-
"rules": [
|
|
1845
|
-
{ "field": "stage", "operator": "!=", "value": "lost" },
|
|
1846
|
-
{ "field": "stage", "operator": "!=", "value": "won" }
|
|
1847
|
-
]
|
|
1848
|
-
},
|
|
1849
|
-
"inputs": [{
|
|
1850
|
-
"kind": "aggregate",
|
|
1851
|
-
"name": "count",
|
|
1852
|
-
"inputs": []
|
|
1853
|
-
}]
|
|
1854
|
-
}
|
|
1855
|
-
},
|
|
1856
|
-
"orderBy": "profit_margin DESC",
|
|
1857
|
-
"limit": 20
|
|
1858
|
-
}
|
|
1859
|
-
```
|
|
1860
|
-
|
|
1861
|
-
### Example 6: Combined Pivot + Calculations + Filters
|
|
1862
|
-
|
|
1863
|
-
Daily hourly breakdown of support tickets per agent for today:
|
|
1864
|
-
|
|
1865
|
-
```json
|
|
1866
|
-
{
|
|
1867
|
-
"dataSourceFullSlug": "support_ticket",
|
|
1868
|
-
"columns": "...priority(priority_name:name)",
|
|
1869
|
-
"pivot": {
|
|
1870
|
-
"matrix": [
|
|
1871
|
-
{
|
|
1872
|
-
"using": "created_on",
|
|
1873
|
-
"columns": "hour:hours_of_today@created_on",
|
|
1874
|
-
"dateRange": {
|
|
1875
|
-
"interval": "hour",
|
|
1876
|
-
"min": "2025-10-15T00:00:00Z",
|
|
1877
|
-
"max": "2025-10-15T23:59:59Z"
|
|
1878
|
-
},
|
|
1879
|
-
"spread": true
|
|
1880
|
-
},
|
|
1881
|
-
{
|
|
1882
|
-
"using": "assigned_agent",
|
|
1883
|
-
"columns": "agent:name",
|
|
1884
|
-
"spread": true,
|
|
1885
|
-
"filters": {
|
|
1886
|
-
"rules": [
|
|
1887
|
-
{ "field": "is_active", "operator": "true" }
|
|
1888
|
-
]
|
|
1889
|
-
}
|
|
1890
|
-
}
|
|
1891
|
-
],
|
|
1892
|
-
"hideEmptyRows": false
|
|
1893
|
-
},
|
|
1894
|
-
"calculations": [
|
|
1895
|
-
{ "field": "id", "func": "count", "name": "ticket_count" }
|
|
1896
|
-
],
|
|
1897
|
-
"filters": {
|
|
1898
|
-
"rules": [
|
|
1899
|
-
{ "field": "created_on", "operator": "today" }
|
|
1900
|
-
]
|
|
1901
|
-
}
|
|
1902
|
-
}
|
|
1903
|
-
```
|
|
1904
|
-
|
|
1905
|
-
### Example 7: Complex Nested Filters
|
|
1906
|
-
|
|
1907
|
-
```json
|
|
1908
|
-
{
|
|
1909
|
-
"dataSourceFullSlug": "crm_deal",
|
|
1910
|
-
"columns": "id, name, amount, stage, record_owner(name)",
|
|
1911
|
-
"filters": {
|
|
1912
|
-
"combinator": "and",
|
|
1913
|
-
"rules": [
|
|
1914
|
-
{
|
|
1915
|
-
"field": "amount",
|
|
1916
|
-
"operator": ">",
|
|
1917
|
-
"value": 10000
|
|
1918
|
-
},
|
|
1919
|
-
{
|
|
1920
|
-
"combinator": "or",
|
|
1921
|
-
"rules": [
|
|
1922
|
-
{
|
|
1923
|
-
"combinator": "and",
|
|
1924
|
-
"rules": [
|
|
1925
|
-
{ "field": "stage", "operator": "=", "value": "negotiation" },
|
|
1926
|
-
{ "field": "created_on", "operator": "this_month" }
|
|
1927
|
-
]
|
|
1928
|
-
},
|
|
1929
|
-
{
|
|
1930
|
-
"combinator": "and",
|
|
1931
|
-
"rules": [
|
|
1932
|
-
{ "field": "stage", "operator": "=", "value": "proposal" },
|
|
1933
|
-
{ "field": "record_owner", "operator": "active_user" }
|
|
1934
|
-
]
|
|
1935
|
-
}
|
|
1936
|
-
]
|
|
1937
|
-
},
|
|
1938
|
-
{
|
|
1939
|
-
"field": "rel_account/industry",
|
|
1940
|
-
"operator": "in",
|
|
1941
|
-
"value": ["technology", "finance", "healthcare"]
|
|
1942
|
-
}
|
|
1943
|
-
]
|
|
1944
|
-
},
|
|
1945
|
-
"orderBy": "amount DESC",
|
|
1946
|
-
"limit": 100
|
|
1947
|
-
}
|
|
1948
|
-
```
|
|
1949
|
-
|
|
1950
|
-
### Example 8: CASE Formula with Multiple Conditions
|
|
1951
|
-
|
|
1952
|
-
```json
|
|
1953
|
-
{
|
|
1954
|
-
"dataSourceFullSlug": "crm_deal",
|
|
1955
|
-
"columns": "id, name, amount, deal_tier",
|
|
1956
|
-
"formulas": {
|
|
1957
|
-
"deal_tier": {
|
|
1958
|
-
"inputs": [{
|
|
1959
|
-
"kind": "case",
|
|
1960
|
-
"cases": [
|
|
1961
|
-
{
|
|
1962
|
-
"when": {
|
|
1963
|
-
"kind": "compare", "op": ">=",
|
|
1964
|
-
"left": { "kind": "column", "name": "amount" },
|
|
1965
|
-
"right": { "kind": "literal", "literal": 100000 }
|
|
1966
|
-
},
|
|
1967
|
-
"then": { "kind": "literal", "literal": "Enterprise" }
|
|
1968
|
-
},
|
|
1969
|
-
{
|
|
1970
|
-
"when": {
|
|
1971
|
-
"kind": "compare", "op": ">=",
|
|
1972
|
-
"left": { "kind": "column", "name": "amount" },
|
|
1973
|
-
"right": { "kind": "literal", "literal": 25000 }
|
|
1974
|
-
},
|
|
1975
|
-
"then": { "kind": "literal", "literal": "Mid-Market" }
|
|
1976
|
-
},
|
|
1977
|
-
{
|
|
1978
|
-
"when": {
|
|
1979
|
-
"kind": "compare", "op": ">=",
|
|
1980
|
-
"left": { "kind": "column", "name": "amount" },
|
|
1981
|
-
"right": { "kind": "literal", "literal": 5000 }
|
|
1982
|
-
},
|
|
1983
|
-
"then": { "kind": "literal", "literal": "SMB" }
|
|
1984
|
-
}
|
|
1985
|
-
],
|
|
1986
|
-
"else": { "kind": "literal", "literal": "Micro" }
|
|
1987
|
-
}]
|
|
1988
|
-
}
|
|
1989
|
-
}
|
|
1990
|
-
}
|
|
1991
|
-
```
|
|
1992
|
-
|
|
1993
|
-
### Example 9: Date Formatting with Block Formula
|
|
1994
|
-
|
|
1995
|
-
```json
|
|
1996
|
-
{
|
|
1997
|
-
"dataSourceFullSlug": "crm_activity",
|
|
1998
|
-
"columns": "id, subject, formatted_date, formatted_time",
|
|
1999
|
-
"formulas": {
|
|
2000
|
-
"formatted_date": {
|
|
2001
|
-
"inputs": [{
|
|
2002
|
-
"kind": "function",
|
|
2003
|
-
"name": "to_char",
|
|
2004
|
-
"inputs": [
|
|
2005
|
-
{ "kind": "column", "name": "created_on" },
|
|
2006
|
-
{ "kind": "literal", "literal": "DD Mon YYYY" }
|
|
2007
|
-
]
|
|
2008
|
-
}]
|
|
2009
|
-
},
|
|
2010
|
-
"formatted_time": {
|
|
2011
|
-
"inputs": [{
|
|
2012
|
-
"kind": "function",
|
|
2013
|
-
"name": "to_char",
|
|
2014
|
-
"inputs": [
|
|
2015
|
-
{ "kind": "column", "name": "created_on" },
|
|
2016
|
-
{ "kind": "literal", "literal": "HH24:MI" }
|
|
2017
|
-
]
|
|
2018
|
-
}]
|
|
2019
|
-
}
|
|
2020
|
-
},
|
|
2021
|
-
"orderBy": "created_on DESC",
|
|
2022
|
-
"limit": 50
|
|
2023
|
-
}
|
|
2024
|
-
```
|
|
2025
|
-
|
|
2026
|
-
### Example 10: Distinct Count with Min/Max Bounds
|
|
2027
|
-
|
|
2028
|
-
```json
|
|
2029
|
-
{
|
|
2030
|
-
"dataSourceFullSlug": "shop_order",
|
|
2031
|
-
"columns": "category",
|
|
2032
|
-
"calculations": [
|
|
2033
|
-
{
|
|
2034
|
-
"field": "id",
|
|
2035
|
-
"func": "count",
|
|
2036
|
-
"name": "total_orders"
|
|
2037
|
-
},
|
|
2038
|
-
{
|
|
2039
|
-
"field": "amount",
|
|
2040
|
-
"func": "sum",
|
|
2041
|
-
"name": "valid_revenue",
|
|
2042
|
-
"minValue": 0,
|
|
2043
|
-
"maxValue": 1000000
|
|
2044
|
-
},
|
|
2045
|
-
{
|
|
2046
|
-
"field": "amount",
|
|
2047
|
-
"func": "avg",
|
|
2048
|
-
"name": "avg_amount",
|
|
2049
|
-
"numberType": "decimal"
|
|
2050
|
-
},
|
|
2051
|
-
{
|
|
2052
|
-
"field": "product_code",
|
|
2053
|
-
"func": "count",
|
|
2054
|
-
"name": "unique_products",
|
|
2055
|
-
"isDistinct": true
|
|
2056
|
-
}
|
|
2057
|
-
]
|
|
2058
|
-
}
|
|
2059
|
-
```
|