@graphenedata/cli 0.0.9 → 0.0.11

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,22 @@ 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
+ - [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
+ - [Available functions](#available-functions)
29
+ - [Aggregate functions](#aggregate-functions)
30
+ - [Date and time functions](#date-and-time-functions)
31
+ - [Conditional Functions](#conditional-functions)
32
+ - [Math Functions](#math-functions)
33
+ - [Type Conversion](#type-conversion)
34
+ - [Subqueries, CTEs, and chaining queries](#subqueries-ctes-and-chaining-queries)
35
+ - [Other miscellaneous details](#other-miscellaneous-details)
23
36
  - [`table as` statements](#table-as-statements)
24
37
  - [`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
38
  - [Graphene data apps (dashboards)](#graphene-data-apps-dashboards)
30
39
  - [Visualization components](#visualization-components)
31
40
  - [Bar chart](#bar-chart)
@@ -68,8 +77,6 @@ Graphene also has a CLI that lets you check syntax, run queries, serve data apps
68
77
  - [Input components](#input-components)
69
78
  - [Text input](#text-input)
70
79
  - [All text input attributes](#all-text-input-attributes)
71
- - [Date range](#date-range)
72
- - [All date range attributes](#all-date-range-attributes)
73
80
  - [Dropdown](#dropdown)
74
81
  - [All dropdown attributes](#all-dropdown-attributes)
75
82
  - [DropdownOption](#dropdownoption)
@@ -81,16 +88,27 @@ Graphene also has a CLI that lets you check syntax, run queries, serve data apps
81
88
  - [Currencies](#currencies)
82
89
  - [Numbers](#numbers)
83
90
  - [Percentages](#percentages)
84
- - [Graphene CLI](#graphene-cli)
85
- - [AGENT INSTRUCTIONS](#agent-instructions)
86
91
 
87
- ## Graphene SQL (GSQL)
92
+ # Graphene CLI
93
+
94
+ These are the available commands:
95
+ - `npm run graphene check` - Checks the syntax (GSQL and Markdown) for the entire Graphene project.
96
+ - `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.
97
+ - `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.
98
+ - `npm run graphene compile [GSQL | gsqlPath]` - Shows how GSQL is translated into the underlying database SQL.
99
+ - `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.
100
+
101
+ # Graphene SQL (GSQL)
88
102
 
89
- GSQL is comprised of `table` statements that declare tables and `select` statements that query them.
103
+ GSQL is comprised of four primary statements:
104
+ 1. `table` statements that declare existing tables and semantic metadata
105
+ 2. `select` statements that query tables
106
+ 3. `table as` statements that create new tables using `select`
107
+ 4. `extend` statements that attach semantic metadata to tables
90
108
 
91
- ### `table` statements
109
+ ## `table` statements
92
110
 
93
- `table` statements manifest tables that already exist in your database. Here's an example of two tables, `orders` and `users`, in GSQL.
111
+ `table` statements declare tables that already exist in your database. Here's an example of two tables, `orders` and `users`, in GSQL.
94
112
 
95
113
  ```sql
96
114
  table orders (
@@ -123,25 +141,25 @@ table users (
123
141
 
124
142
  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
143
 
126
- #### Base columns (required)
144
+ ### Base columns (required)
127
145
 
128
146
  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
147
 
130
- #### Join relationships
148
+ ### Join relationships
131
149
 
132
150
  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
151
 
134
152
  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
153
 
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.
154
+ This information is provided with the two supported join types, `join one` and `join many`:
155
+ - `join one` is used if there are many rows in the **left** table for each row in the **right** table.
156
+ - `join many` is used if there are many rows in the **right** table for each row in the **left** table.
139
157
 
140
158
  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
159
 
142
160
  Note that all joins in GSQL are left outer joins. There is no inner, right, or cross join.
143
161
 
144
- ##### Multiple join relationships between the same two tables
162
+ #### Multiple join relationships between the same two tables
145
163
 
146
164
  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
165
 
@@ -164,12 +182,12 @@ table users (
164
182
  )
165
183
  ```
166
184
 
167
- ##### Best practices for modeling join relationships
185
+ #### Best practices for modeling join relationships
168
186
 
169
187
  - 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
188
  - 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
189
 
172
- #### Stored expressions
190
+ ### Stored expressions
173
191
 
174
192
  **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
193
 
@@ -194,8 +212,7 @@ table orders (
194
212
  )
195
213
  ```
196
214
 
197
-
198
- ### `select` statements
215
+ ## `select` statements
199
216
 
200
217
  `select` is how you write queries in Graphene SQL. It behaves similarly to regular SQL except in the following ways:
201
218
  - It can invoke join relationships and stored expressions from `table` statements.
@@ -203,7 +220,7 @@ table orders (
203
220
 
204
221
  These differences are described in the sections below.
205
222
 
206
- #### Using join relationships in queries
223
+ ### Using join relationships in queries
207
224
 
208
225
  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
226
 
@@ -237,7 +254,7 @@ order by 2 desc
237
254
  limit 10
238
255
  ```
239
256
 
240
- ##### Multi-hop joins
257
+ #### Multi-hop joins
241
258
 
242
259
  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
260
 
@@ -278,7 +295,7 @@ order by 2 desc
278
295
  limit 10
279
296
  ```
280
297
 
281
- #### Using stored expressions in queries
298
+ ### Using scalar stored expressions (dimensions) in queries
282
299
 
283
300
  A stored expression can be invoked in a query by simply referencing it by name.
284
301
 
@@ -326,7 +343,9 @@ group by 1
326
343
 
327
344
  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
345
 
329
- This is an important concept to understand when invoking stored expressions that are **aggregative** (ie. contain agg functions). Here's an example.
346
+ ### Using aggregative stored expressions (measures) in queries
347
+
348
+ 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
349
 
331
350
  ```sql
332
351
  -- Profit by month
@@ -349,41 +368,218 @@ group by 1
349
368
  order by 1 asc
350
369
  ```
351
370
 
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!
371
+ [CRITICAL!] This means:
372
+ - 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.
373
+ - 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.
374
+ - 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.
375
+ - 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.
376
+
377
+ Another way of thinking about this is that measures are "self-aggregating."
378
+
379
+ ### Safe aggregation in fan-outs
380
+
381
+ 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.
382
+
383
+ 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.
384
+
385
+ >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.
386
+
387
+ Here's an example of a fan-out:
388
+
389
+ ```sql
390
+ table orders (
391
+ id BIGINT primary_key
392
+ customer_name VARCHAR
393
+ amt_with_shipping FLOAT
394
+
395
+ join many order_items on id = order_items.order_id
396
+ )
397
+
398
+ table order_items (
399
+ id BIGINT primary_key
400
+ order_id BIGINT
401
+ product VARCHAR
402
+ price FLOAT
403
+
404
+ join one orders on order_id = orders.id
405
+ )
406
+ ```
407
+
408
+ With the following data:
409
+
410
+ **orders**
411
+
412
+ | id | customer_name | amt_with_shipping |
413
+ |----|---------------|-------------------|
414
+ | 1 | Alice | 110.00 |
415
+ | 2 | Bob | 85.00 |
416
+
417
+ **order_items**
418
+
419
+ | id | order_id | product | price |
420
+ |----|----------|---------|-------|
421
+ | 1 | 1 | Widget | 60.00 |
422
+ | 2 | 1 | Gadget | 40.00 |
423
+ | 3 | 2 | Widget | 60.00 |
424
+ | 4 | 2 | Thingamajig | 15.00 |
353
425
 
354
- #### Safe aggregation in fan-outs
426
+ When joining orders to order_items, the result looks like:
355
427
 
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.
428
+ **orders joined to order_items**
429
+
430
+ | orders.id | customer_name | amt_with_shipping | order_items.id | product | price |
431
+ |-----------|---------------|-------------------|----------------|---------|-------|
432
+ | 1 | Alice | 110.00 | 1 | Widget | 60.00 |
433
+ | 1 | Alice | 110.00 | 2 | Gadget | 40.00 |
434
+ | 2 | Bob | 85.00 | 3 | Widget | 60.00 |
435
+ | 2 | Bob | 85.00 | 4 | Thingamajig | 15.00 |
436
+
437
+ 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.
438
+
439
+ 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:
440
+
441
+ ```sql
442
+ select
443
+ customer_name,
444
+ sum(orders.amt_with_shipping) as total_spent,
445
+ count(order_items.id) as items_purchased
446
+ from orders
447
+ group by customer_name
448
+ ```
357
449
 
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.
450
+ This returns:
359
451
 
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.
452
+ | customer_name | total_spent | items_purchased |
453
+ |---------------|-------------|-----------------|
454
+ | Alice | 110.00 | 2 |
455
+ | Bob | 85.00 | 2 |
361
456
 
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):
457
+ In regular SQL, you'd need a subquery or CTE to avoid the fan-out problem:
363
458
 
364
459
  ```sql
460
+ WITH order_totals AS (
461
+ SELECT customer_name, SUM(amt_with_shipping) as total_spent
462
+ FROM orders
463
+ GROUP BY customer_name
464
+ ),
465
+ item_counts AS (
466
+ SELECT o.customer_name, COUNT(oi.id) as items_purchased
467
+ FROM orders o
468
+ JOIN order_items oi ON o.id = oi.order_id
469
+ GROUP BY o.customer_name
470
+ )
365
471
  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`
472
+ ot.customer_name,
473
+ ot.total_spent,
474
+ ic.items_purchased
475
+ FROM order_totals ot
476
+ JOIN item_counts ic ON ot.customer_name = ic.customer_name
378
477
  ```
379
478
 
380
- You don't have to understand this; the point is that GSQL is minimizing the chances that naive users aggregate data incorrectly.
479
+ ### Available functions
480
+
481
+ 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.
482
+
483
+ #### Aggregate functions
484
+
485
+ | Function | Description | Parameters | Return Type | DuckDB | BigQuery | Snowflake |
486
+ | - | - | - | - | - | - | - |
487
+ | count(), count(*) | Counts the number of rows. | - | Number | x | x | x |
488
+ | count(column) | Counts the number of non-null values in a column. | `column` - Any column/expression | Number | x | x | x |
489
+ | count(distinct column) | Counts the number of distinct non-null values in a column. | `column` - Any column/expression | Number | x | x | x |
490
+ | sum(column) | Calculates the sum of numeric values. | `column` - Numeric column/expression | Number | x | x | x |
491
+ | avg(column) | Calculates the average (mean) of numeric values. | `column` - Numeric column/expression | Number | x | x | x |
492
+ | min(column) | Returns the minimum value. | `column` - Any comparable column/expression | Same as input | x | x | x |
493
+ | max(column) | Returns the maximum value. | `column` - Any comparable column/expression | Same as input | x | x | x |
494
+ | string_agg(column) | Concatenates string values. | `column` - String column/expression | String | x | x | x |
495
+ | stddev(column) | Calculates the standard deviation. | `column` - Numeric column/expression | Number | x | x | x |
496
+ | pXX(column) | Returns the XXth percentile (e.g., p50, p975, p9999). | `column` - Numeric column/expression | Number | x | x (≤p99) | x |
497
+
498
+ #### Date and time functions
499
+
500
+ | Function | Description | Parameters | Return Type | DuckDB | BigQuery | Snowflake |
501
+ | - | - | - | - | - | - | - |
502
+ | current_date() | Returns the current date. | - | Date | x | x | x |
503
+ | current_time() | Returns the current time. | - | Timestamp | x | x | x |
504
+ | current_timestamp() | Returns the current timestamp. | - | Timestamp | x | x | x |
505
+ | local_timestamp() | Returns the local timestamp. | - | Timestamp | x | x | x |
506
+ | current_datetime() | Returns the current datetime (BigQuery only). | - | Timestamp | | x | |
507
+ | 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 | | |
508
+ | 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 | |
509
+ | 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 |
510
+ | 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 | |
511
+
512
+ #### Conditional Functions
513
+
514
+ | Function | Description | Parameters | Return Type | DuckDB | BigQuery | Snowflake |
515
+ | - | - | - | - | - | - | - |
516
+ | 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 | |
517
+ | case when ... then ... else ... end | Evaluates conditions and returns values. | Multiple when/then clauses | Varies | x | x | x |
518
+ | coalesce(value1, value2, ...) | Returns first non-null value. | `value1, value2, ...` - Same type | Same as input | x | x | x |
519
+
520
+ #### Math Functions
521
+
522
+ | Function | Description | Parameters | Return Type | DuckDB | BigQuery | Snowflake |
523
+ | - | - | - | - | - | - | - |
524
+ | floor(number) | Rounds down to nearest integer. | `number` - Numeric value | Number | x | x | x |
525
+ | ceil(number) | Rounds up to nearest integer. | `number` - Numeric value | Number | x | x | x |
526
+ | greatest(value1, value2, ...) | Returns the greatest value. | `value1, value2, ...` - Same type | Same as input | x | x | x |
527
+ | least(value1, value2, ...) | Returns the smallest value. | `value1, value2, ...` - Same type | Same as input | x | x | x |
528
+ | safe_divide(numerator, denominator) | Divides, returning null on division by zero (BigQuery). | `numerator` - Number ; `denominator` - Number | Number | | x | |
381
529
 
382
- #### Percentile shorthand
530
+ #### Type Conversion
383
531
 
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.).
532
+ | Function | Description | Parameters | Return Type | DuckDB | BigQuery | Snowflake |
533
+ | - | - | - | - | - | - | - |
534
+ | 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 |
535
+ | value::type | Alternative cast syntax. | `value` - Any value ; `type` - Target type | Specified type | x | x | x |
385
536
 
386
- ### `table as` statements
537
+ ### Subqueries, CTEs, and chaining queries
538
+
539
+ 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.
540
+
541
+ Using `table as`:
542
+
543
+ ```sql
544
+ table sales_per_store as (
545
+ select
546
+ store_id,
547
+ sum(amount) as total_sales
548
+ from orders
549
+ group by store_id
550
+ )
551
+
552
+ -- average store sales
553
+ select avg(total_sales)
554
+ from sales_per_store
555
+ ```
556
+
557
+ In a Markdown file:
558
+
559
+ ````md
560
+ ```sql sales_per_store
561
+ select
562
+ store_id,
563
+ sum(amount) as total_sales
564
+ from orders
565
+ group by store_id
566
+ ```
567
+
568
+ ```sql average_store_sales
569
+ select avg(total_sales)
570
+ from sales_per_store
571
+ ```
572
+ ````
573
+
574
+ ### Other miscellaneous details
575
+
576
+ - The clauses in a `select` statement (`select`, `from`, `join`, `group by`, etc.) can be written in any order. They cannot be repeated, however.
577
+ - `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`.
578
+ - Expressions in `group by` are implicitly selected, so `from orders select avg(amount) group by user_id` will return two columns.
579
+ - `count` is a reserved word. Do not alias your columns as `count`.
580
+ - Window functions and set operations (`union [all]`, `intersect`, `except`) are not supported.
581
+
582
+ ## `table as` statements
387
583
 
388
584
  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
585
 
@@ -424,11 +620,11 @@ table user_facts as (
424
620
  )
425
621
  ```
426
622
 
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.
623
+ `table as` statements are conceptually the same as view tables and CTEs in regular SQL. A few things to note:
624
+ - You cannot declare join relationships or stored expressions directly in a `table as` statement. Use an `extend` statement.
429
625
  - 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
626
 
431
- ### `extend` statements
627
+ ## `extend` statements
432
628
 
433
629
  `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
630
 
@@ -457,53 +653,7 @@ extend daily_orders (
457
653
 
458
654
  Note that you cannot add new base columns with `extend`; you can only add joins and stored expressions.
459
655
 
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)
656
+ # Graphene data apps (dashboards)
507
657
 
508
658
  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
659
 
@@ -537,9 +687,9 @@ Best practices
537
687
  - If you have multiple time series charts, align their x-axes to have the same range and granularity.
538
688
  - Use the same color for a given metric if it is used in multiple charts.
539
689
 
540
- ### Visualization components
690
+ ## Visualization components
541
691
 
542
- #### Bar chart
692
+ ### Bar chart
543
693
 
544
694
  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
695
 
@@ -555,9 +705,9 @@ Here's an example:
555
705
  />
556
706
  ```
557
707
 
558
- ##### All bar chart attributes
708
+ #### All bar chart attributes
559
709
 
560
- ###### General
710
+ ##### General
561
711
 
562
712
  | Attribute | Description | Options | Default |
563
713
  |----------|-------------|---------|---------|
@@ -569,11 +719,11 @@ Here's an example:
569
719
  | downloadableData | Whether to show the download button to allow users to download the data | `true`, `false` | `true` |
570
720
  | downloadableImage | Whether to show the button to allow users to save the chart as an image | `true`, `false` | `true` |
571
721
 
572
- ###### Data
722
+ ##### Data
573
723
 
574
724
  | Attribute | Description | Required | Options | Default |
575
725
  |----------|-------------|----------|---------|---------|
576
- | data | Query name, wrapped in curly braces | true | query name | - |
726
+ | data | GSQL query or table name | true | query name | - |
577
727
  | x | Column or expression to use for the x-axis of the chart | false | column name, stored expression name, GSQL expression | First column |
578
728
  | 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
729
  | 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 +735,7 @@ Here's an example:
585
735
  | 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
736
  | 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
737
 
588
- ###### Formatting & Styling
738
+ ##### Formatting & Styling
589
739
 
590
740
  | Attribute | Description | Options | Default |
591
741
  |----------|-------------|---------|---------|
@@ -603,7 +753,7 @@ Here's an example:
603
753
  | rightPadding | Number representing the padding (whitespace) on the left side of the chart. Useful to avoid labels getting cut off | number | - |
604
754
  | xLabelWrap | Whether to wrap x-axis labels when there is not enough space. Default behaviour is to truncate the labels. | `true`, `false` | `false` |
605
755
 
606
- ###### Value Labels
756
+ ##### Value Labels
607
757
 
608
758
  | Attribute | Description | Options | Default |
609
759
  |----------|-------------|---------|---------|
@@ -618,7 +768,7 @@ Here's an example:
618
768
  | 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
769
  | showAllLabels | Allow all labels to appear on chart, including overlapping labels | `true`, `false` | `false` |
620
770
 
621
- ###### Axes
771
+ ##### Axes
622
772
 
623
773
  | Attribute | Description | Options | Default |
624
774
  |----------|-------------|---------|---------|
@@ -648,13 +798,13 @@ Here's an example:
648
798
  | y2Scale | Whether to scale the y-axis to fit your data. `y2Min` and `y2Max` take precedence over `y2Scale` | `true`, `false` | `false` |
649
799
  | 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
800
 
651
- ###### Interactivity
801
+ ##### Interactivity
652
802
 
653
803
  | Attribute | Description | Options |
654
804
  |----------|-------------|---------|
655
805
  | 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
806
 
657
- #### Pie chart
807
+ ### Pie chart
658
808
 
659
809
  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
810
 
@@ -669,24 +819,24 @@ Here's an example:
669
819
  />
670
820
  ```
671
821
 
672
- ##### All pie chart attributes
822
+ #### All pie chart attributes
673
823
 
674
- ###### General
824
+ ##### General
675
825
 
676
826
  | Attribute | Description | Options | Default |
677
827
  |----------|-------------|---------|---------|
678
828
  | title | Chart title. Appears at top left of chart. | string | - |
679
829
  | subtitle | Chart subtitle. Appears just under title. | string | - |
680
830
 
681
- ###### Data
831
+ ##### Data
682
832
 
683
833
  | Attribute | Description | Required | Options | Default |
684
834
  |------|-------------|----------|---------|---------|
685
- | data | Query name, wrapped in curly braces | true | query name | - |
835
+ | data | GSQL query or table name | true | query name | - |
686
836
  | category | Column or expression to use for slice names | true | column name, stored expression name, GSQL expression | - |
687
837
  | value | Column or expression to use for slice values | true | column name, stored expression name, GSQL expression | - |
688
838
 
689
- #### Line chart
839
+ ### Line chart
690
840
 
691
841
  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
842
 
@@ -703,9 +853,9 @@ Here's an example:
703
853
  />
704
854
  ```
705
855
 
706
- ##### All line chart attributes
856
+ #### All line chart attributes
707
857
 
708
- ###### General
858
+ ##### General
709
859
 
710
860
  | Attribute | Description | Required | Options | Default |
711
861
  |------|-------------|----------|---------|---------|
@@ -717,11 +867,11 @@ Here's an example:
717
867
  | downloadableData | Whether to show the download button to allow users to download the data | false | `true`, `false` | `true` |
718
868
  | downloadableImage | Whether to show the button to allow users to save the chart as an image | false | `true`, `false` | `true` |
719
869
 
720
- ###### Data
870
+ ##### Data
721
871
 
722
872
  | Attribute | Description | Required | Options | Default |
723
873
  |------|-------------|----------|---------|---------|
724
- | data | Query name, wrapped in curly braces | true | query name | - |
874
+ | data | GSQL query or table name | true | query name | - |
725
875
  | x | Column or expression to use for the x-axis of the chart | true | column name, stored expression name, GSQL expression | - |
726
876
  | 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
877
  | 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 +882,7 @@ Here's an example:
732
882
  | 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
883
  | 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
884
 
735
- ###### Formatting & Styling
885
+ ##### Formatting & Styling
736
886
 
737
887
  | Attribute | Description | Required | Options | Default |
738
888
  |------|-------------|----------|---------|---------|
@@ -763,7 +913,7 @@ Here's an example:
763
913
  | rightPadding | Number representing the padding (whitespace) on the left side of the chart. Useful to avoid labels getting cut off | false | number | - |
764
914
  | 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
915
 
766
- ###### Axes
916
+ ##### Axes
767
917
 
768
918
  | Attribute | Description | Required | Options | Default |
769
919
  |------|-------------|----------|---------|---------|
@@ -791,14 +941,14 @@ Here's an example:
791
941
  | y2Max | Maximum value for the y2-axis | false | number | - |
792
942
  | y2Scale | Whether to scale the y-axis to fit your data. `y2Min` and `y2Max` take precedence over `y2Scale` | false | `true`, `false` | `false` |
793
943
 
794
- ###### Interactivity
944
+ ##### Interactivity
795
945
 
796
946
  | Attribute | Description | Required | Options | Default |
797
947
  |------|-------------|----------|---------|---------|
798
948
  | 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
949
 
800
950
 
801
- #### Area chart
951
+ ### Area chart
802
952
 
803
953
  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
954
 
@@ -812,9 +962,9 @@ Here's an example:
812
962
  />
813
963
  ```
814
964
 
815
- ##### All area chart attributes
965
+ #### All area chart attributes
816
966
 
817
- ###### General
967
+ ##### General
818
968
 
819
969
  | Attribute | Description | Required | Options | Default |
820
970
  |------|-------------|----------|---------|---------|
@@ -826,11 +976,11 @@ Here's an example:
826
976
  | downloadableData | Whether to show the download button to allow users to download the data | false | `true`, `false` | `true` |
827
977
  | downloadableImage | Whether to show the button to allow users to save the chart as an image | false | `true`, `false` | `true` |
828
978
 
829
- ###### Data
979
+ ##### Data
830
980
 
831
981
  | Attribute | Description | Required | Options | Default |
832
982
  |------|-------------|----------|---------|---------|
833
- | data | Query name, wrapped in curly braces | true | query name | - |
983
+ | data | GSQL query or table name | true | query name | - |
834
984
  | x | Column or expression to use for the x-axis of the chart | true | column name, stored expression name, GSQL expression | First column |
835
985
  | 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
986
  | 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 +990,7 @@ Here's an example:
840
990
  | 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
991
  | 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
992
 
843
- ###### Formatting & Styling
993
+ ##### Formatting & Styling
844
994
 
845
995
  | Attribute | Description | Required | Options | Default |
846
996
  |------|-------------|----------|---------|---------|
@@ -859,7 +1009,7 @@ Here's an example:
859
1009
  | rightPadding | Number representing the padding (whitespace) on the left side of the chart. Useful to avoid labels getting cut off | false | number | - |
860
1010
  | 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
1011
 
862
- ###### Value Labels
1012
+ ##### Value Labels
863
1013
 
864
1014
  | Attribute | Description | Required | Options | Default |
865
1015
  |------|-------------|----------|---------|---------|
@@ -870,7 +1020,7 @@ Here's an example:
870
1020
  | 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
1021
  | showAllLabels | Allow all labels to appear on chart, including overlapping labels | false | `true`, `false` | `false` |
872
1022
 
873
- ###### Axes
1023
+ ##### Axes
874
1024
 
875
1025
  | Attribute | Description | Required | Options | Default |
876
1026
  |------|-------------|----------|---------|---------|
@@ -890,14 +1040,14 @@ Here's an example:
890
1040
  | yMax | Maximum value for the y-axis | false | number | - |
891
1041
  | yScale | Whether to scale the y-axis to fit your data. `yMin` and `yMax` take precedence over `yScale` | false | `true`, `false` | `false` |
892
1042
 
893
- ###### Interactivity
1043
+ ##### Interactivity
894
1044
 
895
1045
  | Attribute | Description | Required | Options | Default |
896
1046
  |------|-------------|----------|---------|---------|
897
1047
  | 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
1048
 
899
1049
 
900
- #### Big value
1050
+ ### Big value
901
1051
 
902
1052
  Use big values to display a large value standalone, and optionally include a comparison and a sparkline.
903
1053
 
@@ -914,13 +1064,13 @@ Here's an example:
914
1064
  />
915
1065
  ```
916
1066
 
917
- ##### All big value attributes
1067
+ #### All big value attributes
918
1068
 
919
- ###### Data
1069
+ ##### Data
920
1070
 
921
1071
  | Attribute | Description | Required | Options | Default |
922
1072
  |------|-------------|----------|---------|---------|
923
- | data | Query name, wrapped in curly braces | true | query name | - |
1073
+ | data | GSQL query or table name | true | query name | - |
924
1074
  | value | Column or expression to pull the main value from. | true | column name, stored expression name, GSQL expression | - |
925
1075
  | title | Title of the card. | false | string | Title of the value column. |
926
1076
  | minWidth | Overrides min-width of component | false | % or px value | `"18%"` |
@@ -930,7 +1080,7 @@ Here's an example:
930
1080
  | 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
1081
  | 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
1082
 
933
- ###### Comparison
1083
+ ##### Comparison
934
1084
 
935
1085
  | Attribute | Description | Required | Options | Default |
936
1086
  |------|-------------|----------|---------|---------|
@@ -942,7 +1092,7 @@ Here's an example:
942
1092
  | neutralMax | Sets the top of the range for 'neutral' values - neutral values appear in grey rather than red or green | false | number | `0` |
943
1093
  | comparisonFmt | Sets format for the comparison ([see available formats](#value-formatting)) | false | Excel-style format, built-in format | - |
944
1094
 
945
- ###### Sparkline
1095
+ ##### Sparkline
946
1096
 
947
1097
  | Attribute | Description | Required | Options | Default |
948
1098
  |------|-------------|----------|---------|---------|
@@ -955,7 +1105,7 @@ Here's an example:
955
1105
  | 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
1106
  | description | Adds an info icon with description tooltip on hover | false | string | - |
957
1107
 
958
- #### Table
1108
+ ### Table
959
1109
 
960
1110
  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
1111
 
@@ -965,13 +1115,13 @@ Here's an example:
965
1115
  <Table data=orders_summary />
966
1116
  ```
967
1117
 
968
- ##### All table attributes
1118
+ #### All table attributes
969
1119
 
970
- ###### Table
1120
+ ##### Table
971
1121
 
972
1122
  | Attribute | Description | Required | Options | Default |
973
1123
  |------|-------------|----------|---------|---------|
974
- | data | Query name, wrapped in curly braces | true | query name | - |
1124
+ | data | GSQL query or table name | true | query name | - |
975
1125
  | 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
1126
  | title | Title for the table | false | string | - |
977
1127
  | subtitle | Subtitle - appears under the title | false | string | - |
@@ -997,7 +1147,7 @@ Here's an example:
997
1147
  | 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
1148
  | 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
1149
 
1000
- ###### Groups
1150
+ ##### Groups
1001
1151
 
1002
1152
  | Attribute | Description | Required | Options | Default |
1003
1153
  |------|-------------|----------|---------|---------|
@@ -1011,7 +1161,7 @@ Here's an example:
1011
1161
  | subtotalFontColor | [groupType=section] Font color for the subtotal row | false | Hex color code, css color name | - |
1012
1162
  | groupNamePosition | [groupType=section] Where the group label will appear in its cell | false | `top`, `middle`, `bottom` | `middle` |
1013
1163
 
1014
- ###### Column
1164
+ ##### Column
1015
1165
 
1016
1166
  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
1167
 
@@ -1102,11 +1252,11 @@ Conditional formatting (`contentType=colorscale`)
1102
1252
  | 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
1253
  | 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
1254
 
1105
- ### Input components
1255
+ ## Input components
1106
1256
 
1107
- #### Text input
1257
+ ### Text input
1108
1258
 
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.
1259
+ Creates a text input that can be used to filter or search.
1110
1260
 
1111
1261
  Here's an example:
1112
1262
 
@@ -1125,7 +1275,7 @@ from users
1125
1275
  where email ilike concat('%', $name_of_input, '%')
1126
1276
  ```
1127
1277
 
1128
- ##### All text input attributes
1278
+ #### All text input attributes
1129
1279
 
1130
1280
  | Attribute | Description | Required | Options | Default |
1131
1281
  |------|-------------|----------|---------|---------|
@@ -1136,9 +1286,9 @@ where email ilike concat('%', $name_of_input, '%')
1136
1286
  | description | Adds an info icon with description tooltip on hover | false | string | - |
1137
1287
 
1138
1288
 
1139
- #### Dropdown
1289
+ ### Dropdown
1140
1290
 
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.
1291
+ 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
1292
 
1143
1293
  Here's an example:
1144
1294
 
@@ -1164,12 +1314,12 @@ from orders
1164
1314
  where status = $status_dropdown
1165
1315
  ```
1166
1316
 
1167
- ##### All dropdown attributes
1317
+ #### All dropdown attributes
1168
1318
 
1169
1319
  | Attribute | Description | Required | Options | Default |
1170
1320
  |------|-------------|----------|---------|---------|
1171
1321
  | 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 | - |
1322
+ | data | GSQL query or table name | false | query name | - |
1173
1323
  | value | Column name from the query containing values to pick from | false | column name | - |
1174
1324
  | multiple | Enables multi-select which returns a list | false | `true`, `false` | `false` |
1175
1325
  | 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 +1333,7 @@ where status = $status_dropdown
1183
1333
  | hideDuringPrint | Hide the component when the report is printed | false | `true`, `false` | `true` |
1184
1334
  | description | Adds an info icon with description tooltip on hover | false | string | - |
1185
1335
 
1186
- ###### DropdownOption
1336
+ ##### DropdownOption
1187
1337
 
1188
1338
  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
1339
 
@@ -1202,11 +1352,11 @@ Here's an example:
1202
1352
  | value | Value to use when the option is selected | true | - | - |
1203
1353
  | valueLabel | Label to display for the option in the dropdown | false | - | Uses the value |
1204
1354
 
1205
- ### Other components
1355
+ ## Other components
1206
1356
 
1207
1357
  `<Row></Row>` - Evenly distributes components inside along the same row.
1208
1358
 
1209
- ### Value formatting
1359
+ ## Value formatting
1210
1360
 
1211
1361
  The easiest way to format numbers and dates in Graphene is through component attributes. You can pass in either of the following:
1212
1362
 
@@ -1241,11 +1391,11 @@ In the example above, `xFmt` is passing in an Excel-style code to format the dat
1241
1391
 
1242
1392
  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
1393
 
1244
- #### Built-in Formats
1394
+ ### Built-in Formats
1245
1395
 
1246
1396
  Graphene supports a variety of date/time, number, percentage, and currency formats.
1247
1397
 
1248
- ##### Auto-Formatting
1398
+ #### Auto-Formatting
1249
1399
 
1250
1400
  Wherever you see `auto` listed beside a format, that means Graphene will automatically format your value based on the context it is in.
1251
1401
 
@@ -1253,7 +1403,7 @@ For example, Graphene automatically formats large numbers into shortened version
1253
1403
 
1254
1404
  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
1405
 
1256
- ##### Dates
1406
+ #### Dates
1257
1407
 
1258
1408
  Graphene supports the following date formats:
1259
1409
 
@@ -1269,7 +1419,7 @@ Graphene supports the following date formats:
1269
1419
  * `dmy` - Day/month/year (e.g., 9/1/22)
1270
1420
  * `hms` - Time format (e.g., 11:45:03 AM)
1271
1421
 
1272
- ##### Currencies
1422
+ #### Currencies
1273
1423
 
1274
1424
  Supported currencies include USD, AUD, BRL, CAD, CNY, EUR, GBP, JPY, INR, KRW, NGN, RUB, and SEK.
1275
1425
 
@@ -1288,7 +1438,7 @@ For example, the available tags for USD are:
1288
1438
 
1289
1439
  Similar patterns apply to other supported currencies.
1290
1440
 
1291
- ##### Numbers
1441
+ #### Numbers
1292
1442
 
1293
1443
  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
1444
 
@@ -1303,7 +1453,7 @@ Available number formats:
1303
1453
  * `mult`, `mult0`, `mult1`, `mult2` - Multiplier format (e.g., 5.32x)
1304
1454
  * `sci` - Scientific notation
1305
1455
 
1306
- ##### Percentages
1456
+ #### Percentages
1307
1457
 
1308
1458
  Available percentage formats:
1309
1459
 
@@ -1312,25 +1462,3 @@ Available percentage formats:
1312
1462
  * `pct1` - Percentage with 1 decimal place (e.g., 73.1%)
1313
1463
  * `pct2` - Percentage with 2 decimal places (e.g., 73.10%)
1314
1464
  * `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.