出于商业原因,我们需要将一些条件逻辑外部化为外部文件:最好是JSON。
可以通过添加节点来处理简单的过滤方案:
"filter": [
{
"criteria": "status",
"value": "open",
"condition": "=="
}
]
多个条件可以通过数组中的其他值来处理。
"filter": [
{
"criteria": "status",
"value": "open",
"condition": "=="
},
{
"criteria": "condition2",
"value": "value2",
"condition": "=="
}
]
然而,当我们处理涉及AND或OR的复杂条件时,它会有点混乱。
问题:在JSON中表示这种逻辑是否有标准化(甚至被广泛接受)的格式?如果由你决定,你会怎么做?
注意:第一个答案是一个可编辑的维基,所以任何感觉都可以改进。
答案 0 :(得分:48)
如果必须使用标准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" }]
。但是有些情况(比如示例),前一种语法不能忠实地表示写入的条件;你需要额外的技巧,比如翻译条件或使用虚拟属性名称。后一种语法应始终有效。
答案 1 :(得分:28)
我需要一种格式:
所以我建立了一种我打电话给JsonLogic的格式。规则是JSON对象,操作符位于键位置,值位置中有一个或一组参数。 (灵感来自Amazon CloudFormation functions。)任何参数都可以是另一条规则,因此您可以构建任意深度的逻辑。
我还为它编写了两个解析器:JsonLogic for JavaScript和JsonLogic for PHP。
cHao的例子将写成
{ "and", [
{"==", [ {"var" : "var1"}, "value1" ]},
{ "or", [
{"==", [ {"var" : "var2"}, "value2" ]},
{"==", [ {"var" : "var3"}, "value3" ]}
]}
]}
var
这里是获取"数据"的属性的运算符。对象,与"规则一起传递"解析器的对象,例如:
jsonLogic(
{"==", [{"var":"filling"}, "apple"]} // rule, is this pie apple?
{"filling":"apple", "temperature":100} // data, a pie I'm inspecting
);
// true
还有更多可能的运算符(大于,不等于,数组内,三元等),并且两个解析器都可以在GitHub上使用(带有单元测试和文档)。
答案 2 :(得分:4)
我有类似的需求(在javascript中构建一个sql where子句)。我创建了以下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;
}
我创建了queryOperation,就像这样:
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
}
}
};
当我将queryOperation传递给ParseQuery时,它返回 ((tradedate = Thu Jul 24 17:30:37 EDT 2014))AND(((systemid = 9))OR((systemid = 10)))
我需要添加一些类型转换和其他运算符,但基本结构可以工作。
答案 3 :(得分:4)
顺便说一句,IBM DB2 supports logic statements encoded in JSON。
布尔运算看起来像cHao的解决方案和Amazon CloudFormation之间的交叉:
{"$and":[{"age":5},{"name":"Joe"}]}
对我而言,比较操作看起来就像音译SQL一样。 (而不是亚马逊或Russellg或cHao向抽象语法树的转变。)
{"age":{"$lt":3}}
答案 4 :(得分:2)
我的同事提出了这个可能的解决方案:
“所有OR条件都是数组,AND条件是对象,
例如,OR可以匹配数组中的任何对象:
[
{
"var1":"value1"
},
{
"var2":"value2"
},
{
"var3":"value3"
}
]
将是
{
"var1":"val1",
"var2":"val2",
"var3":"val3"
}
答案 5 :(得分:2)
请查看(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')匹配数据库中的错误记录。该规则进一步要求$ bug变量为$ bind(ed)对包含$或中的相关状态值(打开和关闭)的模式。只有那些在$ bug中的状态值满足规则主体的所有部分的bug记录才有资格获得结果。
最终使用转换规范转换结果:transform : '$wip'
字面上要求查询的$ wip变量中返回的所有值的数组。
答案 6 :(得分:1)
在Jeremy Wadhams评论之后,我实现了一个解析器,希望它可以为您提供帮助:
https://play.golang.org/p/QV0FQLrTlyo
这个想法是使用$
或$and
这样的$lte
字符在特殊键中设置所有逻辑运算符。
例如:
{
"$or":[
{
"age":{
"$lte":3
}
},
{
"name":"Joe"
},
{
"$and":[
{
"age":5
},
{
"age ":{
" $nin ":[
1,
2,
3
]
}
}
]
}
]
}
致谢。
翻译为:
( age <= 3 OR name = Joe OR ( age = 5 AND age NOT IN (1,2,3) ) )
答案 7 :(得分:0)
逻辑可以通过“ set”:[“ a”,“ b” ...]上的“ logicOp”:“ Operator”来实现 对于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"
}
答案 8 :(得分:0)
我想出了这种格式,其主要目标是读取尽可能接近实际SQL的内容。
这是打字稿中的Type定义:
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:内部数组被解析为嵌套组(EG被包装在()
中)
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 AND(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格式的条件>
答案 9 :(得分:0)
Formula parser +一些JS代码将数据放入公式中,是另一种解决方法described with example in this answer。
答案 10 :(得分:0)
我只是想通过在JavaScript中为答案中提到的JSON结构定义解析逻辑来提供帮助:https://stackoverflow.com/a/53215240/6908656
这对于那些在为此编写解析逻辑时费力的人很有帮助。
evaluateBooleanArray = (arr, evaluated = true) => {
if (arr.length === 0) return evaluated;
else if (typeof arr[0] === "object" && !Array.isArray(arr[0])) {
let newEvaluated = checkForCondition(arr[0]);
return evaluateBooleanArray(arr.splice(1), newEvaluated);
} else if (typeof arr[0] === "string" && arr[0].toLowerCase() === "or") {
return evaluated || evaluateBooleanArray(arr.splice(1), evaluated);
} else if (typeof arr[0] === "string" && arr[0].toLowerCase() === "and") {
return evaluated && evaluateBooleanArray(arr.splice(1), evaluated);
} else if (Array.isArray(arr[0])) {
let arrToValuate = [].concat(arr[0]);
return evaluateBooleanArray(
arr.splice(1),
evaluateBooleanArray(arrToValuate, evaluated)
);
} else {
throw new Error("Invalid Expression in Conditions");
}
};
因此,此处的参数arr将是条件数组,该条件的定义如附件链接所述。
答案 11 :(得分:0)
首先想到的是
dict1={'$lte':'<','$nin':'not in '}
def updateOp(subdictItem):
for ites in subdictItem:
ops = ites
print dict1.get(ops), subdictItem.get(ops), type(subdictItem.get(ops))
if type(subdictItem.get(ops)) is list:
valuelist=subdictItem.get(ops)
strlist=' ,'.join([str(x) for x in valuelist])
sub = dict1.get(ops) + "(" +strlist +")"
else:
sub = dict1.get(ops) +' ' + str(subdictItem.get(ops))
return sub
def jsonString(input_dict):
items=''
itemslist=[]
list = []
for item in input_dict:
op=item
itemssublist=[]
# print "item",op
for subitem in input_dict.get(op):
# print("subitem",subitem)
for ite in subitem:
if ite not in ('and','or'):
# print('ite_raw',ite,subitem.get(ite))
sub=''
if type(subitem.get(ite)) is dict:
sub=updateOp(subitem.get(ite))
else:
sub='=' + str(subitem.get(ite))
itemssublist.append(ite+sub)
else:
item1=jsonString(subitem)
itemssublist.append(item1)
delimiter=" "+op+ " "
items= "("+delimiter.join(itemssublist)+")"
return items
if __name__ == "__main__":
input_dict={}
with open('ops.json','r') as f:
input_dict=json.load(f)
print input_dict
test= jsonString(input_dict)
#result : (age< 3 or name=Joe or (age=5 and age not in (1 ,2 ,3)))
ops.json file:
{
"or":[
{
"age":{
"$lte":3
}
},
{
"name":"Joe"
},
{
"and":[
{
"age":5
},
{
"age ":{
"$nin":[
1,
2,
3
]
}
}
]
}
]
}