@graphenedata/cli 0.0.10 → 0.0.12

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.
@@ -8,6 +8,7 @@ Graphene also has a CLI that lets you check syntax, run queries, serve data apps
8
8
 
9
9
  **Table of Contents**
10
10
 
11
+ - [Graphene CLI](#graphene-cli)
11
12
  - [Graphene SQL (GSQL)](#graphene-sql-gsql)
12
13
  - [`table` statements](#table-statements)
13
14
  - [Base columns (required)](#base-columns-required)
@@ -18,14 +19,19 @@ Graphene also has a CLI that lets you check syntax, run queries, serve data apps
18
19
  - [`select` statements](#select-statements)
19
20
  - [Using join relationships in queries](#using-join-relationships-in-queries)
20
21
  - [Multi-hop joins](#multi-hop-joins)
21
- - [Using stored expressions in queries](#using-stored-expressions-in-queries)
22
+ - [Using scalar stored expressions (dimensions) in queries](#using-scalar-stored-expressions-dimensions-in-queries)
23
+ - [Using aggregative stored expressions (measures) in queries](#using-aggregative-stored-expressions-measures-in-queries)
22
24
  - [Safe aggregation in fan-outs](#safe-aggregation-in-fan-outs)
25
+ - [Available functions](#available-functions)
26
+ - [Aggregate functions](#aggregate-functions)
27
+ - [Date and time functions](#date-and-time-functions)
28
+ - [Conditional Functions](#conditional-functions)
29
+ - [Math Functions](#math-functions)
30
+ - [Type Conversion](#type-conversion)
31
+ - [Subqueries, CTEs, and chaining queries](#subqueries-ctes-and-chaining-queries)
32
+ - [Other miscellaneous details](#other-miscellaneous-details)
23
33
  - [`table as` statements](#table-as-statements)
24
34
  - [`extend` statements](#extend-statements)
25
- - [Working with dates, timestamps, and intervals](#working-with-dates-timestamps-and-intervals)
26
- - [Date and timestamp literals](#date-and-timestamp-literals)
27
- - [Interval literals](#interval-literals)
28
- - [Other miscellaneous details about GSQL](#other-miscellaneous-details-about-gsql)
29
35
  - [Graphene data apps (dashboards)](#graphene-data-apps-dashboards)
30
36
  - [Visualization components](#visualization-components)
31
37
  - [Bar chart](#bar-chart)
@@ -68,8 +74,6 @@ Graphene also has a CLI that lets you check syntax, run queries, serve data apps
68
74
  - [Input components](#input-components)
69
75
  - [Text input](#text-input)
70
76
  - [All text input attributes](#all-text-input-attributes)
71
- - [Date range](#date-range)
72
- - [All date range attributes](#all-date-range-attributes)
73
77
  - [Dropdown](#dropdown)
74
78
  - [All dropdown attributes](#all-dropdown-attributes)
75
79
  - [DropdownOption](#dropdownoption)
@@ -81,16 +85,27 @@ Graphene also has a CLI that lets you check syntax, run queries, serve data apps
81
85
  - [Currencies](#currencies)
82
86
  - [Numbers](#numbers)
83
87
  - [Percentages](#percentages)
84
- - [Graphene CLI](#graphene-cli)
85
- - [AGENT INSTRUCTIONS](#agent-instructions)
86
88
 
87
- ## Graphene SQL (GSQL)
89
+ # Graphene CLI
90
+
91
+ These are the available commands:
92
+ - `npm run graphene check` - Checks the syntax (GSQL and Markdown) for the entire Graphene project.
93
+ - `npm run graphene check [mdPath]` - Checks the syntax for a specified Graphene markdown file. Will also do a runtime check if the dev server is running, and if successful, take a full page screenshot to a temp directory for the agent to view.
94
+ - `npm run graphene check [mdPath] -c [chartTitle]` - Same as above, except if the runtime check is successful, only takes a screenshot of the specified chart. `[chartTitle]` must match (case sensitive) the `title` attribute on the chart component.
95
+ - `npm run graphene compile [GSQL | gsqlPath]` - Shows how GSQL is translated into the underlying database SQL.
96
+ - `npm run graphene run [GSQL | gsqlPath]` - Runs a GSQL query. The tables and semantics defined in all .gsql files in the project are available for the query to use.
97
+
98
+ # Graphene SQL (GSQL)
88
99
 
89
- GSQL is comprised of `table` statements that declare tables and `select` statements that query them.
100
+ GSQL is comprised of four primary statements:
101
+ 1. `table` statements that declare existing tables and semantic metadata
102
+ 2. `select` statements that query tables
103
+ 3. `table as` statements that create new tables using `select`
104
+ 4. `extend` statements that attach semantic metadata to tables
90
105
 
91
- ### `table` statements
106
+ ## `table` statements
92
107
 
93
- `table` statements manifest tables that already exist in your database. Here's an example of two tables, `orders` and `users`, in GSQL.
108
+ `table` statements declare tables that already exist in your database. Here's an example of two tables, `orders` and `users`, in GSQL.
94
109
 
95
110
  ```sql
96
111
  table orders (
@@ -123,25 +138,25 @@ table users (
123
138
 
124
139
  We can break down a table statement into three parts: [base columns](#base-columns-required), [join relationships](#join-relationships), and [stored expressions](#stored-expressions) (aka dimensions and measures).
125
140
 
126
- #### Base columns (required)
141
+ ### Base columns (required)
127
142
 
128
143
  The base column set is simply a reflection of the underlying database table's schema. Similar to `create table` statements in regular SQL DDL, you list each column's name and data type. One column must be designated as the primary key.
129
144
 
130
- #### Join relationships
145
+ ### Join relationships
131
146
 
132
147
  Join relationships in a `table` statement declare joins that can be used when querying them. This makes query writing easier and more foolproof. See [Using join relationships in queries](#using-join-relationships-in-queries) below for how to use modeled joins in queries.
133
148
 
134
149
  The other main difference about joins in GSQL vs. regular SQL is that you have to explain if there are many rows in the left table for each row in the right table, or vice versa. This additional bit of information allows Graphene to prevent incorrect aggregation as a result of row duplication (aka fan-out) through joins. See [Safe aggregation in fan-outs](#safe-aggregation-in-fan-outs) for more details.
135
150
 
136
- This information is provided with the two supported join types, `join_one` and `join_many`:
137
- - `join_one` is used if there are many rows in the **left** table for each row in the **right** table.
138
- - `join_many` is used if there are many rows in the **right** table for each row in the **left** table.
151
+ This information is provided with the two supported join types, `join one` and `join many`:
152
+ - `join one` is used if there are many rows in the **left** table for each row in the **right** table.
153
+ - `join many` is used if there are many rows in the **right** table for each row in the **left** table.
139
154
 
140
155
  In the example above with `orders` and `users`, the joins confirm that there are many orders per user, and only one user per order.
141
156
 
142
157
  Note that all joins in GSQL are left outer joins. There is no inner, right, or cross join.
143
158
 
144
- ##### Multiple join relationships between the same two tables
159
+ #### Multiple join relationships between the same two tables
145
160
 
146
161
  Sometimes there are multiple valid ways to join two tables together. You can model this in Graphene by aliasing the various joins with `as`, just as you would in normal SQL. For example:
147
162
 
@@ -164,12 +179,12 @@ table users (
164
179
  )
165
180
  ```
166
181
 
167
- ##### Best practices for modeling join relationships
182
+ #### Best practices for modeling join relationships
168
183
 
169
184
  - For a given `table` statement, only model joins that are directly on that table. Multi-hop join paths do not need to be written explicitly in order for queries to traverse them.
170
185
  - A join between two tables should be modeled in both the respective `table` statements. This may seem redundant but it offers more flexibility for queries to choose which table to set in the `from` (remember that direction matters in queries since all joins are left joins).
171
186
 
172
- #### Stored expressions
187
+ ### Stored expressions
173
188
 
174
189
  **Stored expressions** are GSQL expressions (ie. any arbitrary combination of functions, operators, and column references) that you want to make reusable to queries. Stored expressions are great for canonizing metrics, segments, and other important business definitions.
175
190
 
@@ -194,8 +209,7 @@ table orders (
194
209
  )
195
210
  ```
196
211
 
197
-
198
- ### `select` statements
212
+ ## `select` statements
199
213
 
200
214
  `select` is how you write queries in Graphene SQL. It behaves similarly to regular SQL except in the following ways:
201
215
  - It can invoke join relationships and stored expressions from `table` statements.
@@ -203,7 +217,7 @@ table orders (
203
217
 
204
218
  These differences are described in the sections below.
205
219
 
206
- #### Using join relationships in queries
220
+ ### Using join relationships in queries
207
221
 
208
222
  If a `table` has join relationships declared in it, a `select` query on that table can leverage that join without needing to write its own join statement. This is helpful for query writers who have not memorized all the correct join keys.
209
223
 
@@ -237,7 +251,7 @@ order by 2 desc
237
251
  limit 10
238
252
  ```
239
253
 
240
- ##### Multi-hop joins
254
+ #### Multi-hop joins
241
255
 
242
256
  Sometimes you need to access columns or stored expressions in a table that is two or more joins away from the `from` table. To do this, simply use more dot operators to trace the desired join path. For example, say there is another table added to our project, `countries`:
243
257
 
@@ -278,7 +292,7 @@ order by 2 desc
278
292
  limit 10
279
293
  ```
280
294
 
281
- #### Using stored expressions in queries
295
+ ### Using scalar stored expressions (dimensions) in queries
282
296
 
283
297
  A stored expression can be invoked in a query by simply referencing it by name.
284
298
 
@@ -326,7 +340,9 @@ group by 1
326
340
 
327
341
  You can see that invoking a stored expression is like using a macro: the definition for the stored expression is effectively expanded in-line by Graphene when it runs the query.
328
342
 
329
- This is an important concept to understand when invoking stored expressions that are **aggregative** (ie. contain agg functions). Here's an example.
343
+ ### Using aggregative stored expressions (measures) in queries
344
+
345
+ The macro concept is important to understand when invoking stored expressions that are **aggregative** (ie. contain agg functions), which can also be called "measures." Here's an example.
330
346
 
331
347
  ```sql
332
348
  -- Profit by month
@@ -349,41 +365,219 @@ group by 1
349
365
  order by 1 asc
350
366
  ```
351
367
 
352
- For this reason, in a query you would never wrap an aggregative stored expression in a `sum()` or `avg()` or any other agg function for the same reason you would never write `sum(sum(foo))` in SQL. That would throw an error!
368
+ [CRITICAL!] This means:
369
+ - You would NEVER wrap a measure in an agg function like `sum(my_measure)`, for the same reason that you cannot do `SUM(SUM(foo))` in regular SQL.
370
+ - You would NEVER group by a measure like `group by my_measure`, for the same reason that you cannot do `GROUP BY SUM(foo)` in regular SQL.
371
+ - You CAN wrap a measure in a scalar function like `floor(my_measure)`, for the same reason that can do `FLOOR(SUM(foo))` in regular SQL.
372
+ - You CAN compose measures together in expressions like `my_measure + my_other_measure`, for the same reason that you can do `SUM(foo) + SUM(bar)` in regular SQL.
373
+
374
+ Another way of thinking about this is that measures are "self-aggregating."
375
+
376
+ ### Safe aggregation in fan-outs
377
+
378
+ Aggregations in GSQL are fan-out safe. In the event of a join, Graphene knows which values are duplicated ("fanned out") and will discard the duplicates when calculating aggregates.
379
+
380
+ To do this, Graphene assumes that the column or expression you're aggregating is only dependent on one table. The grain of this table establishes the "correct" grain to compute the aggregate over.
381
+
382
+ >NOTE: Graphene does not support aggregating over expressions that depend on multiple tables (e.g. `sum(table1.col - table2.col)`). To work around this, you can consolidate the expression into a dimension e.g. `my_dim: col - table2.col` and then aggregate over that dimension.
383
+
384
+ Here's an example of a fan-out:
385
+
386
+ ```sql
387
+ table orders (
388
+ id BIGINT primary_key
389
+ customer_name VARCHAR
390
+ amt_with_shipping FLOAT
391
+
392
+ join many order_items on id = order_items.order_id
393
+ )
394
+
395
+ table order_items (
396
+ id BIGINT primary_key
397
+ order_id BIGINT
398
+ product VARCHAR
399
+ price FLOAT
400
+
401
+ join one orders on order_id = orders.id
402
+ )
403
+ ```
404
+
405
+ With the following data:
406
+
407
+ **orders**
408
+
409
+ | id | customer_name | amt_with_shipping |
410
+ |----|---------------|-------------------|
411
+ | 1 | Alice | 110.00 |
412
+ | 2 | Bob | 85.00 |
413
+
414
+ **order_items**
415
+
416
+ | id | order_id | product | price |
417
+ |----|----------|---------|-------|
418
+ | 1 | 1 | Widget | 60.00 |
419
+ | 2 | 1 | Gadget | 40.00 |
420
+ | 3 | 2 | Widget | 60.00 |
421
+ | 4 | 2 | Thingamajig | 15.00 |
353
422
 
354
- #### Safe aggregation in fan-outs
423
+ When joining orders to order_items, the result looks like:
355
424
 
356
- A common and dangerous user error in regular SQL is aggregating data incorrectly after joining tables. This can happen when rows of one table match multiple rows of another, and effectively get duplicated for each match.
425
+ **orders joined to order_items**
426
+
427
+ | orders.id | customer_name | amt_with_shipping | order_items.id | product | price |
428
+ |-----------|---------------|-------------------|----------------|---------|-------|
429
+ | 1 | Alice | 110.00 | 1 | Widget | 60.00 |
430
+ | 1 | Alice | 110.00 | 2 | Gadget | 40.00 |
431
+ | 2 | Bob | 85.00 | 3 | Widget | 60.00 |
432
+ | 2 | Bob | 85.00 | 4 | Thingamajig | 15.00 |
433
+
434
+ Notice that both orders appear twice because they each contain two items. In regular SQL, if you naively computed `sum(amt_with_shipping)` over this joined result, you'd get 390.00 instead of the correct 195.00. In GSQL, because `amt_with_shipping` belongs to the `orders` table, Graphene knows to compute the sum at the `orders` grain, yielding 195.00.
435
+
436
+ Here's how you can use this to simplify your queries. Say you want to see total order value and total item count by customer. In GSQL, you can write this directly:
437
+
438
+ ```sql
439
+ select
440
+ customer_name,
441
+ sum(orders.amt_with_shipping) as total_spent,
442
+ count(order_items.id) as items_purchased
443
+ from orders
444
+ group by customer_name
445
+ ```
357
446
 
358
- For example, after joining `users` to `orders`, your joined result will have some users repeated multiple times if they've made multiple purchases. If you wanted to find the average age of customers over this joined result, simply using an `avg(users.age)` would be _incorrect_, because you would be weighting the average towards users with multiple purchases, rather than taking the true average.
447
+ This returns:
359
448
 
360
- GSQL aims to solve this problem. With the additional information provided via `join_one` and `join_many`, Graphene knows under which scenarios when row dupliation occurs, and will rewrite aggregative expressions in a way that ignores the duplicate rows.
449
+ | customer_name | total_spent | items_purchased |
450
+ |---------------|-------------|-----------------|
451
+ | Alice | 110.00 | 2 |
452
+ | Bob | 85.00 | 2 |
361
453
 
362
- The query `select avg(users.age) from orders` will be rewritten to the following SQL when Graphene queries the underlying database (this is for BigQuery, specifically):
454
+ In regular SQL, you'd need a subquery or CTE to avoid the fan-out problem:
363
455
 
364
456
  ```sql
457
+ WITH order_totals AS (
458
+ SELECT customer_name, SUM(amt_with_shipping) as total_spent
459
+ FROM orders
460
+ GROUP BY customer_name
461
+ ),
462
+ item_counts AS (
463
+ SELECT o.customer_name, COUNT(oi.id) as items_purchased
464
+ FROM orders o
465
+ JOIN order_items oi ON o.id = oi.order_id
466
+ GROUP BY o.customer_name
467
+ )
365
468
  SELECT
366
- (CAST((
367
- (
368
- SUM(DISTINCT
369
- (CAST(ROUND(COALESCE(users_0.`age`,0)*(1*1.0), 9) AS NUMERIC) +
370
- (cast(cast(concat('0x', substr(to_hex(md5(CAST(users_0.`id` AS STRING))), 1, 15)) as int64) as numeric) * 4294967296 + cast(cast(concat('0x', substr(to_hex(md5(CAST(users_0.`id` AS STRING))), 16, 8)) as int64) as numeric)) * 0.000000001
371
- ))
372
- -
373
- SUM(DISTINCT (cast(cast(concat('0x', substr(to_hex(md5(CAST(users_0.`id` AS STRING))), 1, 15)) as int64) as numeric) * 4294967296 + cast(cast(concat('0x', substr(to_hex(md5(CAST(users_0.`id` AS STRING))), 16, 8)) as int64) as numeric)) * 0.000000001)
374
- )/(1*1.0)) AS FLOAT64))/NULLIF(COUNT(DISTINCT CASE WHEN users_0.`age` IS NOT NULL THEN users_0.`id` END),0) as `col_0`
375
- FROM `bigquery-public-data.thelook_ecommerce.orders` as base
376
- LEFT JOIN `bigquery-public-data.thelook_ecommerce.users` AS users_0
377
- ON users_0.`id`=base.`user_id`
469
+ ot.customer_name,
470
+ ot.total_spent,
471
+ ic.items_purchased
472
+ FROM order_totals ot
473
+ JOIN item_counts ic ON ot.customer_name = ic.customer_name
378
474
  ```
379
475
 
380
- You don't have to understand this; the point is that GSQL is minimizing the chances that naive users aggregate data incorrectly.
476
+ ### Available functions
477
+
478
+ Function availability varies depending on the connected database, noted in the tables below. Check your package.json to see what database you are connected to.
479
+
480
+ #### Aggregate functions
481
+
482
+ | Function | Description | Parameters | Return Type | DuckDB | BigQuery | Snowflake |
483
+ | - | - | - | - | - | - | - |
484
+ | count(), count(*) | Counts the number of rows. | - | Number | x | x | x |
485
+ | count(column) | Counts the number of non-null values in a column. | `column` - Any column/expression | Number | x | x | x |
486
+ | count(distinct column) | Counts the number of distinct non-null values in a column. | `column` - Any column/expression | Number | x | x | x |
487
+ | sum(column) | Calculates the sum of numeric values. | `column` - Numeric column/expression | Number | x | x | x |
488
+ | avg(column) | Calculates the average (mean) of numeric values. | `column` - Numeric column/expression | Number | x | x | x |
489
+ | min(column) | Returns the minimum value. | `column` - Any comparable column/expression | Same as input | x | x | x |
490
+ | max(column) | Returns the maximum value. | `column` - Any comparable column/expression | Same as input | x | x | x |
491
+ | string_agg(column) | Concatenates string values. | `column` - String column/expression | String | x | x | x |
492
+ | stddev(column) | Calculates the standard deviation. | `column` - Numeric column/expression | Number | x | x | x |
493
+ | pXX(column) | Returns the XXth percentile (e.g., p50, p975, p9999). | `column` - Numeric column/expression | Number | x | x (≤p99) | x |
494
+
495
+ #### Date and time functions
496
+
497
+ | Function | Description | Parameters | Return Type | DuckDB | BigQuery | Snowflake |
498
+ | - | - | - | - | - | - | - |
499
+ | current_date() | Returns the current date. | - | Date | x | x | x |
500
+ | current_time() | Returns the current time. | - | Timestamp | x | x | x |
501
+ | current_timestamp() | Returns the current timestamp. | - | Timestamp | x | x | x |
502
+ | local_timestamp() | Returns the local timestamp. | - | Timestamp | x | x | x |
503
+ | current_datetime() | Returns the current datetime (BigQuery only). | - | Timestamp | | x | |
504
+ | date_trunc(unit, date) | Truncates date/timestamp to unit (DuckDB). | `unit` - Quoted date part ('year', 'quarter', 'month', 'week', 'day', 'hour', 'minute', 'second') ; `date` - Timestamp | Date or timestamp | x | | |
505
+ | date_trunc(date, unit) | Truncates date/timestamp to unit (BigQuery). | `date` - Date or timestamp ; `unit` - Unquoted date part (year, quarter, month, week, day, hour, minute, second) | Date or timestamp | | x | |
506
+ | extract(unit from timestamp) | Extracts a date part from timestamp. | `unit` - Date part (year, quarter, month, week, day, hour, minute, second, day_of_week, day_of_year) ; `timestamp` - Timestamp/date | Number | x | x | x |
507
+ | timestamp_diff(start, end, unit) | Calculates difference between timestamps (BigQuery). | `start` - Timestamp ; `end` - Timestamp ; `unit` - Keyword (year, quarter, month, week, day, hour, minute, second) | Number | | x | |
508
+
509
+ #### Conditional Functions
510
+
511
+ | Function | Description | Parameters | Return Type | DuckDB | BigQuery | Snowflake |
512
+ | - | - | - | - | - | - | - |
513
+ | if(condition, trueValue, falseValue) | Returns one of two values based on condition. | `condition` - Boolean ; `trueValue` - Any type ; `falseValue` - Same type as trueValue | Same as value args | x | x | |
514
+ | case when ... then ... else ... end | Evaluates conditions and returns values. | Multiple when/then clauses | Varies | x | x | x |
515
+ | coalesce(value1, value2, ...) | Returns first non-null value. | `value1, value2, ...` - Same type | Same as input | x | x | x |
516
+
517
+ #### Math Functions
518
+
519
+ | Function | Description | Parameters | Return Type | DuckDB | BigQuery | Snowflake |
520
+ | - | - | - | - | - | - | - |
521
+ | floor(number) | Rounds down to nearest integer. | `number` - Numeric value | Number | x | x | x |
522
+ | ceil(number) | Rounds up to nearest integer. | `number` - Numeric value | Number | x | x | x |
523
+ | greatest(value1, value2, ...) | Returns the greatest value. | `value1, value2, ...` - Same type | Same as input | x | x | x |
524
+ | least(value1, value2, ...) | Returns the smallest value. | `value1, value2, ...` - Same type | Same as input | x | x | x |
525
+ | safe_divide(numerator, denominator) | Divides, returning null on division by zero (BigQuery). | `numerator` - Number ; `denominator` - Number | Number | | x | |
381
526
 
382
- #### Percentile shorthand
527
+ #### Type Conversion
383
528
 
384
- Graphene provides percentile helpers so you rarely have to remember the SQL form for each warehouse. Anywhere you can call an aggregate, you can also write `pXX(column)` where `XX` is a whole number between 0 and 100. If you need precision finer than a whole percentile, append extra digits—everything after the first two digits is treated as decimals. Examples: `p975` → 97.5th percentile, `p9999` → 99.99th percentile. Graphene rewrites these shorthands to the dialect’s native function (`quantile_cont` on DuckDB, `approx_quantiles` on BigQuery, `PERCENTILE_CONT` on Snowflake) and ensures they behave like other aggregates (automatic grouping, structPath handling, etc.).
529
+ | Function | Description | Parameters | Return Type | DuckDB | BigQuery | Snowflake |
530
+ | - | - | - | - | - | - | - |
531
+ | cast(value as type) | Converts value to specified type. | `value` - Any value ; `type` - Target type (int, int64, integer, bigint, smallint, tinyint, byteint, float, float64, double, decimal, numeric, bigdecimal, number, text, string, varchar, variant, bool, boolean, date, datetime, time, timestamp, timestamp_ntz, geography) | Specified type | x | x | x |
532
+ | value::type | Alternative cast syntax. | `value` - Any value ; `type` - Target type | Specified type | x | x | x |
385
533
 
386
- ### `table as` statements
534
+ ### Subqueries, CTEs, and chaining queries
535
+
536
+ Queries can be chained together for more complex, multi-stage query logic. Instead of using subqueries or CTEs (`WITH`), in GSQL this is done by using the `table as` statement, or in Markdown, by simply chaining queries.
537
+
538
+ Using `table as`:
539
+
540
+ ```sql
541
+ table sales_per_store as (
542
+ select
543
+ store_id,
544
+ sum(amount) as total_sales
545
+ from orders
546
+ group by store_id
547
+ )
548
+
549
+ -- average store sales
550
+ select avg(total_sales)
551
+ from sales_per_store
552
+ ```
553
+
554
+ In a Markdown file:
555
+
556
+ ````md
557
+ ```sql sales_per_store
558
+ select
559
+ store_id,
560
+ sum(amount) as total_sales
561
+ from orders
562
+ group by store_id
563
+ ```
564
+
565
+ ```sql average_store_sales
566
+ select avg(total_sales)
567
+ from sales_per_store
568
+ ```
569
+ ````
570
+
571
+ ### Other miscellaneous details
572
+
573
+ - The clauses in a `select` statement (`select`, `from`, `join`, `group by`, etc.) can be written in any order. They cannot be repeated, however.
574
+ - `group by all` is implied if aggregative and scalar expressions are both present in the `select` clause. This means that `group by` can be omitted and the query will still effectively execute the `group by all`.
575
+ - Expressions in `group by` are implicitly selected, so `from orders select avg(amount) group by user_id` will return two columns.
576
+ - `count` is a reserved word. Do not alias your columns as `count`.
577
+ - Window functions and set operations (`union [all]`, `intersect`, `except`) are not supported.
578
+ - Date and timestamp literals can be written with explicit keywords (`date '2024-01-01'` or `timestamp '2024-01-01 12:00:00'`), but these keywords are optional when the expected type is known from context (e.g., when comparing against a date or timestamp column). Interval literals always require the `interval` keyword.
579
+
580
+ ## `table as` statements
387
581
 
388
582
  You can turn the output of any `select` statement into a table with `table foo as (select ...)`. Here's an example of an additional table `user_facts` added to the two tables from earlier:
389
583
 
@@ -424,11 +618,11 @@ table user_facts as (
424
618
  )
425
619
  ```
426
620
 
427
- `table as` statements are conceptually the same as view tables in regular SQL. A few things to note:
428
- - You cannot yet declare join relationships or stored expressions directly in a `table as` statement. Other tables can declare join relationships to it, though, as shown above.
621
+ `table as` statements are conceptually the same as view tables and CTEs in regular SQL. A few things to note:
622
+ - You cannot declare join relationships or stored expressions directly in a `table as` statement. Use an `extend` statement.
429
623
  - In the example above, the `ltv` and `lifetime_orders` columns from `user_facts` are "hoisted" back into `users` so that they appear as if they are columns from `users`. This is simply a design choice which allows query writers to never need to know about `user_facts`.
430
624
 
431
- ### `extend` statements
625
+ ## `extend` statements
432
626
 
433
627
  `extend` statements allow you to add join relationships or stored expressions to an existing table. This is especially useful for tables created via `table as` statements, which do not support defining these properties directly.
434
628
 
@@ -457,53 +651,7 @@ extend daily_orders (
457
651
 
458
652
  Note that you cannot add new base columns with `extend`; you can only add joins and stored expressions.
459
653
 
460
- ### Working with dates, timestamps, and intervals
461
-
462
- Graphene understands a handful of common literal formats so you rarely need explicit casts when filtering or doing time math.
463
-
464
- **Date and timestamp literals**
465
-
466
- - `YYYY`, `YYYY-MM`, and `YYYY-MM-DD` strings are treated as dates. Leading/trailing spaces are ignored.
467
- - `YYYY-MM-DD HH[:MM[:SS]]` (with either a space or `T` between the date and time) is treated as a timestamp. Missing minutes or seconds default to `00`.
468
-
469
- ```sql
470
- from users select id
471
- where created_at >= '2024-01-01' and created_at <= '2024-02-01'
472
- ```
473
-
474
- **Interval literals**
475
-
476
- To add or subtract time, provide a quantity followed by a unit inside a string literal. Supported units include `second`, `minute`, `hour`, `day`, `week`, `month`, `quarter`, and `year` (plural forms or shorthands like `secs`, `mins`, `hrs` also work).
477
-
478
- ```sql
479
- from users select
480
- created_at + '5 minutes' as first_seen_plus_5,
481
- created_at - '2 days' as first_seen_minus_2
482
- ```
483
-
484
- Interval literals accept decimals (`'1.5 hours'`) and negative values (`'-7 days'`). Invalid strings produce a diagnostic such as “Could not parse interval literal: "many moons"”.
485
-
486
- ### Other miscellaneous details about GSQL
487
-
488
- - Trailing commas in `table` statements are optional.
489
- - Trailing semicolons after `table` and `table as` statements are optional.
490
- - The clauses in a `select` statement (`select`, `from`, `join`, `group by`, etc.) can be written in any order. They cannot be repeated, however.
491
- - `group by all` is implied if aggregative and scalar expressions are both present in the `select` clause. This means that `group by` can be omitted and the query will still effectively execute the `group by all`.
492
- - Expressions in `group by` are implicitly selected, so `from orders select avg(amount) group by user_id` will return two columns.
493
- - `count` is a reserved word. Do not alias your columns as `count`.
494
- - Window functions and set operations are not supported.
495
- - Subqueries are not supported. However, you can accomplish the same functionality by chaining queries:
496
- ````md
497
- ```sql my_subquery
498
- select ...
499
- ```
500
-
501
- ```sql my_query
502
- select ... from my_subquery
503
- ```
504
- ````
505
-
506
- ## Graphene data apps (dashboards)
654
+ # Graphene data apps (dashboards)
507
655
 
508
656
  Graphene data apps are written in Markdown with the addition of special Graphene HTML components. Markdown files can contain named GSQL queries in code fences that components can then refer to. Those queries can use any tables defined in .gsql files.
509
657
 
@@ -537,9 +685,9 @@ Best practices
537
685
  - If you have multiple time series charts, align their x-axes to have the same range and granularity.
538
686
  - Use the same color for a given metric if it is used in multiple charts.
539
687
 
540
- ### Visualization components
688
+ ## Visualization components
541
689
 
542
- #### Bar chart
690
+ ### Bar chart
543
691
 
544
692
  Use bar or column charts to compare a metric across categories. Bar charts are best with a small number of categories and series, and should generally start at 0.
545
693
 
@@ -555,9 +703,9 @@ Here's an example:
555
703
  />
556
704
  ```
557
705
 
558
- ##### All bar chart attributes
706
+ #### All bar chart attributes
559
707
 
560
- ###### General
708
+ ##### General
561
709
 
562
710
  | Attribute | Description | Options | Default |
563
711
  |----------|-------------|---------|---------|
@@ -569,11 +717,11 @@ Here's an example:
569
717
  | downloadableData | Whether to show the download button to allow users to download the data | `true`, `false` | `true` |
570
718
  | downloadableImage | Whether to show the button to allow users to save the chart as an image | `true`, `false` | `true` |
571
719
 
572
- ###### Data
720
+ ##### Data
573
721
 
574
722
  | Attribute | Description | Required | Options | Default |
575
723
  |----------|-------------|----------|---------|---------|
576
- | data | Query name, wrapped in curly braces | true | query name | - |
724
+ | data | GSQL query or table name | true | query name | - |
577
725
  | x | Column or expression to use for the x-axis of the chart | false | column name, stored expression name, GSQL expression | First column |
578
726
  | y | Column(s) or expression(s) to use for the y-axis of the chart. Each will create its own series. Consider a split axis with `y2` if there is a difference of scale or unit of measure between the series. | false | column name, stored expression name, GSQL expression, list of any combination of these e.g. `"col1, my_expr"` | Any non-assigned numeric columns |
579
727
  | y2 | Column(s) or expression(s) to include on a secondary y-axis. | false | column name, stored expression name, GSQL expression, list of any combination of these e.g. `"col1, my_expr"` | - |
@@ -585,7 +733,7 @@ Here's an example:
585
733
  | emptySet | Sets behaviour for empty datasets. Can throw an error, a warning, or allow empty. When set to 'error', empty datasets will block builds in `build:strict`. Note this only applies to initial page load - empty datasets caused by input component changes (dropdowns, etc.) are allowed. | false | `error`, `warn`, `pass` | `error` |
586
734
  | emptyMessage | Text to display when an empty dataset is received - only applies when `emptySet` is 'warn' or 'pass', or when the empty dataset is a result of an input component change (dropdowns, etc.). | false | string | No records |
587
735
 
588
- ###### Formatting & Styling
736
+ ##### Formatting & Styling
589
737
 
590
738
  | Attribute | Description | Options | Default |
591
739
  |----------|-------------|---------|---------|
@@ -603,7 +751,7 @@ Here's an example:
603
751
  | rightPadding | Number representing the padding (whitespace) on the left side of the chart. Useful to avoid labels getting cut off | number | - |
604
752
  | xLabelWrap | Whether to wrap x-axis labels when there is not enough space. Default behaviour is to truncate the labels. | `true`, `false` | `false` |
605
753
 
606
- ###### Value Labels
754
+ ##### Value Labels
607
755
 
608
756
  | Attribute | Description | Options | Default |
609
757
  |----------|-------------|---------|---------|
@@ -618,7 +766,7 @@ Here's an example:
618
766
  | y2LabelFmt | Format to use for value labels for series on the y2 axis. Overrides any other formats ([see available formats](#value-formatting)) | Excel-style format, built-in format name | - |
619
767
  | showAllLabels | Allow all labels to appear on chart, including overlapping labels | `true`, `false` | `false` |
620
768
 
621
- ###### Axes
769
+ ##### Axes
622
770
 
623
771
  | Attribute | Description | Options | Default |
624
772
  |----------|-------------|---------|---------|
@@ -648,13 +796,13 @@ Here's an example:
648
796
  | y2Scale | Whether to scale the y-axis to fit your data. `y2Min` and `y2Max` take precedence over `y2Scale` | `true`, `false` | `false` |
649
797
  | yAxisColor | Turns on/off color on the y-axis (turned on by default when secondary y-axis is used). Can also be used to set a specific color | `true`, `false`, color string (CSS name, hexademical, RGB, HSL) | `true` when y2 used; `false` otherwise |
650
798
 
651
- ###### Interactivity
799
+ ##### Interactivity
652
800
 
653
801
  | Attribute | Description | Options |
654
802
  |----------|-------------|---------|
655
803
  | connectGroup | Group name to connect this chart to other charts for synchronized tooltip hovering. Charts with the same `connectGroup` name will become connected | string |
656
804
 
657
- #### Pie chart
805
+ ### Pie chart
658
806
 
659
807
  Use a pie chart to show part-to-whole relationships across categories. Best for a small number of categories where proportions are easy to compare.
660
808
 
@@ -669,24 +817,24 @@ Here's an example:
669
817
  />
670
818
  ```
671
819
 
672
- ##### All pie chart attributes
820
+ #### All pie chart attributes
673
821
 
674
- ###### General
822
+ ##### General
675
823
 
676
824
  | Attribute | Description | Options | Default |
677
825
  |----------|-------------|---------|---------|
678
826
  | title | Chart title. Appears at top left of chart. | string | - |
679
827
  | subtitle | Chart subtitle. Appears just under title. | string | - |
680
828
 
681
- ###### Data
829
+ ##### Data
682
830
 
683
831
  | Attribute | Description | Required | Options | Default |
684
832
  |------|-------------|----------|---------|---------|
685
- | data | Query name, wrapped in curly braces | true | query name | - |
833
+ | data | GSQL query or table name | true | query name | - |
686
834
  | category | Column or expression to use for slice names | true | column name, stored expression name, GSQL expression | - |
687
835
  | value | Column or expression to use for slice values | true | column name, stored expression name, GSQL expression | - |
688
836
 
689
- #### Line chart
837
+ ### Line chart
690
838
 
691
839
  Use line charts to display how one or more metrics vary over time. Line charts are suitable for plotting a large number of data points on the same chart.
692
840
 
@@ -703,9 +851,9 @@ Here's an example:
703
851
  />
704
852
  ```
705
853
 
706
- ##### All line chart attributes
854
+ #### All line chart attributes
707
855
 
708
- ###### General
856
+ ##### General
709
857
 
710
858
  | Attribute | Description | Required | Options | Default |
711
859
  |------|-------------|----------|---------|---------|
@@ -717,11 +865,11 @@ Here's an example:
717
865
  | downloadableData | Whether to show the download button to allow users to download the data | false | `true`, `false` | `true` |
718
866
  | downloadableImage | Whether to show the button to allow users to save the chart as an image | false | `true`, `false` | `true` |
719
867
 
720
- ###### Data
868
+ ##### Data
721
869
 
722
870
  | Attribute | Description | Required | Options | Default |
723
871
  |------|-------------|----------|---------|---------|
724
- | data | Query name, wrapped in curly braces | true | query name | - |
872
+ | data | GSQL query or table name | true | query name | - |
725
873
  | x | Column or expression to use for the x-axis of the chart | true | column name, stored expression name, GSQL expression | - |
726
874
  | y | Column(s) or expression(s) to use for the y-axis of the chart. Each will create its own series. Consider a split axis with `y2` if there is a difference of scale or unit of measure between the series. | true | column name, stored expression name, GSQL expression, list of any combination of these e.g. `"col1, my_expr"` | - |
727
875
  | y2 | Column(s) or expression(s) to include on a secondary y-axis. | false | column name, stored expression name, GSQL expression, list of any combination of these e.g. `"col1, my_expr"` | - |
@@ -732,7 +880,7 @@ Here's an example:
732
880
  | emptySet | Sets behaviour for empty datasets. Can throw an error, a warning, or allow empty. When set to 'error', empty datasets will block builds in `build:strict`. Note this only applies to initial page load - empty datasets caused by input component changes (dropdowns, etc.) are allowed. | false | `error`, `warn`, `pass` | `error` |
733
881
  | emptyMessage | Text to display when an empty dataset is received - only applies when `emptySet` is 'warn' or 'pass', or when the empty dataset is a result of an input component change (dropdowns, etc.). | false | string | - |
734
882
 
735
- ###### Formatting & Styling
883
+ ##### Formatting & Styling
736
884
 
737
885
  | Attribute | Description | Required | Options | Default |
738
886
  |------|-------------|----------|---------|---------|
@@ -763,7 +911,7 @@ Here's an example:
763
911
  | rightPadding | Number representing the padding (whitespace) on the left side of the chart. Useful to avoid labels getting cut off | false | number | - |
764
912
  | xLabelWrap | Whether to wrap x-axis labels when there is not enough space. Default behaviour is to truncate the labels. | false | `true`, `false` | `false` |
765
913
 
766
- ###### Axes
914
+ ##### Axes
767
915
 
768
916
  | Attribute | Description | Required | Options | Default |
769
917
  |------|-------------|----------|---------|---------|
@@ -791,14 +939,14 @@ Here's an example:
791
939
  | y2Max | Maximum value for the y2-axis | false | number | - |
792
940
  | y2Scale | Whether to scale the y-axis to fit your data. `y2Min` and `y2Max` take precedence over `y2Scale` | false | `true`, `false` | `false` |
793
941
 
794
- ###### Interactivity
942
+ ##### Interactivity
795
943
 
796
944
  | Attribute | Description | Required | Options | Default |
797
945
  |------|-------------|----------|---------|---------|
798
946
  | connectGroup | Group name to connect this chart to other charts for synchronized tooltip hovering. Charts with the same `connectGroup` name will become connected | false | - | - |
799
947
 
800
948
 
801
- #### Area chart
949
+ ### Area chart
802
950
 
803
951
  Use area charts to track how a metric with multiple series changes over time, or a continuous range. Area charts emphasize changes in the sum of series over the individual series.
804
952
 
@@ -812,9 +960,9 @@ Here's an example:
812
960
  />
813
961
  ```
814
962
 
815
- ##### All area chart attributes
963
+ #### All area chart attributes
816
964
 
817
- ###### General
965
+ ##### General
818
966
 
819
967
  | Attribute | Description | Required | Options | Default |
820
968
  |------|-------------|----------|---------|---------|
@@ -826,11 +974,11 @@ Here's an example:
826
974
  | downloadableData | Whether to show the download button to allow users to download the data | false | `true`, `false` | `true` |
827
975
  | downloadableImage | Whether to show the button to allow users to save the chart as an image | false | `true`, `false` | `true` |
828
976
 
829
- ###### Data
977
+ ##### Data
830
978
 
831
979
  | Attribute | Description | Required | Options | Default |
832
980
  |------|-------------|----------|---------|---------|
833
- | data | Query name, wrapped in curly braces | true | query name | - |
981
+ | data | GSQL query or table name | true | query name | - |
834
982
  | x | Column or expression to use for the x-axis of the chart | true | column name, stored expression name, GSQL expression | First column |
835
983
  | y | Column(s) or expression(s) to use for the y-axis of the chart. Each will create its own series. Consider a split axis with `y2` if there is a difference of scale or unit of measure between the series. | true | column name, stored expression name, GSQL expression, list of any combination of these e.g. `"col1, my_expr"` | Any non-assigned numeric columns |
836
984
  | series | Column or expression to use to define the series (groups) in a multi-series chart. Use when values of a particular column dictate the multiple series to plot, eg. `country` would create a series for every distinct country in the column. | false | column name, stored expression name, GSQL expression | - |
@@ -840,7 +988,7 @@ Here's an example:
840
988
  | emptySet | Sets behaviour for empty datasets. Can throw an error, a warning, or allow empty. When set to 'error', empty datasets will block builds in `build:strict`. Note this only applies to initial page load - empty datasets caused by input component changes (dropdowns, etc.) are allowed. | false | `error`, `warn`, `pass` | `error` |
841
989
  | emptyMessage | Text to display when an empty dataset is received - only applies when `emptySet` is 'warn' or 'pass', or when the empty dataset is a result of an input component change (dropdowns, etc.). | false | string | "No records" |
842
990
 
843
- ###### Formatting & Styling
991
+ ##### Formatting & Styling
844
992
 
845
993
  | Attribute | Description | Required | Options | Default |
846
994
  |------|-------------|----------|---------|---------|
@@ -859,7 +1007,7 @@ Here's an example:
859
1007
  | rightPadding | Number representing the padding (whitespace) on the left side of the chart. Useful to avoid labels getting cut off | false | number | - |
860
1008
  | xLabelWrap | Whether to wrap x-axis labels when there is not enough space. Default behaviour is to truncate the labels. | false | `true`, `false` | `false` |
861
1009
 
862
- ###### Value Labels
1010
+ ##### Value Labels
863
1011
 
864
1012
  | Attribute | Description | Required | Options | Default |
865
1013
  |------|-------------|----------|---------|---------|
@@ -870,7 +1018,7 @@ Here's an example:
870
1018
  | labelFmt | Format to use for value labels ([see available formats](#value-formatting)) | false | Excel-style format, built-in format name | same as y column |
871
1019
  | showAllLabels | Allow all labels to appear on chart, including overlapping labels | false | `true`, `false` | `false` |
872
1020
 
873
- ###### Axes
1021
+ ##### Axes
874
1022
 
875
1023
  | Attribute | Description | Required | Options | Default |
876
1024
  |------|-------------|----------|---------|---------|
@@ -890,14 +1038,14 @@ Here's an example:
890
1038
  | yMax | Maximum value for the y-axis | false | number | - |
891
1039
  | yScale | Whether to scale the y-axis to fit your data. `yMin` and `yMax` take precedence over `yScale` | false | `true`, `false` | `false` |
892
1040
 
893
- ###### Interactivity
1041
+ ##### Interactivity
894
1042
 
895
1043
  | Attribute | Description | Required | Options | Default |
896
1044
  |------|-------------|----------|---------|---------|
897
1045
  | connectGroup | Group name to connect this chart to other charts for synchronized tooltip hovering. Charts with the same `connectGroup` name will become connected | false | - | - |
898
1046
 
899
1047
 
900
- #### Big value
1048
+ ### Big value
901
1049
 
902
1050
  Use big values to display a large value standalone, and optionally include a comparison and a sparkline.
903
1051
 
@@ -914,13 +1062,13 @@ Here's an example:
914
1062
  />
915
1063
  ```
916
1064
 
917
- ##### All big value attributes
1065
+ #### All big value attributes
918
1066
 
919
- ###### Data
1067
+ ##### Data
920
1068
 
921
1069
  | Attribute | Description | Required | Options | Default |
922
1070
  |------|-------------|----------|---------|---------|
923
- | data | Query name, wrapped in curly braces | true | query name | - |
1071
+ | data | GSQL query or table name | true | query name | - |
924
1072
  | value | Column or expression to pull the main value from. | true | column name, stored expression name, GSQL expression | - |
925
1073
  | title | Title of the card. | false | string | Title of the value column. |
926
1074
  | minWidth | Overrides min-width of component | false | % or px value | `"18%"` |
@@ -930,7 +1078,7 @@ Here's an example:
930
1078
  | emptyMessage | Text to display when an empty dataset is received - only applies when `emptySet` is 'warn' or 'pass', or when the empty dataset is a result of an input component change (dropdowns, etc.). | false | string | `"No records"` |
931
1079
  | link | Used to navigate to other pages. Can be a full external link like `"https://google.com"` or an internal link like `"/sales/performance"` | false | - | - |
932
1080
 
933
- ###### Comparison
1081
+ ##### Comparison
934
1082
 
935
1083
  | Attribute | Description | Required | Options | Default |
936
1084
  |------|-------------|----------|---------|---------|
@@ -942,7 +1090,7 @@ Here's an example:
942
1090
  | neutralMax | Sets the top of the range for 'neutral' values - neutral values appear in grey rather than red or green | false | number | `0` |
943
1091
  | comparisonFmt | Sets format for the comparison ([see available formats](#value-formatting)) | false | Excel-style format, built-in format | - |
944
1092
 
945
- ###### Sparkline
1093
+ ##### Sparkline
946
1094
 
947
1095
  | Attribute | Description | Required | Options | Default |
948
1096
  |------|-------------|----------|---------|---------|
@@ -955,7 +1103,7 @@ Here's an example:
955
1103
  | connectGroup | Group name to connect this sparkline to other charts for synchronized tooltip hovering. Charts with the same `connectGroup` name will become connected | false | string | - |
956
1104
  | description | Adds an info icon with description tooltip on hover | false | string | - |
957
1105
 
958
- #### Table
1106
+ ### Table
959
1107
 
960
1108
  Use a Table component to display a richly formatted table of data from a query. Tables are powerful default choice for data display that allow high information density, and are easy to read.
961
1109
 
@@ -965,13 +1113,13 @@ Here's an example:
965
1113
  <Table data=orders_summary />
966
1114
  ```
967
1115
 
968
- ##### All table attributes
1116
+ #### All table attributes
969
1117
 
970
- ###### Table
1118
+ ##### Table
971
1119
 
972
1120
  | Attribute | Description | Required | Options | Default |
973
1121
  |------|-------------|----------|---------|---------|
974
- | data | Query name, wrapped in curly braces | true | query name | - |
1122
+ | data | GSQL query or table name | true | query name | - |
975
1123
  | rows | Number of rows to show in the table before paginating results. Use `"rows=all"` to show all rows in the table. | false | number, `all` | `10` |
976
1124
  | title | Title for the table | false | string | - |
977
1125
  | subtitle | Subtitle - appears under the title | false | string | - |
@@ -997,7 +1145,7 @@ Here's an example:
997
1145
  | emptySet | Sets behaviour for empty datasets. Can throw an error, a warning, or allow empty. When set to 'error', empty datasets will block builds in `build:strict`. Note this only applies to initial page load - empty datasets caused by input component changes (dropdowns, etc.) are allowed. | false | `error`, `warn`, `pass` | `error` |
998
1146
  | emptyMessage | Text to display when an empty dataset is received - only applies when `emptySet` is 'warn' or 'pass', or when the empty dataset is a result of an input component change (dropdowns, etc.). | false | string | "No records" |
999
1147
 
1000
- ###### Groups
1148
+ ##### Groups
1001
1149
 
1002
1150
  | Attribute | Description | Required | Options | Default |
1003
1151
  |------|-------------|----------|---------|---------|
@@ -1011,7 +1159,7 @@ Here's an example:
1011
1159
  | subtotalFontColor | [groupType=section] Font color for the subtotal row | false | Hex color code, css color name | - |
1012
1160
  | groupNamePosition | [groupType=section] Where the group label will appear in its cell | false | `top`, `middle`, `bottom` | `middle` |
1013
1161
 
1014
- ###### Column
1162
+ ##### Column
1015
1163
 
1016
1164
  Use the Column sub-component to choose specific columns to display in your table, and to apply options to specific columns. If you don't supply any columns to the table, it will display all columns from your query result.
1017
1165
 
@@ -1102,11 +1250,11 @@ Conditional formatting (`contentType=colorscale`)
1102
1250
  | colorBreakpoints | List of numbers to use as breakpoints for each color in your color scale. Should line up with the colors you provide in `colorScale` | false | list of numbers | - |
1103
1251
  | scaleColumn | Column or expression to use to define the color scale range. Values in this column will have their cell color determined by the value in the scaleColumn | false | column name, stored expression name, GSQL expression | - |
1104
1252
 
1105
- ### Input components
1253
+ ## Input components
1106
1254
 
1107
- #### Text input
1255
+ ### Text input
1108
1256
 
1109
- Creates a text input that can be used to filter or search. To see how to filter a query using a text input, see Filters.
1257
+ Creates a text input that can be used to filter or search.
1110
1258
 
1111
1259
  Here's an example:
1112
1260
 
@@ -1125,7 +1273,7 @@ from users
1125
1273
  where email ilike concat('%', $name_of_input, '%')
1126
1274
  ```
1127
1275
 
1128
- ##### All text input attributes
1276
+ #### All text input attributes
1129
1277
 
1130
1278
  | Attribute | Description | Required | Options | Default |
1131
1279
  |------|-------------|----------|---------|---------|
@@ -1136,9 +1284,9 @@ where email ilike concat('%', $name_of_input, '%')
1136
1284
  | description | Adds an info icon with description tooltip on hover | false | string | - |
1137
1285
 
1138
1286
 
1139
- #### Dropdown
1287
+ ### Dropdown
1140
1288
 
1141
- Creates a dropdown menu with a list of options that can be selected. The selected option can be used to filter queries or in markdown. To see how to filter a query using a dropdown, see Filters.
1289
+ Creates a dropdown menu with a list of options that can be selected. The selected option can be used to filter queries or in markdown.
1142
1290
 
1143
1291
  Here's an example:
1144
1292
 
@@ -1164,12 +1312,12 @@ from orders
1164
1312
  where status = $status_dropdown
1165
1313
  ```
1166
1314
 
1167
- ##### All dropdown attributes
1315
+ #### All dropdown attributes
1168
1316
 
1169
1317
  | Attribute | Description | Required | Options | Default |
1170
1318
  |------|-------------|----------|---------|---------|
1171
1319
  | name | Name of the dropdown, used to reference the selected value elsewhere as `"$name"` | true | - | - |
1172
- | data | Query name, wrapped in curly braces | false | query name | - |
1320
+ | data | GSQL query or table name | false | query name | - |
1173
1321
  | value | Column name from the query containing values to pick from | false | column name | - |
1174
1322
  | multiple | Enables multi-select which returns a list | false | `true`, `false` | `false` |
1175
1323
  | defaultValue | Value to use when the dropdown is first loaded. Must be one of the options in the dropdown. Lists supported for multi-select. | false | value from dropdown, list of values e.g. `"Value 1, Value 2"` | - |
@@ -1183,7 +1331,7 @@ where status = $status_dropdown
1183
1331
  | hideDuringPrint | Hide the component when the report is printed | false | `true`, `false` | `true` |
1184
1332
  | description | Adds an info icon with description tooltip on hover | false | string | - |
1185
1333
 
1186
- ###### DropdownOption
1334
+ ##### DropdownOption
1187
1335
 
1188
1336
  The `DropdownOption` sub-component can be used to manually add options to a dropdown. This is useful to add a default option, or to add options that are not in a query.
1189
1337
 
@@ -1202,11 +1350,11 @@ Here's an example:
1202
1350
  | value | Value to use when the option is selected | true | - | - |
1203
1351
  | valueLabel | Label to display for the option in the dropdown | false | - | Uses the value |
1204
1352
 
1205
- ### Other components
1353
+ ## Other components
1206
1354
 
1207
1355
  `<Row></Row>` - Evenly distributes components inside along the same row.
1208
1356
 
1209
- ### Value formatting
1357
+ ## Value formatting
1210
1358
 
1211
1359
  The easiest way to format numbers and dates in Graphene is through component attributes. You can pass in either of the following:
1212
1360
 
@@ -1241,11 +1389,11 @@ In the example above, `xFmt` is passing in an Excel-style code to format the dat
1241
1389
 
1242
1390
  Formatting does not apply to the date axis of a chart. For example, if you set `xFmt` to `"m/d/yy"`, you will only see that formatting reflected in your chart tooltips and annotations. This is to ensure that the chart axis labels have the correct spacing.
1243
1391
 
1244
- #### Built-in Formats
1392
+ ### Built-in Formats
1245
1393
 
1246
1394
  Graphene supports a variety of date/time, number, percentage, and currency formats.
1247
1395
 
1248
- ##### Auto-Formatting
1396
+ #### Auto-Formatting
1249
1397
 
1250
1398
  Wherever you see `auto` listed beside a format, that means Graphene will automatically format your value based on the context it is in.
1251
1399
 
@@ -1253,7 +1401,7 @@ For example, Graphene automatically formats large numbers into shortened version
1253
1401
 
1254
1402
  You can choose to handle these numbers differently by choosing a specific format code. For example, if Graphene is formatting a column as millions, but you want to see all numbers in thousands, you could use the `num0k` format, which will show all numbers in the column in thousands with 0 decimal places.
1255
1403
 
1256
- ##### Dates
1404
+ #### Dates
1257
1405
 
1258
1406
  Graphene supports the following date formats:
1259
1407
 
@@ -1269,7 +1417,7 @@ Graphene supports the following date formats:
1269
1417
  * `dmy` - Day/month/year (e.g., 9/1/22)
1270
1418
  * `hms` - Time format (e.g., 11:45:03 AM)
1271
1419
 
1272
- ##### Currencies
1420
+ #### Currencies
1273
1421
 
1274
1422
  Supported currencies include USD, AUD, BRL, CAD, CNY, EUR, GBP, JPY, INR, KRW, NGN, RUB, and SEK.
1275
1423
 
@@ -1288,7 +1436,7 @@ For example, the available tags for USD are:
1288
1436
 
1289
1437
  Similar patterns apply to other supported currencies.
1290
1438
 
1291
- ##### Numbers
1439
+ #### Numbers
1292
1440
 
1293
1441
  The default number format (when no `fmt` is specified) automatically handles decimal places and summary units (in the same way that `usd` does for currency).
1294
1442
 
@@ -1303,7 +1451,7 @@ Available number formats:
1303
1451
  * `mult`, `mult0`, `mult1`, `mult2` - Multiplier format (e.g., 5.32x)
1304
1452
  * `sci` - Scientific notation
1305
1453
 
1306
- ##### Percentages
1454
+ #### Percentages
1307
1455
 
1308
1456
  Available percentage formats:
1309
1457
 
@@ -1312,25 +1460,3 @@ Available percentage formats:
1312
1460
  * `pct1` - Percentage with 1 decimal place (e.g., 73.1%)
1313
1461
  * `pct2` - Percentage with 2 decimal places (e.g., 73.10%)
1314
1462
  * `pct3` - Percentage with 3 decimal places (e.g., 73.100%)
1315
-
1316
- ## Graphene CLI
1317
-
1318
- These are the available commands:
1319
- - `npm run graphene check` - Checks the syntax (GSQL and Markdown) for the entire Graphene project.
1320
- - `npm run graphene check <mdPath>` - Checks the syntax for a specified Graphene markdown file. Will also do a runtime check if the dev server is running, and if successful, take a full page screenshot to a temp directory for the agent to view.
1321
- - `npm run graphene check <mdPath> --chart "<chartTitle>"` - Same as above, except if the runtime check is successful, only takes a screenshot of the specified chart. `<chartTitle>` must match (case sensitive) the `title` attribute on the chart component. `-c` can be used as shorthand for `--chart`.
1322
- - `npm run graphene compile "<GSQL>"` - Shows how GSQL is translated into the underlying database SQL.
1323
- - `npm run graphene run "<GSQL>"` - Runs a GSQL query. The tables and semantics defined in all .gsql files in the project are available for the query to use.
1324
-
1325
- # AGENT INSTRUCTIONS
1326
-
1327
- Follow these guidelines when working in a Graphene project.
1328
- - When formulating GSQL queries:
1329
- - First check all available stored expressions to see if there are any you can use. DO NOT redefine important business definitions like `profit` if they've already been modeled!
1330
- - Run your GSQL queries in the CLI first, _before_ you write them to a file. This way you can reason about the results to make sure they make sense.
1331
- - Do not try to search the web for Graphene-specific info; you will not find anything. All the documentation is here in graphene.md.
1332
- - When writing to a .gsql file, check your code with `npm run graphene check`.
1333
- - When writing to a Graphene .md file:
1334
- - Always check your code with `npm run graphene check <mdPath>`. Run the command with full permissions because the screenshot may not work in a sandbox.
1335
- - Then do a visual check by either a) looking at the screenshot that `npm run graphene check <mdPath>` creates, or b) using your browser tool to open the .md file at `localhost:<port>/mdPath` (without the .md extension; default port 4000).
1336
- - Critique what you see: Are all the data values formatted in a way that is easy to read? Does the shape of the visualized data require an adjustment to scale, axis min/max, etc.? Are any visualizations missing data altogether? Is that visualization type really the best way to paint the picture? Etc.