Xquery - 如何匹配量词表达式中的两个序列

时间:2018-03-20 17:12:44

标签: xquery contains

与许多人一样,我正在处理关于XML的Mondial数据库。如果XQuery语法没有尽力破坏它,那将是一块蛋糕。

let $inland := //province/@id
where every $sea in //sea satisfies
$sea/located/@province != $inland
return $inland

我在上面尝试做的是找到所有"内陆"各省,的省份旁边都有海。然而,这并不起作用,因为$ sea / located / province是一个大字符串,每个省都与它接壤。

所以我试着修改成。

let $inland := //province/@id
where every $sea in //sea satisfies
not(contains($sea/located/@province, $inland))
return $inland

我希望它只能找到属于海洋边界省份的省份。简单明了。

错误讯息:

Stopped at C:/Users/saffekaffe/Desktop/mondial/xml/country_without_island.xml, 2/1:
[XPTY0004] Item expected, sequence found: (attribute id {"prov-Greece-2"},....

我该如何解决这个问题?

// sea / located / province @

的示例
province="prov-France-5 prov-France-20 prov-France-89 prov-France-99" 

//省/ @ id

的示例
id="prov-Greece-2"

1 个答案:

答案 0 :(得分:4)

XQuery有多种方式以不同于您期望的方式工作。

  1. 比较运算符=!=具有存在语义,如果它们的至少一个参数是序列而不是单项。这意味着$seq1 = $seq2相当于some $x in $seq1, $y in $seq2 satisfies $x = $y。查询('foo', 'bar') = ('bar', 'baz', 'quuz')会返回true,因为至少有一个公共项目。

  2. //province/@id这样的XQuery异常会评估所有匹配节点的序列。在您的情况下,这将是超过1000个省ID的序列:(id="prov-cid-cia-Greece-2", id="prov-cid-cia-Greece-3", id="prov-cid-cia-Greece-4", [...])。然后将此序列绑定到$inland子句中的变量let。由于您没有迭代$inland中的单个项目(例如使用for子句),因此where条件可以同时适用于全球所有省份的整个序列。所以你的条件every $sea in //sea satisfies $sea/located/@province != $inland现在意味着:
    “对于每个sea,其旁边都有一个province@id的{​​{1}}不等于至少一个现有的省ID 。”
    Th返回false,因为sea个没有located个孩子,例如亚丁湾

  3. contains($str, $sub)不适合检查子字符串是否包含在以空格分隔的字符串中,因为它还匹配部分条目:contains("foobar baz quux", "oob")返回true
    相反,您应该使用tokenize($str)将字符串拆分为其部分并查看其部分,或使用contains-token($str, $token)

  4. 总而言之,正确的查询非常类似于原始查询:

    for $inland in //province/@id
    where
      every $sea in //sea
      satisfies not(contains-token($sea/located/@province, $inland))
    return $inland
    

    另一种方法是首先收集sea旁边的所有(唯一)省份,然后返回不在该序列中的所有省份:

    let $next-to-sea := distinct-values(//sea/located/@province/tokenize(.))
    return //province/@id[not(. = $next-to-sea)]
    

    更紧凑(但效率可能更低):

    //province/@id[not(. = //sea/located/@province/tokenize(.))]
    

    另一方面,您可以使用XQuery 3.0 地图通过单一查找替换所有海边省份的潜在线性搜索:

    let $seaside :=
      map:merge(
        for $id in //sea/located/@province/tokenize(.)
        return map{ $id: () }
      )
    return //province/@id[not(map:contains($seaside, .))]