Я бы использовал другую структуру, потому что:
- Нет необходимости проводить различие между
operator
и join
. groups
и rules
действительно должны быть одним и тем же понятием. Если в нем должны быть вложенные правила, просто используйте вложенное свойство rules
. - Когда вы используете типы
in
или between
, свойство value
действительно должно быть массивом. Парсер должен вводить запятые и другой синтаксис SQL. Не ставьте запятые внутри одной строки (хотя у вас нет таких примеров) - Избегайте парсинга
not_between
и not_is_null
по отдельности; это то же самое, что between
и is_null
, но с NOT
, примененным к нему. Это можно сделать более динамично.
Вот предлагаемая структура для вашего примера:
{
"type": "AND",
"rules": [{
"id": 100,
"column": "dd_Tttp",
"type": "equal",
"value": true
}, {
"type": "AND",
"rules": [{
"id": 200,
"column": "dd_tering",
"type": "equal",
"value": true
}, {
"id": 201,
"column": "dd_Size",
"type": "in",
"value": ["Standard"] // You should use arrays for type="in"
}, {
"id": 202,
"column": "dd_Lotpth",
"type": "equal",
"value": "12"
}, {
"type": "AND",
"rules": [{
"id": 300,
"column": "dd_cat",
"type": "equal",
"value": "34"
}, {
"id": 301,
"column": "dd_Cot",
"type": "in",
"value": ["Coftlassic"] // You should use arrays for type="in"
}, {
"id": 302,
"column": "dd_dse",
"type": "equal",
"value": "2020-01-01"
}, {
"id": 303,
"column": "dd_turflaid",
"type": "equal",
"value": true
}]
}]
}, {
"type": "AND",
"rules": [{
"id": 100,
"column": "dd_get",
"type": "equal",
"value": true
}, {
"id": 101,
"column": "dd_ccc",
"type": "in",
"value": ["Standard"] // // You should use arrays for type="in"
}]
}]
};
Вот фрагмент кода, который выдает SQL из этого:
const op = { equal: " = ", not_equal: " <> ", less: " < ", less_or_equal: " <= ",
greater: " > ", greater_or_equal: " >= " };
function toSql(rule) {
// recursive case:
if (rule.rules) return "(" + rule.rules.map(toSql).join("\n" + rule.type + " ") + ")";
// Base case (it is an atomic rule):
if (op[rule.type]) return rule.column + op[rule.type] + JSON.stringify(rule.value);
// Deal with "not": that is just a negation of the opposite
let type = rule.type.replace(/^not_/, "");
let sql = rule.column + (
type === "in" ? " IN (" + JSON.stringify(rule.value).slice(1,-1) + ")"
: type === "between" ? " BETWEEN " + rule.value.map(item => JSON.stringify(item)).join(" AND ")
: type === "is_null" ? " IS NULL"
: "<UNKNOWN TYPE:" + type + ">"
);
return type === rule.type ? sql : "NOT (" + sql + ")";
}
let rule = {"type": "AND","rules": [{"id": 100,"column": "dd_Tttp","type": "equal","value": true}, {"type": "AND","rules": [{"id": 200,"column": "dd_tering","type": "equal","value": true}, {"id": 201,"column": "dd_Size","type": "in","value": ["Standard"]}, {"id": 202,"column": "dd_Lotpth","type": "equal","value": "12"}, {"type": "AND","rules": [{"id": 300,"column": "dd_cat","type": "equal","value": "34"}, {"id": 301,"column": "dd_Cot","type": "in","value": ["Coftlassic"]}, {"id": 302,"column": "dd_dse","type": "equal","value": "2020-01-01"}, {"id": 303,"column": "dd_turflaid","type": "equal","value": true}]}]}, {"type": "AND","rules": [{"id": 100,"column": "dd_get","type": "equal","value": true}, {"id": 101,"column": "dd_ccc","type": "in","value": ["Standard"]}]}]};
console.log(toSql(rule));
Отключение правил
В комментарии вы добавляете требование, в котором вы хотите отключить некоторые правила. В этом случае сначала отфильтруйте правила по новому свойству disabled
.
Вот тот же фрагмент с этим изменением, и где последние два (вложенные) правила отключены:
const op = { equal: " = ", not_equal: " <> ", less: " < ", less_or_equal: " <= ",
greater: " > ", greater_or_equal: " >= " };
function toSql(rule) {
// recursive case:
if (rule.rules) {
// Filter out recursive return values that are empty (using Boolean):
let sql = rule.rules.map(toSql).filter(Boolean).join("\n" + rule.type + " ");
// return that SQL in parentheses, except when it is empty
return sql ? "(" + sql + ")" : "";
}
// Base case (it is an atomic rule):
if (rule.disabled) return ""; // Return empty string when disabled
if (op[rule.type]) return rule.column + op[rule.type] + JSON.stringify(rule.value);
// Deal with "not": that is just a negation of the opposite
let type = rule.type.replace(/^not_/, "");
let sql = rule.column + (
type === "in" ? " IN (" + JSON.stringify(rule.value).slice(1,-1) + ")"
: type === "between" ? " BETWEEN " + rule.value.map(item => JSON.stringify(item)).join(" AND ")
: type === "is_null" ? " IS NULL"
: "<UNKNOWN TYPE:" + type + ">"
);
return type === rule.type ? sql : "NOT (" + sql + ")";
}
let rule = {"type": "AND","rules": [{"id": 100,"column": "dd_Tttp","type": "equal","value": true}, {"type": "AND","rules": [{"id": 200,"column": "dd_tering","type": "equal","value": true}, {"id": 201,"column": "dd_Size","type": "in","value": ["Standard"]}, {"id": 202,"column": "dd_Lotpth","type": "equal","value": "12"}, {"type": "AND","rules": [{"id": 300,"column": "dd_cat","type": "equal","value": "34"}, {"id": 301,"column": "dd_Cot","type": "in","value": ["Coftlassic"]}, {"id": 302,"column": "dd_dse","type": "equal","value": "2020-01-01"}, {"id": 303,"column": "dd_turflaid","type": "equal","value": true}]}]}, {"type": "AND","rules": [{"id": 100,disabled:true,"column": "dd_get","type": "equal","value": true}, {"id": 101,disabled:true,"column": "dd_ccc","type": "in","value": ["Standard"]}]}]};
console.log(toSql(rule));