Подтвердить что ты не робот

Представление логики как данных в JSON

По соображениям бизнеса нам необходимо вытеснить некоторую условную логику во внешние файлы: желательно JSON.

Простой сценарий по сценарию можно обработать, добавив node следующим образом:

"filter": [
  {
    "criteria": "status",
    "value": "open",
    "condition": "=="
  }
]

Несколько условий могут обрабатываться дополнительными значениями в массиве.

"filter": [
  {
    "criteria": "status",
    "value": "open",
    "condition": "=="
  },
  {
    "criteria": "condition2",
    "value": "value2",
    "condition": "=="
  }
]

Однако, это немного запутывает, когда мы обрабатываем сложные условия, связанные с AND или OR.

Вопрос: существует ли стандартизованный (или даже широко распространенный) формат для представления такой логики в JSON? Как бы вы это сделали, если бы это было за вас?

ПРИМЕЧАНИЕ. Первый ответ был сделан редактируемой вики, поэтому его можно улучшить любым, кто считает, что это может быть.

4b9b3361

Ответ 1

Если вы должны реализовать это с помощью стандартного JSON, я бы рекомендовал нечто похожее на Lisp "S-выражения". Условие может быть либо простым объектом, либо массивом, первая запись которого является логической операцией, которая их объединяет.

Например:

["AND",
    {"var1" : "value1"},
    ["OR",
        { "var2" : "value2" },
        { "var3" : "value3" }
    ]
]

будет представлять var1 == value1 AND (var2 == value2 OR var3 == value3).

Если вы предпочитаете краткость по консистенции, вы также можете позволить объекту иметь несколько свойств, которые неявно соединяются с помощью AND. Например, { "a": "b", "c": "d" } будет эквивалентно ["AND", { "a": "b" }, { "c": "d" }]. Но есть случаи (например, пример), где прежний синтаксис не может точно представлять условие как написанное; вам потребуется дополнительная обманка, например, перевод условия или использование имен фиктивных свойств. Последний синтаксис всегда должен работать.

Ответ 2

Мне нужен был формат, который:

  • Поддержка сравнений, отличных от равенства.
  • Пусть переменные появляются в любой позиции, а не просто сравниваются с литералами.
  • Быть последовательным, кратким, безопасным и расширяемым.

Итак, я создал формат, который я называю JsonLogic. Правило - это объект JSON с оператором в позиции ключа и один или массив аргументов в позиции значения. (Вдохновленный Функции Amazon CloudFormation.) Любой аргумент может быть другим правилом, поэтому вы можете построить произвольно глубокую логику.

Я также написал для него два парсера: JsonLogic для JavaScript и JsonLogic для PHP.

Пример cHao будет записан как

{ "and", [
    {"==", [ {"var" : "var1"}, "value1" ]},
    { "or", [
        {"==", [ {"var" : "var2"}, "value2" ]},
        {"==", [ {"var" : "var3"}, "value3" ]}
    ]}
]}

var Вот оператор, чтобы получить свойство объекта "data", переданное вместе с объектом "rule" в парсер, например:

jsonLogic(
    {"==", [{"var":"filling"}, "apple"]}    // rule, is this pie apple?
    {"filling":"apple", "temperature":100}  // data, a pie I'm inspecting
);
// true

В GitHub (с модульными тестами и документацией) есть еще много возможных операторов (больше, не равно, в массиве, тройной и т.д.), и оба анализатора доступны.

Ответ 3

Кстати, IBM DB2 поддерживает логические операторы, закодированные в JSON.

Логические операции выглядят как нечто среднее между решением cHao и Amazon CloudFormation:

{"$and":[{"age":5},{"name":"Joe"}]}

Операции сравнения выглядят для меня как транслитерированный SQL. (Вместо Amazon, Russellg или cHao движение к абстрактному синтаксическому дереву.)

{"age":{"$lt":3}}

Ответ 4

