Paths versus literals

An operand starting with $ is a path: it is looked up in the situation. Anything else is a literal: used as written.

Reaching inside

Dots go into objects and lists. Lists are numbered from 0:
If any step doesn’t exist, the whole value is missing.

Missing is null

A missing value behaves like null:
  • exists and truthy are false; empty is true.
  • equals with a missing side is true only if the other is also missing.
  • The greater/less comparisons are false.
  • is one of is false if the list is missing; is not one of is true.
  • contains and the text tests are false.

None of and missing values

None of flips whatever its inside says, including for missing data:
  • None of these rules pass:
    • Compare values: $record.status equals archived
For a record with no status, the inside is false, so None of is true: the record passes. If that isn’t what you want, say it positively: $record.status is one of [open, draft]. Prefer does not equal to None of with an equals inside: they behave the same, and it’s clearer.

Types

Rules compare what the data holds: Case-insensitive compare isn’t built in. Store a normalised (lowercase) column and compare against that, or use matches with (?i).

Lists

contains works on lists ($auth.roles) and text. in takes the value on the left and the list on the right.
  • $auth.user_id is one of $record.member_ids (is the caller in the record’s list)
  • $record.member_ids contains $auth.user_id (the same, the other way round)
Both are fine. Use whichever reads better.

Ownership across types

If a table’s owner_id is an integer and tokens carry a string, eq still works, because ids are normalised before they are compared.

The empty string

An empty text is empty (is empty true, is set false) but it exists. A form that submits an empty text for an optional field makes exists true. Use is set for “has a real value”.

Patterns that bite

The third is the classic: when the caller has no organization and a record has no org, “both null” is equal, so everyone matches. Always require the caller’s side to be set.

Operators

Each operator’s behaviour.

Testing

Prove edge cases.