Skip to contentSkip to navigationSkip to topbar
Page tools
Useful for sharing or LLM
Accelerate development with AI

On this page
Looking for more inspiration?Visit the

Operators and precedence


Supported operators

supported-operators page anchor
OperatorDescriptionExample
!Logical NOT!profile.trait.Account.isTest
-Unary negation-1
*, /, %Multiplication, division, remainderprofile.trait.Account.lifetimeValue * 100
+, -Addition, subtractionprofile.trait.Account.lifetimeValue + 100
==, !=Equal to, not equal toprofile.trait.Account.status == "active"
<, <=, >, >=Less than, less than or equal to, greater than, greater than or equal toprofile.trait.Account.age >= 18
inMembership"US" in ["US", "CA"]
&&Logical ANDprofile.trait.Account.isActive && profile.trait.Account.country == "US"
||Logical ORprofile.trait.Account.isAdmin || profile.trait.Account.isOwner
?:Conditional (ternary): condition ? value_if_true : value_if_falseprofile.trait.Account.plan == "enterprise" ? profile.trait.Account.lifetimeValue > 10000 : profile.trait.Account.lifetimeValue > 1000
., []Field access, method calls, and list indexingprofile.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).

OrderOperatorsNotes
1 (tightest)., []Field access, method calls, and indexing
2!, unary -Logical NOT, unary negation
3*, /, %Multiplication, division, remainder
4+, -Addition, subtraction
5==, !=, <, <=, >, >=, inComparison 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 c and a < b == c evaluate 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:

  1. profile.trait.Account.isTest, profile.trait.Account.age, profile.trait.Account.hasGuardianConsent, and profile.trait.Account.country are field accesses, so they resolve first.
  2. !profile.trait.Account.isTest negates the value of isTest.
  3. The parentheses force profile.trait.Account.age >= 18 || profile.trait.Account.hasGuardianConsent to evaluate as a group, even though || would otherwise bind more loosely than the surrounding &&.
  4. profile.trait.Account.country in ["US", "CA"] evaluates the membership check.
  5. 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.