У меня была аналогичная потребность (для создания предложения sql where в javascript). Я создаю следующую функцию javascript:

  function parseQuery(queryOperation){
        var query="";
        if (queryOperation.operator == 'and')
            query = "(" + parseQuery(queryOperation.leftOp) + ") AND (" + parseQuery(queryOperation.rightOp) + ")";
        if (queryOperation.operator == 'or')
            query = "(" + parseQuery(queryOperation.leftOp) + ") OR (" + parseQuery(queryOperation.rightOp) + ")";
        if (queryOperation.operator == '=')
            query = "(" + queryOperation.leftOp +" = "+ queryOperation.rightOp + ")";
        return query;
    }

Я создаю свой запрос. Так:

 var queryObject =             {          
            operator: 'and',
            leftOp: {
                leftOp: 'tradedate',
                operator: '=',
                rightOp: new Date()
            },
            rightOp: {
                operator: 'or',
                leftOp: {
                    leftOp: 'systemid',
                    operator: '=',
                    rightOp: 9
                },
                rightOp: {
                    leftOp: 'systemid',
                    operator: '=',
                    rightOp:10
                }
            }
        };

Когда я передаю свой запросOperation в ParseQuery, он возвращает ((tradedate = Thu Jul 24 17:30:37 EDT 2014)) AND (((systemid = 9)) ИЛИ ((systemid = 10)))

Мне нужно добавить некоторые преобразования типов и другие операторы, но основная структура работает.

Ответ 5

Мой коллега предложил это возможное решение:

"все условия OR были бы массивом, в то время как условия AND были бы объектами,

Например, OR может соответствовать любому из объектов массива:

[
  {
    "var1":"value1"
  },
  {
    "var2":"value2"
  },
  {
    "var3":"value3"
  }
]

И будет

{ 
  "var1":"val1",
  "var2":"val2",
  "var3":"val3"
}

Ответ 6

