JSON / JSONB
UQL provides first-class support for JSON/JSONB fields across PostgreSQL, MySQL, MariaDB, and SQLite. Query, update, and sort by nested JSON properties using a consistent, type-safe API — UQL generates dialect-specific SQL automatically.
Entity Setup
Section titled “Entity Setup”Wrap JSONB field types with Json<T> to enable full type safety — IDE autocompletion for dot-notation paths, $merge keys, $unset keys, and $push targets.
import { Entity, Id, Field, Json } from 'uql-orm';
@Entity()export class Company { @Id() id?: number;
@Field() name?: string;
@Field({ type: 'jsonb' }) kind?: Json<{ public?: number; private?: number; tags?: string[] }>;
@Field({ type: 'jsonb' }) settings?: Json<{ isArchived?: boolean; theme?: string; locale?: string }>;}Filtering (Dot-Notation)
Section titled “Filtering (Dot-Notation)”Query nested JSON properties using dot-notation paths in $where. All comparison operators are supported.
const companies = await querier.findMany(Company, { $where: { 'settings.isArchived': { $ne: true }, 'settings.theme': 'dark', },});SELECT * FROM "Company"WHERE ("settings"->>'isArchived') IS DISTINCT FROM $1 AND "settings"->>'theme' = $2SELECT * FROM `Company`WHERE (`settings`->>'isArchived') <> ? AND (`settings`->>'theme') = ?SELECT * FROM `Company`WHERE JSON_VALUE(`settings`, '$.isArchived') <> ? AND JSON_VALUE(`settings`, '$.theme') = ?SELECT * FROM `Company`WHERE json_extract(`settings`, '$.isArchived') IS NOT ? AND json_extract(`settings`, '$.theme') = ?Updating ($merge / $unset / $push)
Section titled “Updating ($merge / $unset / $push)”Atomically merge or remove keys in JSON fields directly from update payloads. No need to overwrite the entire JSON value.
$merge — Partial Update
Section titled “$merge — Partial Update”Merge new key-value pairs into an existing JSON field. Existing keys not in $merge are preserved.
await querier.updateOneById(Company, id, { kind: { $merge: { public: 1 } },});UPDATE "Company" SET "kind" = COALESCE("kind", '{}'::jsonb) || $1::jsonb WHERE "id" = $2-- values: ['{"public":1}', id]UPDATE `Company` SET `kind` = JSON_SET(COALESCE(`kind`, '{}'), '$.public', CAST(? AS JSON)) WHERE `id` = ?-- values: ['1', id]UPDATE `Company` SET `kind` = JSON_SET(COALESCE(`kind`, '{}'), '$.public', CAST(? AS JSON)) WHERE `id` = ?-- values: ['1', id]UPDATE `Company` SET `kind` = json_set(COALESCE(`kind`, '{}'), '$.public', json(?)) WHERE `id` = ?-- values: ['1', id]$unset — Remove Keys
Section titled “$unset — Remove Keys”Remove specific keys from a JSON field.
await querier.updateOneById(Company, id, { kind: { $unset: ['private'] },});UPDATE "Company" SET "kind" = ("kind") - 'private' WHERE "id" = $1UPDATE `Company` SET `kind` = JSON_REMOVE(`kind`, '$.private') WHERE `id` = ?UPDATE `Company` SET `kind` = JSON_REMOVE(`kind`, '$.private') WHERE `id` = ?UPDATE `Company` SET `kind` = json_remove(`kind`, '$.private') WHERE `id` = ?$push — Append to Array
Section titled “$push — Append to Array”Append a value to the end of a JSON array using native array-append functions. Only keys whose type is an array are valid $push targets (type-checked at compile time).
await querier.updateOneById(Company, id, { kind: { $push: { tags: 'new-tag' } },});UPDATE "Company" SET "kind" = jsonb_set("kind", '{tags}', COALESCE("kind"->'tags', '[]'::jsonb) || jsonb_build_array($1::jsonb)) WHERE "id" = $2-- values: ['"new-tag"', id]UPDATE `Company` SET `kind` = JSON_ARRAY_APPEND(`kind`, '$.tags', CAST(? AS JSON)) WHERE `id` = ?-- values: ['"new-tag"', id]UPDATE `Company` SET `kind` = JSON_ARRAY_APPEND(`kind`, '$.tags', CAST(? AS JSON)) WHERE `id` = ?-- values: ['"new-tag"', id]UPDATE `Company` SET `kind` = json_insert(`kind`, '$.tags[#]', json(?)) WHERE `id` = ?-- values: ['"new-tag"', id]Combining Operators
Section titled “Combining Operators”$merge, $unset, and $push can be freely combined in a single update.
await querier.updateOneById(Company, id, { kind: { $merge: { public: 1 }, $push: { tags: 'new-tag' }, $unset: ['private'] },});Sorting (Dot-Notation)
Section titled “Sorting (Dot-Notation)”Sort by nested JSON field values using the same dot-notation syntax.
const companies = await querier.findMany(Company, { $sort: { 'kind.public': 'desc' },});SELECT * FROM "Company" ORDER BY "kind"->>'public' DESCSELECT * FROM `Company` ORDER BY (`kind`->>'public') DESCSELECT * FROM `Company` ORDER BY JSON_VALUE(`kind`, '$.public') DESCSELECT * FROM `Company` ORDER BY json_extract(`kind`, '$.public') DESCSupported Dialects
Section titled “Supported Dialects”All JSON features work across four SQL dialects:
| Feature | PostgreSQL | MySQL | MariaDB | SQLite |
|---|---|---|---|---|
| Dot-notation filtering | ->>'key' | ->>'key' | JSON_VALUE() | json_extract() |
$merge | || ::jsonb | JSON_SET() | JSON_SET() | json_set() |
$unset | - 'key' | JSON_REMOVE() | JSON_REMOVE() | json_remove() |
$push | jsonb_set() + || | JSON_ARRAY_APPEND() | JSON_ARRAY_APPEND() | json_insert() |
| Dot-notation sorting | ->>'key' | ->>'key' | JSON_VALUE() | json_extract() |
$size | jsonb_array_length() | JSON_LENGTH() | JSON_LENGTH() | json_array_length() |
$all | @> ::jsonb | JSON_CONTAINS() | JSON_CONTAINS() | json_each() |
$elemMatch | jsonb_array_elements | JSON_TABLE() | JSON_TABLE() | json_each() |
Dialect Compatibility
Section titled “Dialect Compatibility”This page targets modern, actively maintained database lines. Baselines below reflect the current compatibility target for generated SQL:
| Dialect | Practical baseline | Notes |
|---|---|---|
| PostgreSQL | 16+ | Uses jsonb operators/functions (->>, ` |
| MySQL | 8.4+ | Uses ->>, JSON_SET, JSON_REMOVE, JSON_ARRAY_APPEND, JSON_TABLE |
| MariaDB | 12.2+ | Uses JSON_VALUE for dot-notation path extraction (not ->>), plus JSON_SET, JSON_REMOVE, JSON_ARRAY_APPEND, JSON_TABLE |
| SQLite | 3.45+ | Uses json_extract, json_set, json_remove, and json_insert(..., '$[#]', ...) for append |