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

Xquery - How to match two sequences within a quantifier expression

像许多人一样,我正在处理 XML 上的 Mondial 数据库。如果 XQuery 语法没有尽最大努力进行破坏,那将是小菜一碟。

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

我在上面试图做的是找到所有 "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"

XQuery 有多种工作方式,与您的预期不同。

  1. 比较运算符 =!= 具有 存在语义 如果它们的至少一个参数是 sequence 而不是单个项目。这意味着 $seq1 = $seq2 等同于 some $x in $seq1, $y in $seq2 satisfies $x = $y。查询 ('foo', 'bar') = ('bar', 'baz', 'quuz') returns 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", [...])。然后将此序列绑定到 let 子句中的变量 $inland。由于您没有遍历 $inland 中的单个项目(例如使用 for 子句),因此 where 条件会同时作用于全球所有省份的整个序列。所以你的条件 every $sea in //sea satisfies $sea/located/@province != $inland 现在意味着:
    "For every sea there is a province located next to it that has an @id that is not equal to at least one of all existing province IDs."
    这是 returns false 因为有 seas 没有 located children, e.g.the 亚丁湾.

  3. contains($str, $sub) 不适合检查子字符串是否包含在 space-delimited 字符串中,因为它也匹配部分条目:contains("foobar baz quux", "oob") returns true.
    相反,您应该使用 tokenize($str) 将字符串分成多个部分并查看其各个部分,或者使用 contains-token($str, $token).

综合起来,与您的原始查询非常相似的正确查询是:

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

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

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 maps 通过一次查找来替换对所有沿海省份的潜在线性搜索:

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