I came up with this format with the primary goal of reading as close as possible to actually SQL.
Here's the Type def in typescript:
type LogicalOperator = 'AND' | 'OR';
type Operator = '=' | '<=' | '>=' | '>' | '<' | 'LIKE' | 'IN' | 'NOT IN';
type ConditionParams = {field: string, opp: Operator, val: string | number | boolean};
type Conditions = ConditionParams | LogicalOperator | ConditionsList;
interface ConditionsList extends Array<Conditions> { }
Or BNF (ish? my cs teachers wouldn't be proud)
WHEREGROUP: = [ CONDITION | ('AND'|'OR') | WHEREGROUP ]
CONDITION: = {field, opp, val}
With the following Parsing Rules:
AND
is optional (I typically add it for readability). If logical LogicalOperator
is left out between conditions, it will automatically joins them with AND
- Inner arrays are parsed as nested groups (EG get wrapped in
()
)
- this type does not restrict multiple logical operators consecutively (unfortunately). I handled this by just using the last one, although I could have thrown a runtime error instead.
Here are some examples (typescript playground link):
1 AND 2 (AND inferred)
[
{ field: 'name', opp: '=', val: '123' },
{ field: 'otherfield', opp: '>=', val: 123 }
]
1 OR 2
[
{ field: 'name', opp: '=', val: '123' },
'OR',
{ field: 'annualRevenue', opp: '>=', val: 123 }
]
(1 OR 2) AND (3 OR 4)
[
[
{ field: 'name', opp: '=', val: '123' },
'OR',
{ field: 'name', opp: '=', val: '456' }
],
'AND',
[
{ field: 'annualRevenue', opp: '>=', val: 123 },
'OR',
{ field: 'active', opp: '=', val: true }
]
]
1 AND (2 OR 3)
[
{ field: 'name', opp: '=', val: '123' },
'AND',
[
{ field: 'annualRevenue', opp: '>=', val: 123 },
'OR',
{ field: 'active', opp: '=', val: true }
]
]
1 AND 2 OR 3
[
{ field: 'name', opp: '=', val: '123' },
'AND',
{ field: 'annualRevenue', opp: '>=', val: 123 },
'OR',
{ field: 'active', opp: '=', val: true }
]
1 OR (2 AND (3 OR 4))
[
{ field: 'name', opp: '=', val: '123' },
'OR',
[
{ field: 'annualRevenue', opp: '>=', val: 123 },
'AND',
[
{ field: 'active', opp: '=', val: true },
'OR',
{ field: 'accountSource', opp: '=', val: 'web' }
]
]
]
As you can see, if you were to remove ,
and property names, then just replace the []
with ()
, you'd basically have the condition in SQL format