Operators and precedence
| Operator | Description | Example |
|---|---|---|
! | Logical NOT | !profile.trait.Account.isTest |
- | Unary negation | -1 |
*, /, % | Multiplication, division, remainder | profile.trait.Account.lifetimeValue * 100 |
+, - | Addition, subtraction | profile.trait.Account.lifetimeValue + 100 |
==, != | Equal to, not equal to | profile.trait.Account.status == "active" |
<, <=, >, >= | Less than, less than or equal to, greater than, greater than or equal to | profile.trait.Account.age >= 18 |
in | Membership | "US" in ["US", "CA"] |
&& | Logical AND | profile.trait.Account.isActive && profile.trait.Account.country == "US" |
|| | Logical OR | profile.trait.Account.isAdmin || profile.trait.Account.isOwner |
?: | Conditional (ternary): condition ? value_if_true : value_if_false | profile.trait.Account.plan == "enterprise" ? profile.trait.Account.lifetimeValue > 10000 : profile.trait.Account.lifetimeValue > 1000 |
., [] | Field access, method calls, and list indexing | profile.trait.Account.status, str.contains("x"), list[0] |
When your filter criteria combine more than one operator, Cohorts evaluates the operators that bind tightest first, then works outward. The table below lists operator groups from the tightest binding (evaluated first) to the loosest binding (evaluated last).
| Order | Operators | Notes |
|---|---|---|
| 1 (tightest) | ., [] | Field access, method calls, and indexing |
| 2 | !, unary - | Logical NOT, unary negation |
| 3 | *, /, % | Multiplication, division, remainder |
| 4 | +, - | Addition, subtraction |
| 5 | ==, !=, <, <=, >, >=, in | Comparison and membership. All seven operators share one precedence level and evaluate from left to right. |
| 6 | && | Logical AND |
| 7 | || | Logical OR |
| 8 (loosest) | ?: | Conditional |
Key points to remember:
- Comparison and membership share one tier. Expressions such as
a == b in canda < b == cevaluate left to right; none of the comparison or membership operators binds tighter than the others. - Field access binds tighter than everything else, including unary
!. In!profile.trait.Account.isTest, the field access resolves first, and then!negates the result.
Use parentheses whenever the intended grouping is not obvious. Parentheses remove ambiguity for anyone who reads the criteria.
!profile.trait.Account.isTest && (profile.trait.Account.age >= 18 || profile.trait.Account.hasGuardianConsent) && profile.trait.Account.country in ["US", "CA"]
Evaluated from the tightest to the loosest binding:
profile.trait.Account.isTest,profile.trait.Account.age,profile.trait.Account.hasGuardianConsent, andprofile.trait.Account.countryare field accesses, so they resolve first.!profile.trait.Account.isTestnegates the value ofisTest.- The parentheses force
profile.trait.Account.age >= 18 || profile.trait.Account.hasGuardianConsentto evaluate as a group, even though||would otherwise bind more loosely than the surrounding&&. profile.trait.Account.country in ["US", "CA"]evaluates the membership check.- The two
&&operators combine all three results, left to right.
These criteria match profiles that are not test accounts, are either at least 18 years old or have guardian consent, and are located in the United States or Canada.
- Browse the available functions.
- See example criteria for common filtering patterns.