The dated property query language is the syntax used in the Employee list filter in Backoffice, the system-data employee browser, the system-integration sync-preview filter, and the MCP list_employees tool. A query compiles into a filter that runs against an entity’s dated properties, so the same data model that powers the rest of Twine also drives this filter syntax.
A query like the one below finds currently employed employees whose current salary is above 50,000:
employment_terminated = false AND salary_amount > 50000
A single clause
The smallest unit of a query is a clause:
A clause is <property> <operator> <value>. The property name is the same name used elsewhere in Twine for that field - see Employee for the full list of properties on the most common entity. When no operator is written, = is assumed.
Comparison operators
| Operator | Meaning |
|---|
= | equal |
!= | not equal |
> >= < <= | ordering (numbers, dates) |
~= | case-insensitive regex match |
in | list membership |
Equality and inequality (=, !=) are type-tolerant: employee_no = 123 matches a stored "123" and vice versa. This is intentional, because property values arrive from many different source systems and can land in storage with varying types.
Value types
The parser recognises the following value forms:
| Form | Example | Parsed as |
|---|
| Quoted string | "John", "München" | string |
| Integer | 50000 | integer |
| Decimal | 2.5, 2,5 | float |
| Boolean | true, false | boolean |
| Date | 2024-01-01 | date |
| Blank | blank, nil, null | the “no value” sentinel |
| List | ["a", "b"], [1, 2, 3] | list (only with in) |
| Any other text | pending | string (naked) |
Strings
Quoted strings ("John") are the canonical form. Use quotes whenever the value could be confused with another type, contains a comma, or begins or ends with whitespace.
Naked strings are the fallback: anything in value position that does not match a typed form is captured as a string up to the next comma. note = hej baberiba parses cleanly, with the value hej baberiba.
A typo in a value silently degrades to a naked string. salary_amount > 50O00 (with a letter O, not zero) parses as the string "50O00" and will never match a numeric salary. When a query is not returning what you expect, check that each value parsed as the type intended.
Quoted strings have no escape syntax. The first " after the opening quote ends the string.
Numbers
Integers are bare digits. Floats accept both . and , as the decimal separator. Negative integers cannot be written as bare digits - use a float (-5.0) or a quoted string ("-5").
Booleans
true and false, lowercase only. Anything else (True, 1, yes) is treated as a string.
Dates
The canonical date form is YYYY-MM-DD. The separator can be a space or omitted entirely, so 2024 01 01 and 20240101 are also accepted. Invalid calendar dates such as 2024-02-30 cause the parse to fail.
Blank
blank, nil, and null are equivalent and all parse to the same “no value” sentinel. field = blank matches when the property is missing from the entity entirely, when its value is null, or when its value is an empty list. field != blank is the negation. Other operators against blank (such as >) are not meaningful and return no match.
Lists
Bracketed, comma-separated, and used only with in:
employee_no in ["EMP001", "EMP002", "EMP003"]
Strings inside a list must be quoted. Mixed types are allowed ([1, 2.5] works).
Filtering by when a value was valid
Each clause runs against an entity’s dated property timeline. By default it inspects only the currently effective entry. An optional , <date constraint> after the value changes which entries the comparison runs against.
There are four modes:
| Suffix | Meaning |
|---|
| (none) | The clause is checked against the entry effective today. |
, 2024-01-01 | The clause is checked against the entry effective on 2024-01-01. |
, >= 2024-01-01 | The clause matches if any entry whose valid_from satisfies the comparison also satisfies the value check. |
, * | The clause matches if any entry in the timeline (current or historical) satisfies the value check. |
Worked salary-history examples:
salary_amount > 50000 # current salary is above 50000
salary_amount > 50000, 2022-06-01 # salary effective on 2022-06-01 was above 50000
salary_amount > 50000, * # the timeline contains any salary above 50000
salary_amount > 50000, >= 2024-01-01 # a salary entry starting on or after 2024-01-01 is above 50000
, 2024-01-01 (no operator) selects the value effective on that date, which is the most recent entry whose valid_from is on or before the date. This is different from , <= 2024-01-01, which matches if any historical entry’s valid_from is on or before the date. The first picks one entry to compare against; the second considers many.
Combining clauses
Clauses can be combined with AND and OR. Both keywords are uppercase only - lowercase and is captured as part of a naked string. AND binds tighter than OR, so:
first_name = "John" OR first_name = "Jane" AND salary_amount > 50000
parses as first_name = "John" OR (first_name = "Jane" AND salary_amount > 50000). Parentheses override precedence:
(first_name = "John" OR first_name = "Jane") AND salary_amount > 50000
Worked examples
Employees whose current first name is John.
employee_no in ["EMP001", "EMP002"]
Employees matching one of the given employee numbers.
employment_terminated = false
Employees who are currently employed. The value is the boolean false, not the string "false".
Employees with no employee number set.
Employees who do have an employee number.
Employees whose salary history contains any value above 50,000.
salary_amount > 50000, 2024-01-01
Employees whose salary effective on 2024-01-01 was above 50,000.
Employees whose first name matches the case-insensitive regular expression - for example, John and Joel match, Joke matches, joak does not (the pattern requires exactly four characters).
(first_name = "John" OR first_name = "Jane") AND salary_amount > 50000
Employees named John or Jane whose current salary is above 50,000.
Where this query language is used
The same syntax is accepted in every place that filters dated-property-backed entities:
- The Employee list filter in Backoffice.
- The Backoffice system-data employee browser.
- The system-integration sync-preview filter.
- The
query argument on the MCP list_employees tool.
Limitations and gotchas
- No
NOT keyword. Negation is expressed at the operator level with != or != blank. A negative lookahead can be used inside a ~= regex when needed.
AND and OR are case-sensitive. Lowercase and and or are not recognised and are captured as part of the surrounding value.
- Quoted strings have no escape syntax. A literal
" cannot appear inside a quoted string.
- Typos in numeric or date values degrade to strings. The query still parses, but never matches the property’s real value.
- Negative integers must be written as a float or as a quoted string. Bare negatives such as
age = -5 do not parse.
- Property names accept ASCII plus the Latin-1 supplement (
Ä, Å, Ö, ø, ñ, and so on). Other Unicode scripts cannot be used as property names, though they are valid inside quoted string values.
- Invalid calendar dates fail the parse rather than silently degrading -
2024-02-30 returns an error.
- The
id field of a dated property is not addressable from this language. It exists in the data model (see properties with multiple simultaneous values) but the text query syntax always considers every id for a given key.