Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 106 additions & 1 deletion query/src/org/labkey/query/controllers/LabKeySql.md
Original file line number Diff line number Diff line change
Expand Up @@ -231,4 +231,109 @@ Here is a summary of the available functions and methods in LabKey SQL.
* `moduleProperty(module name, property name)`: Returns a module property.
* `overlaps(START1, END1, START2, END2)`: Tests for overlapping time intervals (PostgreSQL only).
* `userid()`, `username()`: Return user information.
* `version()`: Returns the current schema version.
* `version()`: Returns the current schema version.

-----

### **9. JSON and JSONB Operators and Functions (PostgreSQL Only)**

LabKey SQL supports PostgreSQL JSON and JSONB operators and functions for working with JSON data stored in columns. These are **not available on MS SQL Server**. LabKey SQL does not natively understand arrays, but functions that expect them may still work. See the [PostgreSQL docs](https://www.postgresql.org/docs/14/functions-json.html) for detailed usage.

#### **Operators via `json_op`**

Native PostgreSQL operator syntax (`->`, `->>`, etc.) cannot be used directly in LabKey SQL. Instead, use the `json_op` pass-through function with three arguments: the left operand, the operator as a string, and the right operand.

* **Supported operators:** `->`, `->>`, `#>`, `#>>`, `@>`, `<@`, `?`, `?|`, `?&`, `||`, `-`, `#-`
* **Syntax:** `json_op(left_operand, 'operator', right_operand)`
* **Examples:**
* **Extract by key (as JSON):** Get a nested value from a JSONB column:
```sql
SELECT json_op(metadata, '->', 'name') AS name_json FROM samples
```
* **Extract by key (as text):** Get the text value:
```sql
SELECT json_op(metadata, '->>', 'name') AS name_text FROM samples
```
* **Containment check:** Filter rows where JSONB contains a given structure:
```sql
SELECT * FROM samples WHERE json_op(metadata, '@>', parse_jsonb('{"status":"active"}'))
```
* **Key existence check:**
```sql
SELECT * FROM samples WHERE json_op(metadata, '?', 'name')
```

#### **Conversion / Parsing Functions**

* `parse_json(text)`, `parse_jsonb(text)`: Cast a text value to JSON or JSONB. Use instead of `::jsonb` or `CAST(... AS JSONB)`.
```sql
SELECT parse_jsonb('{"a":1, "b":null}')
```
* `to_json(value)`, `to_jsonb(value)`: Convert a value to JSON/JSONB. Text values become a single JSON string.
* `array_to_json(array)`: Convert an array to JSON.
* `row_to_json(value)`: Convert a scalar row to JSON. **Note:** Does not support converting an entire table to JSON; use `to_jsonb()` instead.

#### **Builder Functions**

* `json_build_array(...)`, `jsonb_build_array(...)`: Build a JSON array from arguments.
```sql
SELECT jsonb_build_array(1, 'two', 3.0)
```
* `json_build_object(...)`, `jsonb_build_object(...)`: Build a JSON object from key/value arguments.
```sql
SELECT jsonb_build_object('name', sample_name, 'type', sample_type) FROM samples
```
* `json_object(text_array)`, `jsonb_object(text_array)`: Build a JSON object from a text array.

#### **Query and Extraction Functions**

* `json_array_length(json)`, `jsonb_array_length(jsonb)`: Return the length of the outermost JSON array.
* `json_each(json)`, `jsonb_each(jsonb)`: Expand the outermost JSON object into key/value pairs. **Note:** Only scalar function usage is supported, not the table-returning version.
```sql
SELECT json_each('{"a":"foo", "b":"bar"}') AS Value
```
* `json_each_text(json)`, `jsonb_each_text(jsonb)`: Like `json_each` but values are returned as text. Only scalar usage supported.
* `json_extract_path(json, ...)`, `jsonb_extract_path(jsonb, ...)`: Return the JSON value at the given path.
```sql
SELECT jsonb_extract_path(metadata, 'address', 'city') FROM samples
```
* `json_extract_path_text(json, ...)`, `jsonb_extract_path_text(jsonb, ...)`: Return the value at the given path as text.
* `json_object_keys(json)`, `jsonb_object_keys(jsonb)`: Return the keys of the outermost JSON object.
* `json_array_elements(json)`, `jsonb_array_elements(jsonb)`: Expand a JSON array into a set of values.
* `json_array_elements_text(json)`, `jsonb_array_elements_text(jsonb)`: Expand a JSON array into a set of text values.

#### **Type Inspection and Cleanup Functions**

* `json_typeof(json)`, `jsonb_typeof(jsonb)`: Return the type of the outermost JSON value (e.g., `"object"`, `"array"`, `"string"`, `"number"`).
* `json_strip_nulls(json)`, `jsonb_strip_nulls(jsonb)`: Remove all null-valued keys from a JSON object.

#### **Modification Functions**

* `jsonb_insert(jsonb, path, new_value)`: Insert a value at a given path within a JSONB object.
* `jsonb_pretty(jsonb)`: Format a JSONB value as indented text.
* `jsonb_set(jsonb, path, new_value)`: Set the value at a given path. Strict: returns NULL on NULL input.
* `jsonb_set_lax(jsonb, path, new_value, null_behavior)`: Like `jsonb_set` but not strict. The `null_behavior` argument must be one of: `'raise_exception'`, `'use_json_null'`, `'delete_key'`, or `'return_target'`.

#### **Path Query Functions**

* `jsonb_path_exists(jsonb, path)`, `jsonb_path_exists_tz(...)`: Check whether the JSON path returns any item. The `_tz` variant is timezone-aware.
* `jsonb_path_match(jsonb, path)`, `jsonb_path_match_tz(...)`: Return the result of a JSON path predicate check.
* `jsonb_path_query(jsonb, path)`, `jsonb_path_query_tz(...)`: Return all items matched by the JSON path.
* `jsonb_path_query_array(jsonb, path)`, `jsonb_path_query_array_tz(...)`: Return matched items as an array.
* `jsonb_path_query_first(jsonb, path)`, `jsonb_path_query_first_tz(...)`: Return the first matched item.

#### **Not Supported**

The following functions are **not supported** in LabKey SQL:
`json_populate_record`, `jsonb_populate_record`, `json_populate_recordset`, `jsonb_populate_recordset`, `json_to_record`, `jsonb_to_record`, `json_to_recordset`, `jsonb_to_recordset`.

#### **Quick Reference for Writing LabKey SQL with JSON**

When writing LabKey SQL queries that work with JSON columns:

1. **Always use `json_op()` for operators** — never use raw PostgreSQL operator syntax like `->` or `->>`. Wrap them: `json_op(col, '->>', 'key')`.
2. **Use `parse_jsonb()` to create JSONB literals** — there is no `::jsonb` cast in LabKey SQL. Write `parse_jsonb('{"key":"value"}')`.
3. **Use `jsonb_extract_path_text()` for nested field access** — this is often the clearest way to extract a deeply nested text value: `jsonb_extract_path_text(col, 'level1', 'level2', 'field')`.
4. **Use `jsonb_build_object()` to construct JSON** — for building JSON from column values: `jsonb_build_object('id', rowid, 'name', label)`.
5. **Check database type first** — these functions only work on PostgreSQL. If the target server may use MS SQL Server, do not use them.
6. **The `validateSQL` MCP tool can verify syntax** — use it to check JSON function calls before the user saves a query.
Loading