Representing logic as data in JSON

前端 未结 12 1864
眼角桃花
眼角桃花 2021-01-29 21:37

For business reasons we need to externalize some conditional logic into external files: preferably JSON.

A simple filter-by scenario could be handled by adding a node a

12条回答
  •  你的背包
    2021-01-29 22:00

    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 { }
    

    Or BNF (ish? my cs teachers wouldn't be proud)

    WHEREGROUP: = [ CONDITION | ('AND'|'OR') | WHEREGROUP ]
    CONDITION: = {field, opp, val}
    

    With the following Parsing Rules:

    1. AND is optional (I typically add it for readability). If logical LogicalOperator is left out between conditions, it will automatically joins them with AND
    2. Inner arrays are parsed as nested groups (EG get wrapped in ())
    3. 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

提交回复
热议问题