Files
plunk/packages/shared/src/operators.ts
T

152 lines
4.9 KiB
TypeScript

/**
* Operator Behavior Documentation
*
* This file documents the consistent behavior of operators across the entire application.
* These operators are used in:
* - Segment filtering (SegmentService)
* - Workflow CONDITION steps (WorkflowExecutionService)
*
* IMPORTANT: Both implementations MUST behave identically for consistency.
*/
/**
* All supported operators
*/
export const OPERATORS = {
// String operators
EQUALS: 'equals',
NOT_EQUALS: 'notEquals',
CONTAINS: 'contains',
NOT_CONTAINS: 'notContains',
// Numeric operators
GREATER_THAN: 'greaterThan',
LESS_THAN: 'lessThan',
GREATER_THAN_OR_EQUAL: 'greaterThanOrEqual',
LESS_THAN_OR_EQUAL: 'lessThanOrEqual',
// Existence operators
EXISTS: 'exists',
NOT_EXISTS: 'notExists',
// Temporal operators (segments only)
WITHIN: 'within',
} as const;
/**
* Operators available for segments
*/
export const SEGMENT_OPERATORS = [
OPERATORS.EQUALS,
OPERATORS.NOT_EQUALS,
OPERATORS.CONTAINS,
OPERATORS.NOT_CONTAINS,
OPERATORS.GREATER_THAN,
OPERATORS.LESS_THAN,
OPERATORS.GREATER_THAN_OR_EQUAL,
OPERATORS.LESS_THAN_OR_EQUAL,
OPERATORS.EXISTS,
OPERATORS.NOT_EXISTS,
OPERATORS.WITHIN,
] as const;
/**
* Operators available for workflow conditions
* Note: 'within' is NOT supported in workflow conditions
*/
export const WORKFLOW_CONDITION_OPERATORS = [
OPERATORS.EQUALS,
OPERATORS.NOT_EQUALS,
OPERATORS.CONTAINS,
OPERATORS.NOT_CONTAINS,
OPERATORS.GREATER_THAN,
OPERATORS.LESS_THAN,
OPERATORS.GREATER_THAN_OR_EQUAL,
OPERATORS.LESS_THAN_OR_EQUAL,
OPERATORS.EXISTS,
OPERATORS.NOT_EXISTS,
] as const;
/**
* Operator Behavior Rules
*
* CRITICAL: These rules apply to BOTH SegmentService and WorkflowExecutionService
*
* 1. EQUALS (=)
* - Matches when actualValue === expectedValue
* - CAN match null/undefined if expectedValue is also null/undefined
* - String fields: case-insensitive (email)
* - Boolean fields: strict comparison
*
* 2. NOT_EQUALS (≠)
* - ONLY matches when field EXISTS and actualValue !== expectedValue
* - Does NOT match missing fields (undefined/null)
* - Use notExists operator if you want to find missing fields
* - Use OR[notEquals, notExists] if you want both
*
* 3. CONTAINS (substring)
* - ONLY matches when field EXISTS and contains substring
* - Does NOT match missing fields (undefined/null)
* - String fields: case-insensitive (email)
* - Converts values to strings before comparison
*
* 4. NOT_CONTAINS (not substring)
* - ONLY matches when field EXISTS and does NOT contain substring
* - Does NOT match missing fields (undefined/null)
* - Use notExists operator if you want to find missing fields
* - Use OR[notContains, notExists] if you want both
*
* 5. GREATER_THAN (>)
* - ONLY matches when field EXISTS and Number(actualValue) > Number(expectedValue)
* - Does NOT match missing fields (undefined/null)
* - Converts values to numbers before comparison
*
* 6. LESS_THAN (<)
* - ONLY matches when field EXISTS and Number(actualValue) < Number(expectedValue)
* - Does NOT match missing fields (undefined/null)
* - Converts values to numbers before comparison
*
* 7. GREATER_THAN_OR_EQUAL (≥)
* - ONLY matches when field EXISTS and Number(actualValue) >= Number(expectedValue)
* - Does NOT match missing fields (undefined/null)
* - Converts values to numbers before comparison
*
* 8. LESS_THAN_OR_EQUAL (≤)
* - ONLY matches when field EXISTS and Number(actualValue) <= Number(expectedValue)
* - Does NOT match missing fields (undefined/null)
* - Converts values to numbers before comparison
*
* 9. EXISTS
* - Matches when actualValue !== undefined && actualValue !== null
* - Matches empty strings, zero, false (these are valid existing values)
*
* 10. NOT_EXISTS
* - Matches when actualValue === undefined || actualValue === null
* - Does NOT match empty strings, zero, or false
*
* 11. WITHIN (temporal - segments only)
* - Matches when date field is within specified time period
* - Requires unit: 'days' | 'hours' | 'minutes'
* - Example: within 7 days = createdAt >= (now - 7 days)
*/
/**
* Example: Finding contacts without a field vs with a different value
*
* To find contacts where plan is NOT "basic":
* - Use: { field: 'data.plan', operator: 'notEquals', value: 'basic' }
* - Result: Matches contacts with plan='premium', plan='free', etc.
* - Does NOT match: Contacts without plan field
*
* To find contacts WITHOUT a plan field:
* - Use: { field: 'data.plan', operator: 'notExists' }
* - Result: Matches contacts where plan is undefined or null
*
* To find contacts where plan is NOT "basic" OR plan doesn't exist:
* - Use: OR[
* { field: 'data.plan', operator: 'notEquals', value: 'basic' },
* { field: 'data.plan', operator: 'notExists' }
* ]
* - Result: Matches all except contacts with plan='basic'
*/