改进的过滤器

JEP

9

作者

詹姆斯·萨耶维尼

状态

认可的

创建

2014年7月7日

摘要

jep7引入了过滤器表达式,这是一种机制,允许根据每个列表元素匹配一个表达式来选择列表元素。虽然这个概念很有用,但实际的比较器表达式并不能满足许多常见查询。此JEP通过建议对 and-expressionsnot-expressionparen-expressionsunary-expressions . 现在,通过一个强大的过滤器功能,可以处理大多数查询的新增功能。

动机

jep7引入了过滤器查询,基本上如下所示:

foo[?lhs omparator rhs]

其中左手侧(lhs)和右手侧(rhs)都是 expression ,比较器是 ==, !=, <, <=, >, >= .

这为JMESPath添加了一个有用的特性:能够根据列表中的每个元素计算表达式来过滤列表。

自从jep7成为JMESPath的一部分以来,已经指出了许多过滤器表达式无法解决的情况。下面是每种缺失功能的示例。

或表达式

首先,用户希望能够基于匹配的一个或多个表达式进行过滤。例如,给定:

{
  "cities": [
    {"name": "Seattle", "state": "WA"},
    {"name": "Los Angeles", "state": "CA"},
    {"name": "Bellevue", "state": "WA"},
    {"name": "New York", "state": "NY"},
    {"name": "San Antonio", "state": "TX"},
    {"name": "Portland", "state": "OR"}
  ]
}

用户可能希望选择西海岸的位置,在这个特定的示例中,这意味着 WAORCA . 由于的语法,不可能将其表示为筛选器表达式 expression comparator expression . 理想情况下,用户应该能够使用:

cities[?state == `WA` || state == `OR` || state == `CA`]

JMESPath已经支持Or表达式,只是在筛选器表达式的上下文中不支持。

和表达式

过滤器表达式的下一个缺少的特性是对和表达式的支持。实际上有点奇怪,JMESPath支持Or表达式,但不支持And表达式。例如,给定具有权限的用户帐户列表:

