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 有多种工作方式,与您的预期不同。
比较运算符 =
和 !=
具有 存在语义 如果它们的至少一个参数是 sequence 而不是单个项目。这意味着 $seq1 = $seq2
等同于 some $x in $seq1, $y in $seq2 satisfies $x = $y
。查询 ('foo', 'bar') = ('bar', 'baz', 'quuz')
returns true
因为至少有一个共同项.
像 //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
因为有 sea
s 没有 located
children, e.g.the 亚丁湾.
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, .))]
像许多人一样,我正在处理 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 有多种工作方式,与您的预期不同。
比较运算符
=
和!=
具有 存在语义 如果它们的至少一个参数是 sequence 而不是单个项目。这意味着$seq1 = $seq2
等同于some $x in $seq1, $y in $seq2 satisfies $x = $y
。查询('foo', 'bar') = ('bar', 'baz', 'quuz')
returnstrue
因为至少有一个共同项.像
//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 everysea
there is aprovince
located next to it that has an@id
that is not equal to at least one of all existing province IDs."
这是 returnsfalse
因为有sea
s 没有located
children, e.g.the 亚丁湾.contains($str, $sub)
不适合检查子字符串是否包含在 space-delimited 字符串中,因为它也匹配部分条目:contains("foobar baz quux", "oob")
returnstrue
.
相反,您应该使用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, .))]