@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.
Files changed (111) hide show
  1. package/agent-loader.js +37 -3
  2. package/agent-loader.js.map +2 -2
  3. package/main.js +498 -93
  4. package/main.js.map +4 -4
  5. package/package.json +14 -4
  6. package/resources/chrome-tools/browser-content.js +103 -0
  7. package/resources/chrome-tools/browser-cookies.js +35 -0
  8. package/resources/chrome-tools/browser-eval.js +53 -0
  9. package/resources/chrome-tools/browser-hn-scraper.js +108 -0
  10. package/resources/chrome-tools/browser-nav.js +44 -0
  11. package/resources/chrome-tools/browser-pick.js +162 -0
  12. package/resources/chrome-tools/browser-screenshot.js +34 -0
  13. package/resources/chrome-tools/browser-start.js +86 -0
  14. package/resources/pi-agent/extensions/answer.ts +532 -0
  15. package/resources/pi-agent/extensions/context.ts +578 -0
  16. package/resources/pi-agent/extensions/control.ts +1779 -0
  17. package/resources/pi-agent/extensions/diff.ts +218 -0
  18. package/resources/pi-agent/extensions/files.ts +199 -0
  19. package/resources/pi-agent/extensions/loop.ts +446 -0
  20. package/resources/pi-agent/extensions/multi-edit.ts +835 -0
  21. package/resources/pi-agent/extensions/notify.ts +88 -0
  22. package/resources/pi-agent/extensions/pi-mcp-adapter/CHANGELOG.md +192 -0
  23. package/resources/pi-agent/extensions/pi-mcp-adapter/LICENSE +21 -0
  24. package/resources/pi-agent/extensions/pi-mcp-adapter/README.md +296 -0
  25. package/resources/pi-agent/extensions/pi-mcp-adapter/app-bridge.bundle.js +67 -0
  26. package/resources/pi-agent/extensions/pi-mcp-adapter/cli.js +108 -0
  27. package/resources/pi-agent/extensions/pi-mcp-adapter/commands.ts +211 -0
  28. package/resources/pi-agent/extensions/pi-mcp-adapter/config.ts +227 -0
  29. package/resources/pi-agent/extensions/pi-mcp-adapter/consent-manager.ts +64 -0
  30. package/resources/pi-agent/extensions/pi-mcp-adapter/direct-tools.ts +301 -0
  31. package/resources/pi-agent/extensions/pi-mcp-adapter/errors.ts +219 -0
  32. package/resources/pi-agent/extensions/pi-mcp-adapter/glimpse-ui.ts +80 -0
  33. package/resources/pi-agent/extensions/pi-mcp-adapter/host-html-template.ts +427 -0
  34. package/resources/pi-agent/extensions/pi-mcp-adapter/index.ts +232 -0
  35. package/resources/pi-agent/extensions/pi-mcp-adapter/init.ts +319 -0
  36. package/resources/pi-agent/extensions/pi-mcp-adapter/lifecycle.ts +93 -0
  37. package/resources/pi-agent/extensions/pi-mcp-adapter/logger.ts +169 -0
  38. package/resources/pi-agent/extensions/pi-mcp-adapter/mcp-panel.ts +713 -0
  39. package/resources/pi-agent/extensions/pi-mcp-adapter/metadata-cache.ts +191 -0
  40. package/resources/pi-agent/extensions/pi-mcp-adapter/npx-resolver.ts +419 -0
  41. package/resources/pi-agent/extensions/pi-mcp-adapter/oauth-handler.ts +56 -0
  42. package/resources/pi-agent/extensions/pi-mcp-adapter/package.json +85 -0
  43. package/resources/pi-agent/extensions/pi-mcp-adapter/paths.ts +29 -0
  44. package/resources/pi-agent/extensions/pi-mcp-adapter/proxy-modes.ts +635 -0
  45. package/resources/pi-agent/extensions/pi-mcp-adapter/resource-tools.ts +17 -0
  46. package/resources/pi-agent/extensions/pi-mcp-adapter/server-manager.ts +330 -0
  47. package/resources/pi-agent/extensions/pi-mcp-adapter/state.ts +41 -0
  48. package/resources/pi-agent/extensions/pi-mcp-adapter/tool-metadata.ts +144 -0
  49. package/resources/pi-agent/extensions/pi-mcp-adapter/tool-registrar.ts +46 -0
  50. package/resources/pi-agent/extensions/pi-mcp-adapter/types.ts +367 -0
  51. package/resources/pi-agent/extensions/pi-mcp-adapter/ui-resource-handler.ts +145 -0
  52. package/resources/pi-agent/extensions/pi-mcp-adapter/ui-server.ts +623 -0
  53. package/resources/pi-agent/extensions/pi-mcp-adapter/ui-session.ts +384 -0
  54. package/resources/pi-agent/extensions/pi-mcp-adapter/ui-stream-types.ts +89 -0
  55. package/resources/pi-agent/extensions/pi-mcp-adapter/utils.ts +75 -0
  56. package/resources/pi-agent/extensions/prompt-editor.ts +1315 -0
  57. package/resources/pi-agent/extensions/prompt-url-widget.ts +158 -0
  58. package/resources/pi-agent/extensions/redraws.ts +24 -0
  59. package/resources/pi-agent/extensions/review.ts +2160 -0
  60. package/resources/pi-agent/extensions/todos.ts +2076 -0
  61. package/resources/pi-agent/extensions/tps.ts +47 -0
  62. package/resources/pi-agent/extensions/whimsical.ts +474 -0
  63. package/resources/pi-agent/prompts/coder-system.md +106 -0
  64. package/resources/pi-agent/skills/changelog-generator/SKILL.md +425 -0
  65. package/resources/pi-agent/skills/docyrus-chrome-devtools-cli/SKILL.md +80 -0
  66. package/resources/pi-agent/skills/docyrus-platform/SKILL.md +71 -0
  67. package/resources/pi-agent/skills/docyrus-platform/references/ai-capabilities.md +43 -0
  68. package/resources/pi-agent/skills/docyrus-platform/references/auth-and-multi-tenancy.md +35 -0
  69. package/resources/pi-agent/skills/docyrus-platform/references/automation-and-workflows.md +30 -0
  70. package/resources/pi-agent/skills/docyrus-platform/references/core-building-blocks.md +53 -0
  71. package/resources/pi-agent/skills/{docyrus-api-dev → docyrus-platform}/references/data-source-query-guide.md +32 -28
  72. package/resources/pi-agent/skills/docyrus-platform/references/developer-tools.md +28 -0
  73. package/resources/pi-agent/skills/docyrus-platform/references/docyrus-cli-usage.md +554 -0
  74. package/resources/pi-agent/skills/{docyrus-api-dev → docyrus-platform}/references/formula-design-guide-llm.md +15 -23
  75. package/resources/pi-agent/skills/docyrus-platform/references/integrations-and-events.md +60 -0
  76. package/resources/pi-agent/skills/docyrus-platform/references/platform-services.md +58 -0
  77. package/resources/pi-agent/skills/docyrus-platform/references/querying-and-data-operations.md +27 -0
  78. package/resources/pi-agent/prompts/coder-append-system.md +0 -19
  79. package/resources/pi-agent/skills/docyrus-ai/SKILL.md +0 -28
  80. package/resources/pi-agent/skills/docyrus-api-dev/SKILL.md +0 -161
  81. package/resources/pi-agent/skills/docyrus-api-dev/references/api-client.md +0 -349
  82. package/resources/pi-agent/skills/docyrus-api-dev/references/authentication.md +0 -238
  83. package/resources/pi-agent/skills/docyrus-api-dev/references/query-and-formulas.md +0 -592
  84. package/resources/pi-agent/skills/docyrus-api-doctor/SKILL.md +0 -70
  85. package/resources/pi-agent/skills/docyrus-api-doctor/references/checklist-details.md +0 -588
  86. package/resources/pi-agent/skills/docyrus-app-dev/SKILL.md +0 -159
  87. package/resources/pi-agent/skills/docyrus-app-dev/references/api-client-and-auth.md +0 -275
  88. package/resources/pi-agent/skills/docyrus-app-dev/references/collections-and-patterns.md +0 -352
  89. package/resources/pi-agent/skills/docyrus-app-dev/references/data-source-query-guide.md +0 -2059
  90. package/resources/pi-agent/skills/docyrus-app-dev/references/formula-design-guide-llm.md +0 -320
  91. package/resources/pi-agent/skills/docyrus-app-dev/references/query-guide.md +0 -525
  92. package/resources/pi-agent/skills/docyrus-app-ui-design/SKILL.md +0 -466
  93. package/resources/pi-agent/skills/docyrus-app-ui-design/references/component-selection-guide.md +0 -602
  94. package/resources/pi-agent/skills/docyrus-app-ui-design/references/icon-usage-guide.md +0 -463
  95. package/resources/pi-agent/skills/docyrus-app-ui-design/references/preferred-components-catalog.md +0 -242
  96. package/resources/pi-agent/skills/docyrus-apps/SKILL.md +0 -54
  97. package/resources/pi-agent/skills/docyrus-architect/SKILL.md +0 -174
  98. package/resources/pi-agent/skills/docyrus-architect/references/custom-query-guide.md +0 -410
  99. package/resources/pi-agent/skills/docyrus-architect/references/data-source-query-guide.md +0 -2059
  100. package/resources/pi-agent/skills/docyrus-architect/references/formula-design-guide-llm.md +0 -320
  101. package/resources/pi-agent/skills/docyrus-architect/references/formula-reference.md +0 -145
  102. package/resources/pi-agent/skills/docyrus-auth/SKILL.md +0 -100
  103. package/resources/pi-agent/skills/docyrus-cli-app/SKILL.md +0 -279
  104. package/resources/pi-agent/skills/docyrus-cli-app/references/cli-manifest.md +0 -532
  105. package/resources/pi-agent/skills/docyrus-cli-app/references/list-query-examples.md +0 -248
  106. package/resources/pi-agent/skills/docyrus-curl/SKILL.md +0 -32
  107. package/resources/pi-agent/skills/docyrus-discover/SKILL.md +0 -63
  108. package/resources/pi-agent/skills/docyrus-ds/SKILL.md +0 -95
  109. package/resources/pi-agent/skills/docyrus-env/SKILL.md +0 -21
  110. package/resources/pi-agent/skills/docyrus-studio/SKILL.md +0 -369
  111. 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
- ```