如何动态使用 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"

你好,法拉巴拉:

  1. declare function不是declare functional
  2. 返回的序列是 falsefalse 结果
  3. 由于 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!