Skip to content

Records (objects) contain key-value pairs. This guide shows you how to work with records — accessing fields, extracting keys, combining fragments, and transforming values.

Get values using dot notation or brackets:

from {
user: {
name: "Alice",
age: 30,
address: {
city: "NYC",
zip: "10001"
}
}
}
name = user.name
city = user.address.city
zip = user["address"]["zip"]
has_email = user.has("email")
{
user: {
name: "Alice",
age: 30,
address: {city: "NYC", zip: "10001"}
},
name: "Alice",
city: "NYC",
zip: "10001",
has_email: false
}

Extract field names and values:

from {
config: {
host: "localhost",
port: 8080,
ssl: true
}
}
field_names = config.keys()
// Note: values() function is not available
num_fields = config.keys().length()
{
config: {host: "localhost", port: 8080, ssl: true},
field_names: ["host", "port", "ssl"],
num_fields: 3
}

Use the spread operator ... to combine records. Spread keeps the resulting record visible where you construct it, and lets you mix existing fragments with literals or computed fields. Later fields overwrite earlier fields, so put defaults first and overrides last:

from {
defaults: {host: "localhost", port: 80, ssl: false},
custom: {port: 8080, ssl: true}
}
service = {
...defaults,
...custom,
debug: true,
}
{
defaults: {host: "localhost", port: 80, ssl: false},
custom: {port: 8080, ssl: true},
service: {
host: "localhost",
port: 8080,
ssl: true,
debug: true
}
}

Use else {} when a record fragment may be missing:

from {base: {id: 1, name: "Alice"}}
user = {
...base,
...(profile? else {}),
active: true,
}
{
base: {id: 1, name: "Alice"},
user: {
id: 1,
name: "Alice",
active: true
}
}

The fnmerge function returns the same result for two records, but prefer spread in transformation code when you construct the resulting record.

Note: The map_values function is not available in TQL. To transform record values, you would need to reconstruct the record with transformed values manually:

from {
prices: {
apple: 1.50,
banana: 0.75,
orange: 2.00
}
}
// Manual transformation example:
with_tax = {
apple: prices.apple * 1.1,
banana: prices.banana * 1.1,
orange: prices.orange * 1.1
}

Keep explicit fields by constructing a new record. Use fnselect_matching and fndrop_matching when field names follow a pattern:

from {
user: {
id: 123,
name: "Alice",
email: "alice@example.com",
password: "secret",
api_key: "xyz123"
}
}
public_info = {
id: user.id,
name: user.name,
email: user.email
}
contact = {
name: user.name,
email: user.email
}
credentials = user.select_matching("(password|api_key)$")
safe_user = user.drop_matching("(password|api_key)$")
{
user: {
id: 123,
name: "Alice",
email: "alice@example.com",
password: "secret",
api_key: "xyz123"
},
public_info: {
id: 123,
name: "Alice",
email: "alice@example.com"
},
contact: {
name: "Alice",
email: "alice@example.com"
},
credentials: {
password: "secret",
api_key: "xyz123"
},
safe_user: {
id: 123,
name: "Alice",
email: "alice@example.com"
}
}

The matching functions inspect only top-level field names on the record you call them on. They don’t recurse into nested records.

Last updated: