如何动态使用 xdmp:value?
How can I use xdmp:value dynamically?
我正在尝试将 xdmp:value 与 cts:contains 结合在一个函数中,其中传递给 xdmp:value 的值是动态的。我是函数式编程的新手,所以我的理解可能有很大的缺陷。
这是一个工作示例,它将动态参数放在 cts:contains 中,而不是应该阐明我的意图:
declare functional local:matchRegion($region as xs:string, $country as xs:string?)
as xs:boolean
{
let $in-country := '/country[name-variants/variant = $country]/regions/name-variants/variants'
let $anywhere := '/country/regions/region/name-variants/variant'
let $found := cts:contains(?, $region)
return
if ($country)
then ($found(xdmp:value($in-country)))
else ($found(xdmp:value($anywhere)))
};
local:matchRegion("ohio","usa") (: returns true :)
我正在尝试预先加载对 xdmp:value 的调用。
这是我尝试过的:
1)用箭头运算符
declare functional local:matchRegion($region as xs:string, $country as xs:string?)
as xs:boolean
{
let $in-country := '/country[name-variants/variant = $country]/regions/name-variants/variants'
let $anywhere := '/country/regions/region/name-variants/variant'
let $found := xdmp:value(?) => cts:contains($region)
return
if ($country)
then ($found($in-country))
else ($found($anywhere))
};
local:matchRegion("ohio","usa") (: XDMP-XMLFUNC... Functions cannot be used in the content of an element constructor :)
2) 使用简单的地图运算符:
declare functional local:matchRegion($region as xs:string, $country as xs:string?)
as xs:boolean
{
let $in-country := '/country[name-variants/variant = $country]/regions/name-variants/variants'
let $anywhere := '/country/regions/region/name-variants/variant'
let $found := xdmp:value(?) ! cts:contains(?,$region)
return
if ($country)
then ($found($in-country))
else ($found($anywhere))
};
local:matchRegion("ohio","usa") (: returns false :)
3) 使用匿名函数:
declare functional local:matchRegion($region as xs:string, $country as xs:string?)
as xs:boolean
{
let $in-country := '/country[name-variants/variant = $country]/regions/name-variants/variants'
let $anywhere := '/country/regions/region/name-variants/variant'
let $a := function($x,$y){cts:contains(xdmp:value($x),$y)}
let $found := $a(?,$region)
return
if ($country)
then ($found($in-country))
else ($found($anywhere))
};
local:matchRegion("ohio","usa") (: returns false :)
我要编写的函数与 matchRegion 的意图相似,它将涉及更多的条件和更多的 xpath 表达式;我正在尝试利用动态调用来保持语法的可读性和 xdmp:value 以避免对 xpath 进行不必要的评估。我怎样才能做到这一点?另外,为什么后两个实现 return false?最后,我认为 cts:contains 不会为我做任何特别的事情,因为我需要完全匹配。我对这一切都很陌生,所以如果有更好的功能可以调用,请告诉我。
谢谢
更新:
这是我要查询的 xml 文件的示例:
<country>
<name>Canada</name>
<name-variants>
<variant>canada</variant>
</name-variants>
<regions>
<region>
<name>Manitoba</name>
<name-variants>
<variant>manitoba</variant>
<variant>province of manitoba</variant>
</name-variants>
</region>
</regions>
</country>
你好,法拉巴拉:
请尝试以下 XQuery 函数。
declare function local:matchOrNot( $region as xs:string,
$country as xs:string)
as xs:boolean
{
some $i in doc()/country/descendant-or-self::*[contains(., $country)]
satisfies $i/regions/region/descendant-or-self::*[contains(., $region)]
};
local:matchOrNot("new york", "usa");
您可以测试几个文档,例如:
You should expect below results:
local:matchOrNot("new york", "usa"); => true
local:matchOrNot("new york", "USA"); => true
local:matchOrNot("New York", "USA"); => true
local:matchOrNot("New York", "usa"); => true
local:matchOrNot("New York", "Canada"); => false
Any combination of "New York" and "Canada" throws back "false"
你好,法拉巴拉:
- 是
declare function
不是declare functional
- 返回的序列是
false
即 false 结果
- 由于 xpath 逻辑问题,您的查询变为“
lazy
”,检查的条件较少。
Speaking of execution time: PT0.0000922S < PT0.0002321S
除了上面分享的代码中的各种大拼写错误外,我认为您在尝试过程中遇到了一些障碍。 Fiona 已经提到 'functional' 确实需要 function
,而且你的 xpaths 也有错误,比如缺少 /region
和 /variant
。
首先,使用 ?创建部分函数是非常罕见的。老实说,我以前没有看到它在 XQuery 中使用过,看起来它是 MarkLogic 特定的。 XQuery 3 匿名函数应该同样有效,并且可能使其他人更容易阅读。
第一次尝试是您尝试中最直接的方法。毫不奇怪,它有效,尽管它看起来不必要的复杂。您可以将 XPath 放在适当的位置,而无需 xdmp:value。只有当代码到达它们时,它们才会被评估。 if 可以确保只评估正确的 XPath。
箭头运算符的第二次尝试尝试将部分函数作为第一个参数提供给 cts:contains,但是 cts:contains 没有等待执行,因此它会尝试立即评估第一个参数,然后退出,因为它无法将部分函数转换为文本或元素节点以进行比较。
第三次尝试有两个部分函数,但它不是将第一个输入到第二个中。相反!运算符只是用后者替换第一个。您最终将 XPath 作为字符串传递到 cts:contains,而作为字符串的 XPath 将与区域不匹配。 cts:contains 默默地将原子值转换为文本节点。
第四次尝试使用匿名函数仍然使用 ?上面的参数,看起来有点傻,因为你也可以直接使用匿名函数。它失败的原因是相当微妙的。看起来 $country 的值没有携带,或者至少没有正确携带。将 $country
替换为其在 XPath 中的文字值,您可以在现场执行此操作,因为无论如何它是一个字符串表达式,将为您提供 true
.
就个人而言,我会避免所有这些,而是使用硬编码的 XPath。或者,如果您需要能够将其扩展到多个区域:请改用 cts:search 和 cts:queries。这里有一个要点,其中列出了您的尝试,以及我的一些尝试:
https://gist.github.com/grtjn/d7bd4b65e9b616bf62b0a5196bc006a2
HTH!
我正在尝试将 xdmp:value 与 cts:contains 结合在一个函数中,其中传递给 xdmp:value 的值是动态的。我是函数式编程的新手,所以我的理解可能有很大的缺陷。
这是一个工作示例,它将动态参数放在 cts:contains 中,而不是应该阐明我的意图:
declare functional local:matchRegion($region as xs:string, $country as xs:string?)
as xs:boolean
{
let $in-country := '/country[name-variants/variant = $country]/regions/name-variants/variants'
let $anywhere := '/country/regions/region/name-variants/variant'
let $found := cts:contains(?, $region)
return
if ($country)
then ($found(xdmp:value($in-country)))
else ($found(xdmp:value($anywhere)))
};
local:matchRegion("ohio","usa") (: returns true :)
我正在尝试预先加载对 xdmp:value 的调用。
这是我尝试过的: 1)用箭头运算符
declare functional local:matchRegion($region as xs:string, $country as xs:string?)
as xs:boolean
{
let $in-country := '/country[name-variants/variant = $country]/regions/name-variants/variants'
let $anywhere := '/country/regions/region/name-variants/variant'
let $found := xdmp:value(?) => cts:contains($region)
return
if ($country)
then ($found($in-country))
else ($found($anywhere))
};
local:matchRegion("ohio","usa") (: XDMP-XMLFUNC... Functions cannot be used in the content of an element constructor :)
2) 使用简单的地图运算符:
declare functional local:matchRegion($region as xs:string, $country as xs:string?)
as xs:boolean
{
let $in-country := '/country[name-variants/variant = $country]/regions/name-variants/variants'
let $anywhere := '/country/regions/region/name-variants/variant'
let $found := xdmp:value(?) ! cts:contains(?,$region)
return
if ($country)
then ($found($in-country))
else ($found($anywhere))
};
local:matchRegion("ohio","usa") (: returns false :)
3) 使用匿名函数:
declare functional local:matchRegion($region as xs:string, $country as xs:string?)
as xs:boolean
{
let $in-country := '/country[name-variants/variant = $country]/regions/name-variants/variants'
let $anywhere := '/country/regions/region/name-variants/variant'
let $a := function($x,$y){cts:contains(xdmp:value($x),$y)}
let $found := $a(?,$region)
return
if ($country)
then ($found($in-country))
else ($found($anywhere))
};
local:matchRegion("ohio","usa") (: returns false :)
我要编写的函数与 matchRegion 的意图相似,它将涉及更多的条件和更多的 xpath 表达式;我正在尝试利用动态调用来保持语法的可读性和 xdmp:value 以避免对 xpath 进行不必要的评估。我怎样才能做到这一点?另外,为什么后两个实现 return false?最后,我认为 cts:contains 不会为我做任何特别的事情,因为我需要完全匹配。我对这一切都很陌生,所以如果有更好的功能可以调用,请告诉我。
谢谢
更新: 这是我要查询的 xml 文件的示例:
<country>
<name>Canada</name>
<name-variants>
<variant>canada</variant>
</name-variants>
<regions>
<region>
<name>Manitoba</name>
<name-variants>
<variant>manitoba</variant>
<variant>province of manitoba</variant>
</name-variants>
</region>
</regions>
</country>
你好,法拉巴拉:
请尝试以下 XQuery 函数。
declare function local:matchOrNot( $region as xs:string,
$country as xs:string)
as xs:boolean
{
some $i in doc()/country/descendant-or-self::*[contains(., $country)]
satisfies $i/regions/region/descendant-or-self::*[contains(., $region)]
};
local:matchOrNot("new york", "usa");
您可以测试几个文档,例如:
You should expect below results:
local:matchOrNot("new york", "usa"); => true
local:matchOrNot("new york", "USA"); => true
local:matchOrNot("New York", "USA"); => true
local:matchOrNot("New York", "usa"); => true
local:matchOrNot("New York", "Canada"); => false
Any combination of "New York" and "Canada" throws back "false"
你好,法拉巴拉:
- 是
declare function
不是declare functional
- 返回的序列是
false
即 false 结果 - 由于 xpath 逻辑问题,您的查询变为“
lazy
”,检查的条件较少。
Speaking of execution time: PT0.0000922S < PT0.0002321S
除了上面分享的代码中的各种大拼写错误外,我认为您在尝试过程中遇到了一些障碍。 Fiona 已经提到 'functional' 确实需要 function
,而且你的 xpaths 也有错误,比如缺少 /region
和 /variant
。
首先,使用 ?创建部分函数是非常罕见的。老实说,我以前没有看到它在 XQuery 中使用过,看起来它是 MarkLogic 特定的。 XQuery 3 匿名函数应该同样有效,并且可能使其他人更容易阅读。
第一次尝试是您尝试中最直接的方法。毫不奇怪,它有效,尽管它看起来不必要的复杂。您可以将 XPath 放在适当的位置,而无需 xdmp:value。只有当代码到达它们时,它们才会被评估。 if 可以确保只评估正确的 XPath。
箭头运算符的第二次尝试尝试将部分函数作为第一个参数提供给 cts:contains,但是 cts:contains 没有等待执行,因此它会尝试立即评估第一个参数,然后退出,因为它无法将部分函数转换为文本或元素节点以进行比较。
第三次尝试有两个部分函数,但它不是将第一个输入到第二个中。相反!运算符只是用后者替换第一个。您最终将 XPath 作为字符串传递到 cts:contains,而作为字符串的 XPath 将与区域不匹配。 cts:contains 默默地将原子值转换为文本节点。
第四次尝试使用匿名函数仍然使用 ?上面的参数,看起来有点傻,因为你也可以直接使用匿名函数。它失败的原因是相当微妙的。看起来 $country 的值没有携带,或者至少没有正确携带。将 $country
替换为其在 XPath 中的文字值,您可以在现场执行此操作,因为无论如何它是一个字符串表达式,将为您提供 true
.
就个人而言,我会避免所有这些,而是使用硬编码的 XPath。或者,如果您需要能够将其扩展到多个区域:请改用 cts:search 和 cts:queries。这里有一个要点,其中列出了您的尝试,以及我的一些尝试:
https://gist.github.com/grtjn/d7bd4b65e9b616bf62b0a5196bc006a2
HTH!