Пожалуйста, проверьте (JSL) [https://www.npmjs.com/package/lib-jsl]. Кажется, что это соответствует приведенному описанию.

Вот пример:

var JSL = require('lib-jsl');

var bugs = [
    [{ bug : { desc: 'this is bug1 open', status : 'open' } }],
    [{ bug : { desc: 'this is bug2 resolved', status : 'resolved' } }],
    [{ bug : { desc: 'this is bug3 closed' , status : 'closed' } }],
    [{ bug : { desc: 'this is bug4 open', status : 'open' } }],
    [{ bug : { desc: 'this is bug5 resolved', status : 'resolved' } }],
    [{ bug : { desc: 'this is bug6 open', status : 'open' } }],

    [   { workInProgress : '$bug'},
        { bug : '$bug'},
        { $or : [
            { $bind : [ '$bug', { status : 'open'} ] },
            { $bind : [ '$bug', { status : 'resolved'} ] }
        ] }
    ]
];
var query = [{workInProgress : '$wip'}]
var transform = '$wip'
var jsl = new JSL ({
    rules : bugs,
    query : query,
    transform : transform
});
var retval = jsl.run();
console.log(JSON.stringify(retval, null,2));

Ответ:

[
  {
    "desc": "this is bug1 open",
    "status": "open"
  },
  {
    "desc": "this is bug2 resolved",
    "status": "resolved"
  },
  {
    "desc": "this is bug4 open",
    "status": "open"
  },
  {
    "desc": "this is bug5 resolved",
    "status": "resolved"
  },
  {
    "desc": "this is bug6 open",
    "status": "open"
  }
]

Основная работа выполняется с помощью запроса, определенного в правиле workInProgress:

[   { workInProgress : '$bug'},
    { bug : '$bug'},
    { $or : [
        { $bind : [ '$bug', { status : 'open'} ] },
        { $bind : [ '$bug', { status : 'resolved'} ] }
    ] }
]

Это правило можно прочитать как:

Чтобы удовлетворить запрос с помощью workInProgress, мы определяем переменную {workInProgress : '$bug'}, которую затем переходим к совпадению со всеми ошибками в базе данных, используя следующую часть правила {bug : '$bug'}. Эта часть соответствует всем ошибкам, так как форма объекта (его ключи: "ошибка" ) соответствует записям ошибок в базе данных. Правило далее запрашивает переменную $bug как $bind (ed) против шаблонов, содержащих соответствующие значения состояния (открытые и закрытые) в пределах $или. Только те записи ошибок, значение статуса которых в $bug удовлетворяет всем частям тела правила, имеют право на результат.

Результат окончательно преобразуется с использованием спецификации преобразования: transform : '$wip', которая буквально запрашивает массив всех значений, возвращаемых в переменной $wip запроса.

Ответ 7

Я придумал этот формат с основной целью чтения как можно ближе к SQL.

Вот Тип определения в машинописи:

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

Или BNF (да? Мои преподаватели CS не будут гордиться)

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

со следующими правилами синтаксического анализа:

  1. AND является необязательным (я обычно добавляю его для удобства чтения). Если логический LogicalOperator пропущен между условиями, он автоматически объединит их с AND
  2. Внутренние массивы анализируются как вложенные группы (например, они заключены в ())
  3. этот тип не ограничивает несколько логических операторов последовательно (к сожалению). Я справился с этим, просто используя последний, хотя вместо этого я мог выдать ошибку времени выполнения.

Вот несколько примеров (ссылка на игровую площадку):

1 И 2 (И вывод)

[
    { field: 'name', opp: '=', val: '123' },
    { field: 'otherfield', opp: '>=', val: 123 }
]

1 ИЛИ 2

[
    { field: 'name', opp: '=', val: '123' },
    'OR',
    { field: 'annualRevenue', opp: '>=', val: 123 }
]

(1 ИЛИ 2) И (3 ИЛИ 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 И (2 ИЛИ 3)

[
    { field: 'name', opp: '=', val: '123' },
    'AND',
    [
        { field: 'annualRevenue', opp: '>=', val: 123 },
        'OR',
        { field: 'active', opp: '=', val: true }
    ]
]

1 И 2 ИЛИ 3

[
    { field: 'name', opp: '=', val: '123' },
    'AND',
    { field: 'annualRevenue', opp: '>=', val: 123 },
    'OR',
    { field: 'active', opp: '=', val: true }
]

1 ИЛИ (2 И (3 ИЛИ 4))

[
    { field: 'name', opp: '=', val: '123' },
    'OR',
    [
        { field: 'annualRevenue', opp: '>=', val: 123 },
        'AND',
        [
            { field: 'active', opp: '=', val: true },
            'OR',
            { field: 'accountSource', opp: '=', val: 'web' }
        ]
    ]
]

Как вы можете видеть, если вы удалите , и имена свойств, а затем просто замените [] на (), вы получите условие в формате SQL.

Ответ 8

Логика может быть реализована с помощью "logicOp": "Operator" для "set": ["a", "b"...] Для примера cHau:

"var": {
         "logicOp": "And",
         "set": ["value1",
                 {
                    "LogicOp": "Or",
                    "set": ["value2", "value3"]
                 }
              ]
       }

Также могут быть другие атрибуты/операции для набора, например

"val": { "operators": ["min": 0, "max": 2], "set": ["a", "b", "c"] } 

В воскресенье с двумя шариками одного или нескольких видов мороженого, 1 начинкой и взбитым кремом

"sunday": {
            "icecream": { 
                          "operators": [ "num": 2,
                                        "multipleOfSingleItem": "true"],
                          "set": ["chocolate", "strawberry", "vanilla"]
                        },
            "topping": {
                          "operators": ["num": 1],
                          "set": ["fudge", "caramel"]
                       },
            "whipcream": "true"
          }

Ответ 10

Вы пытались использовать такой сайт, который помог бы вам построить свою логику? http://jsonlogic.com