Skip to main content
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:
first_name = "John"
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

OperatorMeaning
=equal
!=not equal
> >= < <=ordering (numbers, dates)
~=case-insensitive regex match
inlist 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:
FormExampleParsed as
Quoted string"John", "München"string
Integer50000integer
Decimal2.5, 2,5float
Booleantrue, falseboolean
Date2024-01-01date
Blankblank, nil, nullthe “no value” sentinel
List["a", "b"], [1, 2, 3]list (only with in)
Any other textpendingstring (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:
SuffixMeaning
(none)The clause is checked against the entry effective today.
, 2024-01-01The clause is checked against the entry effective on 2024-01-01.
, >= 2024-01-01The 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

first_name = "John"
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".
employee_no = blank
Employees with no employee number set.
employee_no != blank
Employees who do have an employee number.
salary_amount > 50000, *
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.
first_name ~= "^Jo..$"
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.