【问题标题】:Subtree matching with jq与jq匹配的子树
【发布时间】:2021-12-07 18:11:04
【问题描述】:

我正在寻找一种有效的方法来计算一个 JSON 文档是否是另一个 JSON 文档的子树/子集。 给定一个 JSON 文档 a

{
    "a": {
        "a": {"aa": ["uvw", "abc"]},
        "b": {
            "ba": {
                "baa": ["none", "1"],
                "bab": ["0"]
            },
            "bb": {
                "baa": ["yyy"],
                "bab": ["some_string"]
            }
        },
        "c": {
            "ca": ["dd", "cc"],
            "cb": 2,
            "cc": "-" 
        }
    },
    "e": "abc",
    "d": 45678
}

还有一个 JSON 文档 b

{
    "a": {
        "a": {"aa": ["abc"]},
        "b": {
            "ba": {
                "baa": ["none"],
                "bab": ["0"]
            },
            "bb": {
                "bab": ["some_string"]
            }
        },
        "c": {
            "ca": ["cc"]
        }
    },
    "e": "abc"
}

我想确定b 是否是a 的子集

因此,我希望有一个布尔值(或类似 {'match': true} 的东西)表明 ba 的子集。

我尝试了以下

.[0] as $a | .[1] as $b
| reduce ($a|paths) as $p (null; ($a|getpath($p)) as $va
| try($b|getpath($p)) as $vb
| if $va == $vb then setpath(["match"];true) else setpath(["match"];false) end)

但我错过了跟踪失败比较的方法。

如何使用jq 完成这种子树匹配?

提前感谢您的帮助!

【问题讨论】:

  • 你能用文字定义你的意思吗?我会以完全不同的方式解释“子集”和“子树”,而且感觉都不完全准确。从您的示例的形状来看,我猜您想知道b 中的每个路径是否在a 中都有一个匹配的路径,包含相同类型的实体,此外,如果该实体是标量,那么两者元素是否具有相同的值?
  • 好吧,不,我什至不认为这是您要问的...您认为["a", "b", "c"]["c", "b", "a"] 的子集吗? ["a", "a", "a"]["a"] 的子集吗?你如何比较复杂的数组成员? [{"red":1},{"green":1},{"blue":1}][{"red":1,"green":1,"blue":1}] 的子集吗?这是一个棘手的问题,您需要精确地指定它。
  • @Jeremy - 您的文档a 不太正确:.a.b 下的第二个“ba”应该是“bb”。
  • @Weeble 感谢您指出这一点。实际上,我会考虑 ["a", "b", "c"]["c", "b", "a"] 匹配。我认为["a", "a", "a"] 不是 ["a"] 的子集(但是,反向测试应该被认为是正确的)。我认为复杂的数组成员超出了这里的范围,尽管我正在寻找最通用的解决方案。
  • @peak 感谢您指出这一点。其实,这源于一点懈怠,对不起!现在已更正。

标签: json jq


【解决方案1】:

正如@weeble 所指出的,当涉及到数组时,“子树”的概念并不是那么简单。特别是,针对您的问题的简单路径值方法不符合您的要求,因此以下内容有点复杂,但希望布局和命名法有助于澄清问题。

首先,一些辅助函数:

def max(s): reduce s as $x (0; if . == null or $x > . then $x else . end);

def elementOf($x):
  . as $in
  | if ($x|type) == "array"
    then $x|index($in)
    else false end;

# drill down
def shift($n):
  if $n == 0 then .
  elif type | (. != "object" and . != "array") then empty
  elif $n == 1 then .[]
  else .[] | shift($n - 1)
  end;

接下来是主函数,它定义了一个(递归的)子函数来处理 相对简单的案例:

def subtree_of($b):
  def isScalar: type | . != "array" and . != "object";

  # direct_subtree_of($b) checks that every path/atomic-value pair of $a is also in $b
  def direct_subtree_of($b):
    . as $a
    | first( ($a|paths(scalars)) as $p
        | ($a | getpath($p)) as $va
        | ([try ($b|getpath($p)) // empty]) as $vb
        | if ($vb |length > 0) 
             and (($va == $vb[0])
                  or ($va | subtree_of($vb[0]))
                  or ($va | elementOf($b | getpath( $p[:-1] ))))
          then empty
          else 0
          end) // 1
      | . == 1 ;
    
  . as $a
  | if isScalar then $a == $b
    else direct_subtree_of($b)
         or (max($a|paths|length) as $ma
            | max($b|paths|length) as $mb
            | ($mb - $ma) as $diff
            | any(range(1; 1+$diff);
                  any(. as $i | $a | subtree_of($b | shift($i)); . )) )
    end ;

更正你的a后:

a | subtree_of(b)

计算为true

【讨论】:

  • 感谢您的精彩回答!我尝试了它并扩展了您的脚本以将结果作为 JSON 格式以{"match": true} 的形式返回。但是,我有一个额外的询问:您对如何在不使用debug 的情况下计算结果有什么建议吗?我打算调用 jq-script 以通过其 jq-bindings 从程序 Python 执行子树匹配(以加快速度,因为我的 Python 实现相当慢)。在我的测试中,由于产生了调试输出,您提供的脚本不起作用。 (实际上是我假设的python绑定问题)您对此有什么帮助吗?
  • @Jeremy - 请注意,我刚刚上传了一个带有错误修复的新版本,并减去了调试语句。删除它们(几乎总是)是安全的。
  • 嗯。这使得["b","b","b"] 成为["a","b","b"] 的一个子集,这又提出了一个问题,规范是什么?我怀疑子集数组的每个成员都必须对应于超集数组的单个成员,并且子集数组的任何两个成员都不能使用超集的相同元素。我也有一种感觉,如果我们不将数组限制为仅包含标量,那么问题可能是 NP 完全的。
  • @Weeble 将 JSON 文档视为分层树,则不应将 ["b","b","b"] 视为匹配 ["a","b","b"],因为挂在父节点(数组)下方的叶子的结构不同.正如您所建议的那样,应该制定规范:“子集数组的每个成员必须对应于超集数组的单个成员”。实际上,我认为,我可以忍受将数组限制为仅包含标量。感谢您提出这个问题。
  • (Re: NP-completeness。我道歉:我记错了,混淆了我的理论。我应该说我怀疑可能没有最坏情况的多项式时间算法,假设我们允许非标量数组。它不能是 NP 完全的,因为答案是布尔值,只能通过回答问题来验证。)
猜你喜欢
  • 1970-01-01
  • 2022-01-08
  • 1970-01-01
  • 2021-10-11
  • 1970-01-01
  • 1970-01-01
  • 2012-07-07
  • 1970-01-01
  • 2015-06-27
相关资源
最近更新 更多