{
  "users": [
    {"name": "user1", "type": "normal"", "allowed_hosts": ["a", "b"]},
    {"name": "user2", "type": "admin", "allowed_hosts": ["a", "b"]},
    {"name": "user3", "type": "normal", "allowed_hosts": ["c", "d"]},
    {"name": "user4", "type": "admin", "allowed_hosts": ["c", "d"]},
    {"name": "user5", "type": "normal", "allowed_hosts": ["c", "d"]},
    {"name": "user6", "type": "normal", "allowed_hosts": ["c", "d"]}
  ]
}

我们要查找对名为的主机具有权限的管理员用户 c . 理想情况下,过滤器表达式为:

users[?type == `admin` && contains(allowed_hosts, `c`)]

一元表达式

想一想C或Java等语言中的if语句。而您可以编写一个if语句,它看起来像:

if (foo == bar) { ... }

也可以使用一元表达式,例如:

if (allowed_access) { ... }

或:

if (!allowed_access) { ... }

添加对一元表达式的支持会在过滤布尔值时带来自然的语法。而不是::

foo[?boolean_var == `true`]

用户可以使用::

foo[?boolean_var]

作为一个更现实的例子,为 users 以上数据:

{
  "users": [
    {"name": "user1", "is_admin": false, "disabled": false},
    {"name": "user2", "is_admin": true, "disabled": true},
    {"name": "user3", "is_admin": false, "disabled": false},
    {"name": "user4", "is_admin": true, "disabled": false},
    {"name": "user5", "is_admin": false, "disabled": true},
    {"name": "user6", "is_admin": false, "disabled": false}
  ]
}

如果我们想获得启用了帐户的所有管理员用户的名称,我们可以说::

users[?is_admin == `true` && disabled == `false]

但用更自然、更简洁的说法是:

users[?is_admin && !disabled]

可以这样说,这种语法不是绝对必要的。这是真的。但是,在过滤器表达式中添加对一元表达式的支持的主要原因是用户期望这种语法,当这不是一种受支持的语法时,用户会感到惊讶。尤其是现在,我们基本上是锚定在这个JEP中过滤的类C语法,用户将更加期待一元表达式。

Paren表达式

一次 ||&& 语句已被引入,有时您需要重写这些运算符的优先级。

A paren-expression 允许用户重写表达式的优先顺序,例如。 (a || b) && c ,而不是的默认优先级 a || (b && c) 对于表达式 a || b && c .

规范

语法有几个更新:

and-expression         = expression "&&" expression
not-expression         = "!" expression
paren-expression       = "(" expression ")"

另外, filter-expression 规则更新为更通用:

bracket-specifier      =/ "[?" expression "]"

这个 list-filter-expr 现在是一个更普遍的 comparator-expression ::

comparator-expression  = expression comparator expression

现在只是一个表达式:

expression /= comparator-expression

最后 current-node 现在允许作为泛型表达式::

expression /= current-node

运算符优先级

此JEP引入和表达式,通常定义为:

expression     = or-expression / and-expression / not-expression
or-expression  = expression "||" expression
and-expression = expression "&&" expression
not-expression = "!" expression

但是,如果遵循此当前模式,则无法以正确的优先级解析表达式。更标准的表达方式是:

expression          = or-expression
or-expression       = and-expression "||" and-expression
and-expression      = not-expression "&&" not-expression
not-expression      = "!" expression

新的布尔表达式的优先级与大多数其他语言定义布尔表达式的方式相匹配。从最弱的束缚到最紧的束缚:

  • 或者- ||

  • 以及- &&

  • 一元不- !

例如, a || b && c 被解析为 a || (b && c) 而不是 (a || b) && c .

规范中的运算符优先级列表现在将显示为:

  • 管道- |

  • 或者- ||

  • 以及- &&

  • 一元不- !

  • R球拍- ]

现在这些表达式被允许作为一般表达式 expressions ,必须定义其原始上下文之外的语义。

和表达式

作为参考,JMESPath规范已经将以下值定义为“类假”值:

  • 空列表: []

  • 空对象: {{}}

  • 空字符串: ""

  • 假布尔值: false

  • 空值: null

任何不是虚假价值的价值都是类似真理的价值。

and-expression 与其他语言中的和表达式具有相似的语义。如果左侧的表达式是一个类似于真值的值,则返回右侧的值。否则,将返回左侧表达式的结果。这也简化为期望真值表:

与表达式真值表

LHS

RHS

结果

这是一个 logical conjunction (AND) .

下面是和表达式的几个示例:

实例

search(True && False, {"True": true, "False": false}) -> false
search(Number && EmptyList, {"Number": 5, EmptyList: []}) -> []
search(foo[?a == `1` && b == `2`],
       {"foo": [{"a": 1, "b": 2}, {"a": 1, "b": 3}]}) -> [{"a": 1, "b": 2}]

不是表达式

A not-expression 否定表达式的结果。如果表达式的结果是一个类真值,则 not-expression 将此值更改为 false . 如果表达式的结果是一个类假值,则 not-expression 将此值更改为 true .

实例

search(!True, {"True": true}) -> false
search(!False, {"False": false}) -> true
search(!Number, {"Number": 5}) -> false
search(!EmptyList, {"EmptyList": []}) -> true

Paren表达式

A paren-expression 允许用户重写表达式的优先顺序,例如。 (a || b) && c .

实例

search(foo[?(a == `1` || b ==`2`) && c == `5`],
       {"foo": [{"a": 1, "b": 2, "c": 3}, {"a": 3, "b": 4}]}) -> []

理论基础

这个JEP将一些只允许在特定构造中使用的标记引入到更通用的 expression 规则。明确地:

  • The current-node (@) was previously only allowed in function expressions, but is now allowed as a general expression.

  • 这个 filter-expression 现在接受任何武断 expression .

  • 这个 list-filter-expr 现在只是普通的 comparator-expression ,这又是一个将军 expression .

以前的语法规则的作用域最小有几个原因。jep7引入了过滤表达式,其中一个主要原因是为了保持规范“有目的地最小化”。事实上,jep7的末尾声明“有几个扩展可以在将来添加。”这实际上正是JEP提出的,jep7的建议。