Calculated
Derives a value from other columns of the same row.
Typical uses are a full name derived from a first and last name, a line total derived from a quantity and a unit price, a status derived from a threshold, and a distance between two generated points. Any column a calculation reads may be hidden, which generates the inputs a calculation requires without writing them to the output.
A calculation is a tree of operations. Separate operation sets are provided for arithmetic, text, dates and times, geometry, and conditional logic. Any operation's input may itself be an operation, so compound expressions are built by nesting rather than by a dedicated operation for each combination.
Sub-field: value
Each JSON block on this page is either a single field object or, where a calculation reads a column
produced by another field, an array of consecutive field objects in the order they appear in a table's
fields array. See Building a Request for the full request structure.
Properties
| Property | Type | Default | Description |
|---|---|---|---|
calculation |
Calculation |
required | The tree of operations to evaluate. See The three kinds of node. |
map |
object |
required | Maps value to a destination column and data type. The data type also determines how the calculated result is converted before it is written. See The result's data type. |
nullPercentageMax |
number |
0 |
Upper bound (0-100) of the percentage of rows that should be null. See nullPercentageMin. |
nullPercentageMin |
number |
0 |
Lower bound (0-100) of the percentage of rows that should be null. The rate is fixed once for the whole field from this range, not recalculated per row. Set equal to nullPercentageMax for an exact rate. Applied in addition to any nulls the calculation itself produces. |
seed |
integer |
none | Not accepted. A calculation involves no random selection, so a request that sets seed is rejected. |
unique |
boolean |
false |
Must be false. A calculation returns the same result for the same inputs, so regenerating a duplicate row cannot resolve it. Set unique on a column the calculation reads instead. |
The three kinds of node
A calculation is a tree of nodes. Each node is exactly one of the following:
- A column, read from the current row:
{ "column": "first_name" }. - A literal, a fixed value:
{ "literal": 100 },{ "literal": "Auto" },{ "literal": true }. - An operation, evaluated from
inputs, each of which is itself a node:{ "operation": "Add", "inputs": [...] }.
Setting more than one of column, literal, and operation on a single node is rejected, as is
setting none of them. Each operation accepts only the properties listed for it in the sections below.
A property an operation does not read, such as places on Upper, is rejected rather than ignored,
consistent with the handling of unrecognized properties elsewhere in a request.
The following line total applies Round to the result of Multiply, whose own inputs are two
columns produced by the fields before it:
[
{
"type": "Number",
"mode": "Whole",
"min": 1,
"max": 10,
"map": { "value": { "column": "quantity", "dataType": "Integer" } }
},
{
"type": "Number",
"mode": "Real",
"min": 1,
"max": 100,
"scale": 2,
"precision": 5,
"map": { "value": { "column": "unit_price", "dataType": "Decimal" } }
},
{
"type": "Calculated",
"calculation": {
"operation": "Round",
"places": 2,
"inputs": [
{ "operation": "Multiply", "inputs": [{ "column": "quantity" }, { "column": "unit_price" }] }
]
},
"map": { "value": { "column": "line_total", "dataType": "Decimal" } }
}
]
| quantity | unit_price | line_total |
|---|---|---|
| 5 | 95.77 | 478.85 |
| 4 | 43.68 | 174.72 |
| 9 | 5.87 | 52.83 |
| 2 | 54.9 | 109.8 |
| 6 | 88.98 | 533.88 |
| 7 | 76.35 | 534.45 |
Reading other columns
A calculation may read only a column produced by a field declared before it in the same table's
fields array. Fields are generated in declaration order, so at the point a calculation is evaluated,
a field declared after it still contains its value from the previous row. Reading such a column would
produce values offset by one row, so the request is rejected during validation. The error identifies
the columns available at that position.
[
{
"type": "Calculated",
"calculation": { "operation": "Upper", "inputs": [{ "column": "status" }] },
"map": { "value": { "column": "status_upper", "dataType": "VarChar" } }
},
{
"type": "String",
"mode": "Lookup",
"lookupValues": [{ "value": "Open" }, { "value": "Closed" }],
"map": { "value": { "column": "status", "dataType": "VarChar" } }
}
]
Rejected: status is declared after the calculation that reads it.
Declaring the fields a calculation depends on earlier in the array resolves this:
[
{
"type": "String",
"mode": "Lookup",
"lookupValues": [{ "value": "Open" }, { "value": "Closed" }],
"map": { "value": { "column": "status", "dataType": "VarChar" } }
},
{
"type": "Calculated",
"calculation": { "operation": "Upper", "inputs": [{ "column": "status" }] },
"map": { "value": { "column": "status_upper", "dataType": "VarChar" } }
}
]
| status | status_upper |
|---|---|
| Open | OPEN |
| Open | OPEN |
| Closed | CLOSED |
| Open | OPEN |
| Open | OPEN |
| Closed | CLOSED |
A calculation may read a column produced by any earlier field type, not only another Calculated
field, and a Calculated field may in turn be read by a later one, which allows derivations to be
chained across successive fields. A later field reads the result of a Calculated field by the column
name in its map.
A column read by a calculation need not appear in the output. See Hidden columns.
Null handling
Except where an operation's own description states otherwise, a node with a null input evaluates to null, and that null propagates through each enclosing operation. A single null input therefore produces a null result for that row rather than an error.
{
"operation": "Add",
"inputs": [{ "column": "base_fee" }, { "column": "surcharge" }]
}
If surcharge is null for a row, the result is null for that row regardless of the value of
base_fee.
The following operations handle a null input themselves and do not return null because of it:
Coalescereturns the first input that is not null.ConcatwithskipNullsset omits a null input from the join instead of returning null for the whole result.Templatewrites a null value as empty text.RunningTotalleaves the total unchanged for a null input.IfandCasetreat a null condition as not true.Ifreturnselse, andCasemoves on to the next arm.Andreturns true only when every input is true, andOrreturns true when any input is true. A null input counts as not true, soAndwith a null input returns false, andOrreturns false unless another input is true.- The
IsNullandIsNotNullcomparisons evaluate null inputs directly. - The
Equals,NotEquals,In, andNotIncomparisons treat null as a value. Null equals null and does not equal anything else, soEqualsof two nulls is true andNotEqualsof null and1is true. Mapmatches a null input against a mapping whosefromis null, and returnsdefaultwhen there is none.
Not of a null returns null. Every other operation returns null for a null input.
A value that an operation cannot read as the type it needs is treated the same way as a null. Text in a
column that is not a number returns null from Add, and text that is not an ISO 8601 date returns null
from DateAdd. A literal is different: it is part of the request, so a literal an operation cannot read
is rejected when the request is validated, with the path of the literal and the type it needs.
The result's data type
The value a calculation produces is converted to the dataType declared on map.value before it is
written, using the same conversion Cast applies. Arithmetic operations return the numeric type of
their inputs, so declaring the destination column as Decimal, Double, or Integer is normally
sufficient and no explicit Cast is required at the end of the tree. Cast is required only where a
value must change type partway through a calculation, before a subsequent operation is applied to it.
See Types.
A calculation whose result can never be written to its column is rejected before any row is generated. Each operation produces one kind of value, and each kind can be written only to some column types:
| The calculation produces | It can be written to |
|---|---|
| A number | Byte, Short, Integer, Long, Decimal, Float, Double, Boolean, Interval (as seconds), or a text type |
| True or false | Boolean, a number type, or a text type |
| A date or time | Date, Time, DateTime, Timestamp, or a text type |
| An interval | Interval, or a text type |
| A geometry | Geometry, Geography, or a text type |
| Text | Any column type |
DateAdd mapped to an Integer column is rejected, for example. Text can contain a number or a date, so a
text result can be mapped to any column, and so can a value read straight from a column. If, Case,
Map, Coalesce, Min and Max produce the kind of their branches when every branch produces the same
kind. Use Cast to change a result's kind.
A result that is the right kind but does not fit its column in a particular row is written as null for
that row. A whole number outside the range of an Integer, Short, Byte, or Long column is one
example. Numbers written to a text column are written in full, without an exponent.
Arithmetic
| Operation | Inputs | Properties | Result |
|---|---|---|---|
Add |
2+ | Sum of every input. | |
Subtract |
2+ | First input less every input after it. | |
Multiply |
2+ | Product of every input. | |
Divide |
2+ | First input divided by every input after it. Division by zero returns null rather than raising an error. | |
Modulo |
2 | Remainder of the first input divided by the second. Modulo by zero returns null rather than raising an error. | |
Power |
2 | First input raised to the power of the second. A result that is not a real number, such as a negative number raised to a fractional power, returns null. | |
Negate |
1 | The input with its sign reversed. | |
Abs |
1 | The absolute value of the input. | |
Round |
1 | places (default 0) |
The input rounded to places decimal places, half away from zero. A negative places rounds to the left of the decimal point: -2 rounds 1234.5678 to 1200. |
Floor |
1 | The largest whole number no greater than the input. | |
Ceiling |
1 | The smallest whole number no less than the input. | |
Truncate |
1 | The input with its fractional part removed, toward zero. | |
Min |
2+ | The smallest of its inputs, compared across the row rather than down the table. | |
Max |
2+ | The largest of its inputs, compared across the row rather than down the table. | |
Clamp |
1 | low, high (at least one required) |
The input constrained to the range low to high. A low greater than high is rejected. |
Sqrt |
1 | Square root of the input. A negative input returns null. | |
Log |
1 | logBase (default: natural log) |
Logarithm of the input, in logBase where set. An input at or below zero, or a logBase at or below zero or equal to one, returns null. |
Exp |
1 | e raised to the power of the input. A result too large to represent returns null. |
Add, Subtract, Multiply, and Divide retain exact arithmetic when every input is already exact,
meaning a Decimal column or an integer literal. Ten Decimal values of 0.1 sum to exactly 1.0
rather than 0.9999999999999999. If any input is a Double or a Float column, or a literal with a
fractional part such as 0.2, the operation is evaluated in floating point. Declare monetary inputs as
Decimal to retain exact results. Mixing Decimal and Double inputs in the same
operation is permitted, but the result is no longer exact.
Quantity and unit price multiplied and rounded to two decimal places:
[
{
"type": "Number",
"mode": "Whole",
"min": 1,
"max": 12,
"map": { "value": { "column": "quantity", "dataType": "Integer" } }
},
{
"type": "Number",
"mode": "Real",
"min": 5,
"max": 250,
"scale": 2,
"map": { "value": { "column": "unit_price", "dataType": "Decimal" } }
},
{
"type": "Calculated",
"calculation": {
"operation": "Round",
"places": 2,
"inputs": [
{ "operation": "Multiply", "inputs": [{ "column": "quantity" }, { "column": "unit_price" }] }
]
},
"map": { "value": { "column": "line_total", "dataType": "Decimal" } }
}
]
| quantity | unit_price | line_total |
|---|---|---|
| 9 | 56.57 | 509.13 |
| 2 | 159.94 | 319.88 |
| 9 | 133.05 | 1197.45 |
| 10 | 118.85 | 1188.5 |
| 8 | 73.03 | 584.24 |
| 10 | 157.45 | 1574.5 |
A sensor reading constrained to a fixed range, with its absolute value in a separate column:
[
{
"type": "Number",
"mode": "Real",
"min": -20,
"max": 120,
"scale": 1,
"seed": 4,
"map": { "value": { "column": "sensor_reading", "dataType": "Decimal" } }
},
{
"type": "Calculated",
"calculation": { "operation": "Clamp", "low": 0, "high": 100, "inputs": [{ "column": "sensor_reading" }] },
"map": { "value": { "column": "clamped_reading", "dataType": "Decimal" } }
},
{
"type": "Calculated",
"calculation": { "operation": "Abs", "inputs": [{ "column": "sensor_reading" }] },
"map": { "value": { "column": "magnitude", "dataType": "Decimal" } }
}
]
| sensor_reading | clamped_reading | magnitude |
|---|---|---|
| 95.5 | 95.5 | 95.5 |
| -8.9 | 0 | 8.9 |
| 76.7 | 76.7 | 76.7 |
| 100.8 | 100 | 100.8 |
| 77.3 | 77.3 | 77.3 |
| 51.3 | 51.3 | 51.3 |
Readings below 0 are raised to 0, and readings above 100 are lowered to 100. magnitude is each
reading without its sign.
Text
| Operation | Inputs | Properties | Result |
|---|---|---|---|
Template |
0+ | template (required) |
template with each placeholder substituted. See Template. |
Concat |
2+ | separator (default: none), skipNulls (default false) |
Every input joined by separator. |
Upper |
1 | The input in upper case. | |
Lower |
1 | The input in lower case. | |
TitleCase |
1 | The input with the first letter of each word capitalized and the rest in lower case. See TitleCase. | |
Trim |
1 | characters (default: white space) |
The input with characters removed from each end. |
PadLeft |
1 | length (required), padCharacter (default: space) |
The input padded on the left to length. |
PadRight |
1 | length (required), padCharacter (default: space) |
The input padded on the right to length. |
Substring |
1 | start (required), length (default: to the end) |
length characters from start, counting from zero. Returns characters up to the end of the input where it is shorter than start plus length. start cannot be negative. |
Left |
1 | length (required) |
The first length characters. |
Right |
1 | length (required) |
The last length characters. |
Replace |
1 | find (required), replaceWith (default: none, which removes find), regex (default false) |
Every occurrence of find replaced by replaceWith. regex set to true interprets find as a regular expression. |
Length |
1 | The number of characters in the input, as a number. | |
Slug |
1 | separator (default -) |
The input reduced to a lower-case token. Accented letters are reduced to their base letter, and each run of characters that is not a letter or a digit is replaced by separator. See Slug. |
Hash |
1+ | algorithm (default Sha256), separator (default: none) |
Every input joined by separator, then hashed and returned as lower-case hexadecimal. See Hash. |
TitleCase
TitleCase capitalizes the first letter of each word and writes the rest in lower case. A hyphen starts
a new word, so jean-pierre becomes Jean-Pierre. A letter after an apostrophe that follows a single
letter is also capitalized, so o'neil becomes O'Neil, while don't becomes Don't.
Where the input contains lower-case letters, a word written entirely in capitals is treated as an
abbreviation and kept as it is, so USB-C cable becomes USB-C Cable. Input with no lower-case letters
at all is converted word by word, so JOHN O'NEIL becomes John O'Neil.
Template
Template substitutes values from the current row into a string, which avoids composing text from
Concat and a series of conversions. {columnName} inserts that column's value. {0}, {1}, and so
on insert the entry at that position in inputs, which applies where the inserted value is the result
of another operation rather than a column:
[
{
"type": "Person",
"countries": [{ "country": "us" }],
"map": {
"firstName": { "column": "first_name", "dataType": "VarChar" },
"lastName": { "column": "last_name", "dataType": "VarChar" }
}
},
{
"type": "Calculated",
"calculation": { "operation": "Template", "template": "{first_name} {last_name}" },
"map": { "value": { "column": "full_name", "dataType": "VarChar" } }
}
]
| first_name | last_name | full_name |
|---|---|---|
| Raissa | Plocek | Raissa Plocek |
| Tiffannie | Rosencutter | Tiffannie Rosencutter |
| Fawaz | Klaahsen | Fawaz Klaahsen |
| Deijah | Ringdahl | Deijah Ringdahl |
| Janet | Bialka | Janet Bialka |
| Rave | Ghai | Rave Ghai |
A literal brace is written as {{ or }}. An unclosed { is rejected during request validation. A
{columnName} referencing a column that is not available at that position is rejected on the same
basis as a { "column": ... } node. See Reading other columns.
Slug
Slug produces a value suitable for an email local part, a URL segment, or a file name, where the
result must be predictable ASCII regardless of the script of the source value:
[
{
"type": "Person",
"countries": [{ "country": "us" }],
"map": {
"firstName": { "column": "first_name", "dataType": "VarChar" },
"lastName": { "column": "last_name", "dataType": "VarChar" }
}
},
{
"type": "Calculated",
"calculation": {
"operation": "Template",
"template": "{0}.{1}@example.com",
"inputs": [
{ "operation": "Slug", "inputs": [{ "column": "first_name" }] },
{ "operation": "Slug", "inputs": [{ "column": "last_name" }] }
]
},
"map": { "value": { "column": "email", "dataType": "VarChar" } }
}
]
| first_name | last_name | |
|---|---|---|
| Emannuel | Jacomet | emannuel.jacomet@example.com |
| Hiwot | Tolerico | hiwot.tolerico@example.com |
| Demetri | Akre | demetri.akre@example.com |
| Orhan | Dullen | orhan.dullen@example.com |
| Bindiya | Haeffner | bindiya.haeffner@example.com |
| Mariesa | Voskoboynik | mariesa.voskoboynik@example.com |
Accented Latin letters are reduced to their base letter, so é becomes e. Letters that are not
formed from an ASCII base letter, such as ø or ł, are mapped individually to the nearest ASCII
letter, so a value containing one of them still produces a complete token. A value written entirely in
a script with no Latin form, such as Japanese or Greek, yields no ASCII characters, and the result is
null for that row.
Hash
Hash produces a stable digest of its inputs, suitable for a pseudonymized key that must be
reproducible for the same inputs:
[
{
"type": "Person",
"countries": [{ "country": "us" }],
"map": {
"lastName": { "column": "last_name", "dataType": "VarChar" },
"dob": { "column": "date_of_birth", "dataType": "Date" }
}
},
{
"type": "Calculated",
"calculation": {
"operation": "Hash",
"algorithm": "Sha256",
"separator": "|",
"inputs": [{ "column": "last_name" }, { "column": "date_of_birth" }]
},
"map": { "value": { "column": "customer_key", "dataType": "VarChar" } }
}
]
| last_name | date_of_birth | customer_key |
|---|---|---|
| Swazey | 1941-12-29 | a20a840db0b13d81133784604af17e4bd1005c287df9f27a86cda1e4f3fcce3b |
| Kazandjian | 1972-02-28 | 8593381bf6b74bfbc0f0a33d0371df66a7721907ddb9e87ee63af55818a075dc |
| Domville | 1952-06-10 | cc83ef246c569d742936177494280fa584dd0214bdfb6858476193c052d56295 |
| Sekulski | 1970-12-18 | 7d7973dc7477ee4e81b685a41cf83cc679b33c0e67552838903eae1f21070598 |
| Santomauro | 1957-05-11 | 145596a8287158bfee3fc620b30842cdf4c8aad2cf7a66a231d9d4229b7f7d00 |
| Toyoda | 1973-02-13 | 9a6dfd2b147b935243cd4833d7b9c7e9486d4ea4d57ceb7a76d26cbd140ec48a |
algorithm is one of Sha256 (64 hexadecimal characters), Sha512 (128), Sha1 (40), or Md5
(32). None of these algorithms is suitable for password storage, which requires a salted,
purpose-built algorithm rather than a general-purpose digest.
Trimming, casing, and slicing
Text operations compose in the same way as any others. The following normalizes a source value and derives a product code prefix and a sequence number from it:
[
{
"type": "String",
"mode": "Lookup",
"lookupValues": [{ "value": " wireless mouse " }, { "value": "usb-c cable" }, { "value": "laptop stand " }],
"map": { "value": { "column": "raw_name", "dataType": "VarChar", "hidden": true } }
},
{
"type": "Calculated",
"calculation": {
"operation": "TitleCase",
"inputs": [{ "operation": "Trim", "inputs": [{ "column": "raw_name" }] }]
},
"map": { "value": { "column": "display_name", "dataType": "VarChar" } }
},
{
"type": "Calculated",
"calculation": {
"operation": "Left",
"length": 3,
"inputs": [{ "operation": "Upper", "inputs": [{ "operation": "Trim", "inputs": [{ "column": "raw_name" }] }] }]
},
"map": { "value": { "column": "sku_prefix", "dataType": "VarChar" } }
},
{
"type": "Calculated",
"calculation": {
"operation": "PadLeft",
"length": 8,
"padCharacter": "0",
"inputs": [{ "operation": "RowNumber", "startAt": 1 }]
},
"map": { "value": { "column": "sku_number", "dataType": "VarChar" } }
}
]
raw_name contains unnormalized source text such as " wireless mouse " and "usb-c cable". It is
hidden, so only the values derived from it appear in the output.
| display_name | sku_prefix | sku_number |
|---|---|---|
| Usb-C Cable | USB | 00000001 |
| Wireless Mouse | WIR | 00000002 |
| Usb-C Cable | USB | 00000003 |
| Laptop Stand | LAP | 00000004 |
| Laptop Stand | LAP | 00000005 |
| Wireless Mouse | WIR | 00000006 |
PadLeft accepts any node as input, not only text. RowNumber is therefore
converted to text and padded directly, with no intermediate Cast.
Concat with null inputs
Concat with skipNulls set omits a null input from the join instead of returning null for the whole
result:
[
{
"type": "Number",
"mode": "Whole",
"min": 100,
"max": 9999,
"map": { "value": { "column": "street_number", "dataType": "Integer", "hidden": true } }
},
{
"type": "String",
"mode": "Lookup",
"lookupValues": [{ "value": "Elm Way" }, { "value": "Oak Ave" }, { "value": "Main St" }],
"map": { "value": { "column": "street_name", "dataType": "VarChar", "hidden": true } }
},
{
"type": "String",
"mode": "Lookup",
"lookupValues": [{ "value": "Unit 12" }, { "value": "Suite 4" }, { "value": "Apt 7" }],
"nullPercentageMin": 50,
"nullPercentageMax": 50,
"map": { "value": { "column": "unit", "dataType": "VarChar", "hidden": true } }
},
{
"type": "Calculated",
"calculation": {
"operation": "Concat",
"separator": ", ",
"skipNulls": true,
"inputs": [
{ "operation": "Template", "template": "{street_number} {street_name}" },
{ "column": "unit" }
]
},
"map": { "value": { "column": "address_line_1", "dataType": "VarChar" } }
}
]
unit is null on about half the rows, representing an optional suite or apartment number.
| address_line_1 |
|---|
| 7618 Oak Ave |
| 9426 Main St |
| 2947 Main St |
| 302 Oak Ave |
| 2578 Main St |
| 3782 Main St, Apt 7 |
Without skipNulls, each row with a null unit would produce a null address_line_1.
Dates and times
Each operation in this section accepts Date, DateTime, Timestamp, or Time values and returns a
result of the same type. Adding a month to a Date returns a Date, and adding an hour to a
DateTime returns a DateTime. A literal string such as "2026-01-01" is interpreted in the same way
as a column of the corresponding type.
Text is read as a date or time only when it is written in ISO 8601 form: 2026-03-15 for a date,
2026-03-15T09:30:00 or 2026-03-15 09:30:00 for a date and time, with Z or an offset such as +02:00
for a timestamp, and 09:30:00 for a time. A regional form such as 03/15/2026 is not read, because the
same text means different dates in different countries. A text column in another form returns null, and
a literal in another form is rejected.
| Operation | Inputs | Properties | Result |
|---|---|---|---|
DateAdd |
2 | unit (omit to add an Interval instead) |
The first input advanced by the second. The second is a number of unit, or an Interval value where unit is not set. |
DateSubtract |
2 | unit (omit to subtract an Interval instead) |
The first input moved back by the second, on the same basis as DateAdd. |
DateDiff |
2 | unit (required) |
Complete unit from the first input to the second, as a number. Negative where the second is earlier. |
Age |
2 | Completed years from the first input to the second, counted by the calendar. See Age. | |
DatePart |
1 | part (required) |
The component of the input set by part, as a number. See DatePart for the full list of parts. |
DateTruncate |
1 | unit (required) |
The input reduced to the start of its unit. |
CombineDateTime |
2 | A Date and a Time combined into one DateTime, taking the date from the first input and the time from the second. |
|
DateInterval |
2 | The elapsed time from the first input to the second, as an Interval. |
|
FormatDate |
1 | pattern (required) |
The input written as text in pattern, a custom date and time format string. |
unit, for DateAdd, DateSubtract, DateDiff, and DateTruncate, is one of Years, Quarters,
Months, Weeks, Days, Hours, Minutes, Seconds, or Milliseconds. Years and Months are
calendar units rather than fixed durations. Adding one month to January 31 returns the last day of
February, and DateDiff in Months counts complete calendar months rather than treating each month
as 30 days. DateDiff returns the largest number of months that DateAdd can add to the first input
without passing the second, so January 31 to February 28 is one month.
Age
Age is provided separately from DateDiff in Years because it applies an additional rule: a
birthday that has not yet occurred in the later year is not counted. A person born on June 15, 1996 is
29 years old on March 1, 2026, not 30. Age applies this rule directly, without requiring a Case
expression built on DatePart:
[
{
"type": "Date",
"mode": "Random",
"min": "1955-01-01",
"max": "2007-12-31",
"map": { "value": { "column": "date_of_birth", "dataType": "Date" } }
},
{
"type": "Calculated",
"calculation": { "operation": "Age", "inputs": [{ "column": "date_of_birth" }, { "literal": "2026-01-01" }] },
"map": { "value": { "column": "age", "dataType": "Integer" } }
},
{
"type": "Calculated",
"calculation": {
"operation": "Case",
"cases": [
{ "when": { "operation": "Compare", "comparison": "LessThan", "inputs": [{ "column": "age" }, { "literal": 25 }] }, "then": { "literal": "18-24" } },
{ "when": { "operation": "Compare", "comparison": "LessThan", "inputs": [{ "column": "age" }, { "literal": 45 }] }, "then": { "literal": "25-44" } }
],
"default": { "literal": "45+" }
},
"map": { "value": { "column": "age_band", "dataType": "VarChar" } }
}
]
| date_of_birth | age | age_band |
|---|---|---|
| 1993-09-24 | 32 | 25-44 |
| 2003-11-14 | 22 | 18-24 |
| 1972-07-26 | 53 | 45+ |
| 1969-02-14 | 56 | 45+ |
| 2004-02-03 | 21 | 18-24 |
| 1990-05-09 | 35 | 25-44 |
The second input is the reference date, and it has no default. No current date is available, because the same request always produces the same data. Supply the reference date as a literal or as another generated column.
DatePart
part is one of Year, Quarter (1-4), Month (1-12), Week (ISO week of the year), Day (of the
month), DayOfYear, DayOfWeek (1 for Monday through 7 for Sunday), Hour, Minute, Second, or
OffsetMinutes (the UTC offset, for a Timestamp).
Moving and truncating dates
A subscription renewal date one year after the start date, with the calendar month of the start date:
[
{
"type": "Date",
"mode": "Random",
"min": "2025-01-01",
"max": "2026-06-30",
"map": { "value": { "column": "started_on", "dataType": "Date" } }
},
{
"type": "Calculated",
"calculation": { "operation": "DateAdd", "unit": "Months", "inputs": [{ "column": "started_on" }, { "literal": 12 }] },
"map": { "value": { "column": "renews_on", "dataType": "Date" } }
},
{
"type": "Calculated",
"calculation": {
"operation": "FormatDate",
"pattern": "MMMM yyyy",
"inputs": [{ "operation": "DateTruncate", "unit": "Months", "inputs": [{ "column": "started_on" }] }]
},
"map": { "value": { "column": "billing_month", "dataType": "VarChar" } }
}
]
| started_on | renews_on | billing_month |
|---|---|---|
| 2026-06-17 | 2027-06-17 | June 2026 |
| 2026-06-01 | 2027-06-01 | June 2026 |
| 2026-06-24 | 2027-06-24 | June 2026 |
| 2025-10-14 | 2026-10-14 | October 2025 |
| 2025-10-14 | 2026-10-14 | October 2025 |
| 2025-06-10 | 2026-06-10 | June 2025 |
Geometry
Geometry inputs are read from the well-known text in a Geometry column, which is the format
Geospatial produces. A value that cannot be parsed as a geometry, such as an empty
string or malformed text, is treated as a missing input and returns null, consistent with any other
node whose input cannot be interpreted as the required type.
A measurement across the earth's surface needs real coordinates, longitude -180 to 180 and latitude -90
to 90. A geometry with any coordinate outside that range returns null from Distance, GeometryLength,
Area and Buffer in surface units, and from Bearing, Midpoint and Geohash. Measurements in
Degrees and SquareDegrees are flat and accept any coordinates.
| Operation | Inputs | Properties | Result |
|---|---|---|---|
Distance |
2 | units (required) |
The shortest distance between the two geometries, in units. See Required units. |
Centroid |
1 | The center of mass of the input, as a Point. Can be outside the input, for example for a crescent or a ring. |
|
PointOnSurface |
1 | A point that always lies on the input, unlike Centroid. See PointOnSurface. |
|
CoordinateX |
1 | The longitude of the input, or of its centroid where the input is not itself a point. | |
CoordinateY |
1 | The latitude of the input, or of its centroid where the input is not itself a point. | |
Bearing |
2 | The initial compass bearing from the first point to the second, in degrees from north. Both inputs must be points, and any other geometry returns null. | |
Midpoint |
2 | The point half way along the surface path between two points, as a Point. Both inputs must be points, and any other geometry returns null. |
|
Area |
1 | areaUnits (required) |
The area the input encloses, in areaUnits. |
GeometryLength |
1 | units (required) |
The length of a line, or the perimeter of a polygon, in units. |
Envelope |
1 | The bounding box of the input, as a Polygon. |
|
Buffer |
1 | units (required), distance (required) |
The input expanded by distance in units. See Buffer. |
ToGeoJson |
1 | The input rendered as a GeoJSON geometry object rather than well-known text. | |
Geohash |
1 | precision (default 9) |
The position of the input encoded as a geohash of precision characters (1-12). |
Intersects |
2 | Whether the two inputs share at least one point. Returns a boolean. | |
Within |
2 | Whether the first input lies entirely inside the second. Returns a boolean. |
Required units
Distance, GeometryLength, Area, and Buffer each require an explicit units property and provide
no default. Degrees and SquareDegrees measure in the coordinate system itself, where one degree of
longitude corresponds to approximately 111 km at the equator and decreases toward the poles. All other
units, meaning Meters, Kilometers, Miles, NauticalMiles, Feet, and Yards for distance and
length, and SquareMeters, SquareKilometers, SquareMiles, Hectares, and Acres for area,
measure across the surface of the earth. A distance in degrees cannot easily be distinguished from a
distance in another unit, so the unit has no default:
{ "operation": "Distance", "inputs": [{ "column": "origin" }, { "column": "destination" }] }
Rejected: Distance has no units.
Surface measurement also handles the antimeridian correctly. On the equator, two points at longitude
179 and -179 are about 222 kilometers apart, because the surface path crosses the antimeridian between
them. Measured in Degrees, the same two points are 358 apart, because a coordinate-system
measurement takes the longitudes as they are written.
Distance and initial bearing between two generated points:
[
{
"type": "Geospatial",
"geometryType": "Point",
"boundingLocation": { "country": "gb", "region": "eng" },
"map": { "value": { "column": "origin", "dataType": "Geometry", "hidden": true } }
},
{
"type": "Geospatial",
"geometryType": "Point",
"boundingLocation": { "country": "gb" },
"map": { "value": { "column": "destination", "dataType": "Geometry", "hidden": true } }
},
{
"type": "Calculated",
"calculation": {
"operation": "Round",
"places": 1,
"inputs": [{ "operation": "Distance", "units": "Kilometers", "inputs": [{ "column": "origin" }, { "column": "destination" }] }]
},
"map": { "value": { "column": "distance_km", "dataType": "Double" } }
},
{
"type": "Calculated",
"calculation": {
"operation": "Round",
"places": 0,
"inputs": [{ "operation": "Bearing", "inputs": [{ "column": "origin" }, { "column": "destination" }] }]
},
"map": { "value": { "column": "bearing_degrees", "dataType": "Double" } }
}
]
| distance_km | bearing_degrees |
|---|---|
| 328.2 | 309 |
| 672.6 | 340 |
| 735.2 | 333 |
| 583 | 336 |
| 86.3 | 214 |
| 225.2 | 300 |
Buffer
Buffer expands a geometry by a ground distance rather than by a fixed number of degrees. A degree of
longitude corresponds to a different ground distance at each latitude, so expansion applied directly
in degrees produces a distorted result away from the equator. Buffer compensates for this, so a 10
kilometer buffer encloses the same ground area at any latitude. A buffer that would reach past
longitude -180 or 180, or latitude -90 or 90, is cut off at that edge.
A delivery catchment area around a generated store location, with the geohash of the store:
[
{
"type": "Geospatial",
"geometryType": "Point",
"boundingLocation": { "country": "us", "state": "ca" },
"map": { "value": { "column": "location", "dataType": "Geometry", "hidden": true } }
},
{
"type": "Calculated",
"calculation": { "operation": "Buffer", "units": "Kilometers", "distance": 10, "inputs": [{ "column": "location" }] },
"map": { "value": { "column": "catchment", "dataType": "Geometry", "hidden": true } }
},
{
"type": "Calculated",
"calculation": {
"operation": "Round",
"places": 0,
"inputs": [{ "operation": "Area", "areaUnits": "SquareKilometers", "inputs": [{ "column": "catchment" }] }]
},
"map": { "value": { "column": "catchment_km2", "dataType": "Double" } }
},
{
"type": "Calculated",
"calculation": { "operation": "Geohash", "precision": 7, "inputs": [{ "column": "location" }] },
"map": { "value": { "column": "geohash", "dataType": "VarChar" } }
}
]
| catchment_km2 | geohash |
|---|---|
| 312 | 9r0czvq |
| 312 | 9q7hk92 |
| 312 | 9qdvxrj |
| 312 | 9myx9e6 |
| 312 | 9q9k4n9 |
| 312 | 9r14k9h |
catchment_km2 is the same on every row, because the buffer is measured on the surface at each
latitude. A circle with a radius of 10 km has an area of about 314 km². The buffer is a polygon with
straight sides, so its area is slightly less. A buffer measured in degrees would have a different area
at each latitude.
PointOnSurface
Centroid returns the center of mass of a geometry, which is outside the geometry itself for concave
geometries such as a horseshoe or a crescent. PointOnSurface always returns a point on the
geometry, which is required where the result is used as a representative location rather than as a
geometric center:
{
"operation": "PointOnSurface",
"inputs": [{ "column": "service_area" }]
}
Within and Intersects
Within and Intersects compare two geometries and return a boolean, so a table can record whether a
generated point is inside a generated area as well as the distance between them:
[
{
"type": "Geospatial",
"geometryType": "Point",
"boundingLocation": { "country": "us", "city": "Los Angeles", "state": "ca" },
"map": { "value": { "column": "warehouse", "dataType": "Geometry", "hidden": true } }
},
{
"type": "Calculated",
"calculation": { "operation": "Buffer", "units": "Kilometers", "distance": 20, "inputs": [{ "column": "warehouse" }] },
"map": { "value": { "column": "service_area", "dataType": "Geometry", "hidden": true } }
},
{
"type": "Geospatial",
"geometryType": "Point",
"boundingLocation": { "country": "us", "city": "Los Angeles", "state": "ca" },
"map": { "value": { "column": "customer_location", "dataType": "Geometry", "hidden": true } }
},
{
"type": "Calculated",
"calculation": { "operation": "Within", "inputs": [{ "column": "customer_location" }, { "column": "service_area" }] },
"map": { "value": { "column": "in_delivery_zone", "dataType": "Boolean" } }
},
{
"type": "Calculated",
"calculation": {
"operation": "Round",
"places": 1,
"inputs": [{ "operation": "Distance", "units": "Kilometers", "inputs": [{ "column": "warehouse" }, { "column": "customer_location" }] }]
},
"map": { "value": { "column": "distance_km", "dataType": "Double" } }
}
]
| in_delivery_zone | distance_km |
|---|---|
| true | 4.9 |
| true | 13 |
| true | 10.6 |
| false | 30.3 |
| true | 11.7 |
| false | 30 |
warehouse and customer_location are generated independently, so some customers are outside the
20 km service area. in_delivery_zone is true on every row where distance_km is 20 or less.
Conditions
| Operation | Inputs | Properties | Result |
|---|---|---|---|
If |
0 | when, then (required), else (default: null) |
then where when evaluates true, otherwise else. |
Case |
0 | cases (required), default (default: null) |
The result of the first arm in cases whose when evaluates true, or default where none do. |
Coalesce |
2+ | The first input that is not null. | |
Compare |
see Compare | comparison (required) |
Whether the inputs meet the condition set by comparison. Returns a boolean. |
And |
2+ | Whether every input is true. | |
Or |
2+ | Whether any input is true. | |
Not |
1 | The negation of the input. | |
Map |
1 | mappings (required), default (default: null) |
The input translated through mappings, returning default where no entry matches. See Map. |
Compare
comparison is one of:
| Comparison | Inputs | Tests |
|---|---|---|
Equals |
2 | The inputs are equal. Text is compared ignoring case. |
NotEquals |
2 | The inputs are not equal. |
GreaterThan |
2 | The first input sorts after the second. |
GreaterThanOrEqual |
2 | The first input sorts after the second, or equal to it. |
LessThan |
2 | The first input sorts before the second. |
LessThanOrEqual |
2 | The first input sorts before the second, or equal to it. |
In |
2+ | The first input equals any of the inputs after it. |
NotIn |
2+ | The first input equals none of the inputs after it. |
Between |
3 | The first input falls between the second and the third, inclusive at both ends. |
IsNull |
1 | The single input is null. |
IsNotNull |
1 | The single input is not null. |
Contains |
2 | The first input contains the second, ignoring case. |
StartsWith |
2 | The first input begins with the second, ignoring case. |
EndsWith |
2 | The first input ends with the second, ignoring case. |
Matches |
2 | The first input matches the regular expression supplied as the second. A pattern written in the request that is not a valid regular expression is rejected. A pattern read from a column that is not valid returns null for that row. |
The ordered comparisons, meaning GreaterThan, GreaterThanOrEqual, LessThan,
LessThanOrEqual, and Between, compare numeric values numerically, date and time values
chronologically, and all other values as text. 5 and "5" compare as equal, as do a Date column
and a literal date string, so a Cast is rarely required before a comparison.
[
{
"type": "String",
"mode": "Lookup",
"lookupValues": [{ "value": "Open" }, { "value": "Pending" }, { "value": "Closed" }],
"map": { "value": { "column": "status", "dataType": "VarChar" } }
},
{
"type": "Calculated",
"calculation": { "operation": "Compare", "comparison": "In", "inputs": [{ "column": "status" }, { "literal": "Open" }, { "literal": "Pending" }] },
"map": { "value": { "column": "is_active", "dataType": "Boolean" } }
},
{
"type": "Number",
"mode": "Whole",
"min": 1,
"max": 10,
"map": { "value": { "column": "priority", "dataType": "Integer" } }
},
{
"type": "Calculated",
"calculation": { "operation": "Compare", "comparison": "Between", "inputs": [{ "column": "priority" }, { "literal": 4 }, { "literal": 7 }] },
"map": { "value": { "column": "is_medium_priority", "dataType": "Boolean" } }
}
]
| status | is_active | priority | is_medium_priority |
|---|---|---|---|
| Closed | false | 2 | false |
| Open | true | 9 | false |
| Open | true | 5 | true |
| Open | true | 2 | false |
| Open | true | 1 | false |
| Closed | false | 9 | false |
If and Case
An order routed for review above a threshold, with a loyalty tier derived from the same total:
[
{
"type": "Number",
"mode": "Real",
"min": 50,
"max": 2500,
"scale": 2,
"seed": 2,
"map": { "value": { "column": "total", "dataType": "Decimal" } }
},
{
"type": "Calculated",
"calculation": {
"operation": "If",
"when": { "operation": "Compare", "comparison": "GreaterThan", "inputs": [{ "column": "total" }, { "literal": 1000 }] },
"then": { "literal": "Review" },
"else": { "literal": "Auto" }
},
"map": { "value": { "column": "approval_path", "dataType": "VarChar" } }
},
{
"type": "Calculated",
"calculation": {
"operation": "Case",
"cases": [
{ "when": { "operation": "Compare", "comparison": "LessThan", "inputs": [{ "column": "total" }, { "literal": 200 }] }, "then": { "literal": "Bronze" } },
{ "when": { "operation": "Compare", "comparison": "LessThan", "inputs": [{ "column": "total" }, { "literal": 1000 }] }, "then": { "literal": "Silver" } }
],
"default": { "literal": "Gold" }
},
"map": { "value": { "column": "tier", "dataType": "VarChar" } }
}
]
| total | approval_path | tier |
|---|---|---|
| 485.15 | Auto | Silver |
| 853.94 | Auto | Silver |
| 639.77 | Auto | Silver |
| 125.34 | Auto | Bronze |
| 1051.63 | Review | Gold |
| 666.94 | Auto | Silver |
Case evaluates its arms in order and returns the result of the first arm whose when is true.
default is returned for a total of 1000 or more.
And, Or, Not
Eligibility from two separate conditions:
[
{
"type": "Number",
"mode": "Whole",
"min": 16,
"max": 75,
"seed": 2,
"map": { "value": { "column": "age", "dataType": "Integer" } }
},
{
"type": "Boolean",
"percentageTrue": 60,
"seed": 1002,
"map": { "value": { "column": "has_license", "dataType": "Boolean" } }
},
{
"type": "Calculated",
"calculation": {
"operation": "And",
"inputs": [
{ "operation": "Compare", "comparison": "GreaterThanOrEqual", "inputs": [{ "column": "age" }, { "literal": 18 }] },
{ "column": "has_license" }
]
},
"map": { "value": { "column": "eligible_to_drive", "dataType": "Boolean" } }
}
]
| age | has_license | eligible_to_drive |
|---|---|---|
| 26 | true | true |
| 35 | false | false |
| 30 | true | true |
| 17 | true | false |
| 40 | true | true |
| 31 | true | true |
A boolean column may be supplied directly as an input, as has_license is here, without an enclosing
Compare.
Coalesce
Coalesce returns the first input that is not null, which applies where a value has a preferred
source and one or more fallbacks:
[
{
"type": "Name",
"countries": [{ "country": "us" }],
"part": "FirstName",
"caseOutput": "Proper",
"nullPercentageMin": 50,
"nullPercentageMax": 50,
"seed": 3,
"map": { "value": { "column": "preferred_name", "dataType": "VarChar" } }
},
{
"type": "Name",
"countries": [{ "country": "us" }],
"part": "FirstName",
"caseOutput": "Proper",
"seed": 1003,
"map": { "value": { "column": "legal_first_name", "dataType": "VarChar" } }
},
{
"type": "Calculated",
"calculation": { "operation": "Coalesce", "inputs": [{ "column": "preferred_name" }, { "column": "legal_first_name" }] },
"map": { "value": { "column": "display_name", "dataType": "VarChar" } }
}
]
preferred_name is null on about half the rows. On those rows, display_name contains
legal_first_name:
| preferred_name | legal_first_name | display_name |
|---|---|---|
| Muang | Tuere | Muang |
| Tkeyah | Tkeyah | |
| Lynzy | Aarron | Lynzy |
| Yatziri | Yatziri | |
| Agusta | Agusta | |
| Johnathan | Jyssica | Johnathan |
Map
Map provides a compact form of a Case in which every arm tests the same input for equality against
a fixed value:
[
{
"type": "String",
"mode": "Lookup",
"lookupValues": [{ "value": "ca" }, { "value": "ny" }, { "value": "tx" }, { "value": "wa" }],
"seed": 1,
"map": { "value": { "column": "state", "dataType": "VarChar" } }
},
{
"type": "Calculated",
"calculation": {
"operation": "Map",
"mappings": [
{ "from": "ca", "to": { "literal": "West" } },
{ "from": "wa", "to": { "literal": "West" } },
{ "from": "ny", "to": { "literal": "East" } }
],
"default": { "literal": "Other" },
"inputs": [{ "column": "state" }]
},
"map": { "value": { "column": "region", "dataType": "VarChar" } }
}
]
| state | region |
|---|---|
| ca | West |
| ca | West |
| ny | East |
| wa | West |
| tx | Other |
| ny | East |
The equivalent Case would require one when and Compare pair per state in place of each
mappings entry. from is matched using the same rules as Equals, which ignores case for text. A
from of null matches a null input.
Position in the table
| Operation | Inputs | Properties | Result |
|---|---|---|---|
RowNumber |
0 | startAt (default 1) |
The index of the current row, counting from startAt. |
RunningTotal |
1 | The input added to the accumulated total of all preceding rows. |
[
{
"type": "Calculated",
"calculation": { "operation": "RowNumber", "startAt": 1000 },
"map": { "value": { "column": "line_number", "dataType": "Integer" } }
},
{
"type": "Number",
"mode": "Real",
"min": 10,
"max": 500,
"scale": 2,
"map": { "value": { "column": "amount", "dataType": "Decimal" } }
},
{
"type": "Calculated",
"calculation": { "operation": "RunningTotal", "inputs": [{ "column": "amount" }] },
"map": { "value": { "column": "invoice_to_date", "dataType": "Decimal" } }
}
]
| line_number | amount | invoice_to_date |
|---|---|---|
| 1000 | 325.92 | 325.92 |
| 1001 | 470.57 | 796.49 |
| 1002 | 176.15 | 972.64 |
| 1003 | 100.92 | 1073.56 |
| 1004 | 200.77 | 1274.33 |
| 1005 | 322.32 | 1596.65 |
RunningTotal accumulates as rows are generated, so invoice_to_date on any row is the sum of
amount for that row and all preceding rows. Rows are generated in order, so the total is computed in
a single forward pass. Exactness follows the same rule as the arithmetic operations. Accumulating
Decimal values keeps the total exact. Once an inexact value is added, the total is floating point for
every later row.
RunningTotal does not follow the rule described in Null handling. A null input
leaves the accumulated total unchanged and returns that total, rather than returning null for the row,
so a null contributes nothing to the total instead of invalidating every row after it.
RunningTotal accumulates over the rows generated so far. It is not a whole-table aggregate. No
operation computes an aggregate over the complete table, such as a sum or an average applied back to
every row, because that requires a second pass over generated data. RowNumber and RunningTotal
both operate in a single forward pass, so every calculation remains a function of the current row and
the rows already generated.
Types
| Operation | Inputs | Properties | Result |
|---|---|---|---|
Cast |
1 | targetType (required) |
The input converted to targetType, one of the column data types listed in Data types. |
ParseNumber |
1 | The input parsed as a number, returning null where it does not parse. |
[
{
"type": "String",
"mode": "Lookup",
"lookupValues": [{ "value": "42" }, { "value": "7" }, { "value": "153" }],
"map": { "value": { "column": "raw_quantity", "dataType": "VarChar", "hidden": true } }
},
{
"type": "Calculated",
"calculation": { "operation": "Cast", "targetType": "Integer", "inputs": [{ "column": "raw_quantity" }] },
"map": { "value": { "column": "quantity", "dataType": "Integer" } }
},
{
"type": "Calculated",
"calculation": { "operation": "Multiply", "inputs": [{ "column": "quantity" }, { "literal": 2 }] },
"map": { "value": { "column": "doubled", "dataType": "Integer" } }
}
]
| quantity | doubled |
|---|---|
| 153 | 306 |
| 42 | 84 |
| 42 | 84 |
| 153 | 306 |
| 7 | 14 |
| 42 | 84 |
quantity is cast explicitly because Multiply requires a numeric input and raw_quantity is text.
The final map.value conversion requires no corresponding Cast, as described in
The result's data type.
ParseNumber performs the same conversion as Cast to a numeric type. It is provided separately for
cases where the purpose of the calculation is to determine whether a text value parses as a number,
returning null where it does not.
Examples
Each example below is an array of consecutive field objects, in the order they appear in a table's
fields array within a full request. See Building a Request for how it fits into a
full DatasetRequest.
An email address from a name
Template joins the two names, Slug reduces them to a lower-case token separated by a period, and
Concat adds the domain.
[
{
"type": "Person",
"countries": [{ "country": "us" }],
"map": {
"firstName": { "column": "first_name", "dataType": "VarChar" },
"lastName": { "column": "last_name", "dataType": "VarChar" }
}
},
{
"type": "Calculated",
"calculation": {
"operation": "Concat",
"inputs": [
{
"operation": "Slug",
"separator": ".",
"inputs": [{ "operation": "Template", "template": "{first_name} {last_name}" }]
},
{ "literal": "@example.com" }
]
},
"map": { "value": { "column": "email", "dataType": "VarChar" } }
}
]
| first_name | last_name | |
|---|---|---|
| Naida | Krajicek | naida.krajicek@example.com |
| Renaye | Elo | renaye.elo@example.com |
| Felicha | Declercq | felicha.declercq@example.com |
| Kaedan | Braschler | kaedan.braschler@example.com |
| Gwendy | Cuautle | gwendy.cuautle@example.com |
| Xian | Forche | xian.forche@example.com |
Net, tax and gross for an order line
Each calculation reads the columns before it, including the results of earlier calculations.
[
{
"type": "Number",
"mode": "Whole",
"min": 1,
"max": 10,
"map": { "value": { "column": "quantity", "dataType": "Integer" } }
},
{
"type": "Number",
"mode": "Real",
"min": 1,
"max": 100,
"scale": 2,
"precision": 5,
"map": { "value": { "column": "unit_price", "dataType": "Decimal" } }
},
{
"type": "Calculated",
"calculation": {
"operation": "Round",
"places": 2,
"inputs": [{ "operation": "Multiply", "inputs": [{ "column": "quantity" }, { "column": "unit_price" }] }]
},
"map": { "value": { "column": "net", "dataType": "Decimal" } }
},
{
"type": "Calculated",
"calculation": {
"operation": "Round",
"places": 2,
"inputs": [{ "operation": "Multiply", "inputs": [{ "column": "net" }, { "literal": 0.2 }] }]
},
"map": { "value": { "column": "tax", "dataType": "Decimal" } }
},
{
"type": "Calculated",
"calculation": { "operation": "Add", "inputs": [{ "column": "net" }, { "column": "tax" }] },
"map": { "value": { "column": "gross", "dataType": "Decimal" } }
}
]
| quantity | unit_price | net | tax | gross |
|---|---|---|---|---|
| 3 | 57.19 | 171.57 | 34.31 | 205.88 |
| 8 | 67.75 | 542 | 108.4 | 650.4 |
| 5 | 23.63 | 118.15 | 23.63 | 141.78 |
| 9 | 81.37 | 732.33 | 146.47 | 878.8 |
| 8 | 78.91 | 631.28 | 126.26 | 757.54 |
| 5 | 3.18 | 15.9 | 3.18 | 19.08 |
A due date and a billing period from an invoice date
[
{
"type": "Date",
"mode": "Random",
"min": "2026-01-01",
"max": "2026-06-30",
"map": { "value": { "column": "invoice_date", "dataType": "Date" } }
},
{
"type": "Calculated",
"calculation": { "operation": "DateAdd", "unit": "Days", "inputs": [{ "column": "invoice_date" }, { "literal": 30 }] },
"map": { "value": { "column": "due_date", "dataType": "Date" } }
},
{
"type": "Calculated",
"calculation": { "operation": "FormatDate", "pattern": "MMMM yyyy", "inputs": [{ "column": "invoice_date" }] },
"map": { "value": { "column": "billing_period", "dataType": "VarChar" } }
}
]
| invoice_date | due_date | billing_period |
|---|---|---|
| 2026-04-29 | 2026-05-29 | April 2026 |
| 2026-03-28 | 2026-04-27 | March 2026 |
| 2026-03-05 | 2026-04-04 | March 2026 |
| 2026-06-19 | 2026-07-19 | June 2026 |
| 2026-03-30 | 2026-04-29 | March 2026 |
| 2026-05-08 | 2026-06-07 | May 2026 |
Account codes and a running balance
RowNumber padded to six digits gives each row a code, and RunningTotal accumulates the amounts down
the table.
[
{
"type": "Calculated",
"calculation": {
"operation": "Template",
"template": "CUST-{0}",
"inputs": [{ "operation": "PadLeft", "length": 6, "padCharacter": "0", "inputs": [{ "operation": "RowNumber" }] }]
},
"map": { "value": { "column": "customer_code", "dataType": "VarChar" } }
},
{
"type": "Number",
"mode": "Real",
"min": -500,
"max": 1000,
"includeNegative": true,
"scale": 2,
"precision": 7,
"map": { "value": { "column": "amount", "dataType": "Decimal" } }
},
{
"type": "Calculated",
"calculation": { "operation": "RunningTotal", "inputs": [{ "column": "amount" }] },
"map": { "value": { "column": "balance", "dataType": "Decimal" } }
}
]
| customer_code | amount | balance |
|---|---|---|
| CUST-000001 | 756.85 | 756.85 |
| CUST-000002 | -355.92 | 400.93 |
| CUST-000003 | 67.5 | 468.43 |
| CUST-000004 | 747.94 | 1216.37 |
| CUST-000005 | 272.64 | 1489.01 |
| CUST-000006 | 494.64 | 1983.65 |
Distance from a fixed depot
A literal point and a generated column can be inputs to the same operation.
[
{
"type": "Geospatial",
"geometryType": "Point",
"centerX": -0.1276,
"centerY": 51.5072,
"radius": 20,
"radiusUnitOfMeasure": "Kilometer",
"map": { "value": { "column": "drop_off", "dataType": "Geometry" } }
},
{
"type": "Calculated",
"calculation": {
"operation": "Round",
"places": 1,
"inputs": [
{
"operation": "Distance",
"units": "Kilometers",
"inputs": [{ "literal": "POINT(-0.1276 51.5072)" }, { "column": "drop_off" }]
}
]
},
"map": { "value": { "column": "km_from_depot", "dataType": "Double" } }
}
]
| drop_off | km_from_depot |
|---|---|
POINT(-0.158876 51.437287) |
8.1 |
POINT(-0.060277 51.674456) |
19.2 |
POINT(-0.283584 51.392677) |
16.7 |
POINT(0.077301 51.47339) |
14.7 |
POINT(0.151676 51.498924) |
19.4 |
POINT(-0.028164 51.666397) |
19 |
A default for a missing value
Coalesce returns the first input that is not null, so an empty preferred_contact falls back to
Email.
[
{
"type": "String",
"mode": "Lookup",
"lookupValues": [{ "value": "Mobile" }, { "value": "Email" }, { "value": "Post" }],
"nullPercentageMin": 30,
"nullPercentageMax": 30,
"map": { "value": { "column": "preferred_contact", "dataType": "VarChar" } }
},
{
"type": "Calculated",
"calculation": { "operation": "Coalesce", "inputs": [{ "column": "preferred_contact" }, { "literal": "Email" }] },
"map": { "value": { "column": "contact_method", "dataType": "VarChar" } }
}
]
| preferred_contact | contact_method |
|---|---|
| Post | Post |
| Post | Post |
| Post | Post |
| Post | Post |