XQuery中基于变量的动态排序(order by)

Dan*_*ley 5 xml sorting xquery marklogic exist-db

我正在尝试在XQuery中实现动态排序.我目前正在使用Saxon-PE 9.5进行开发,但是将在eXist和marklogic中使用XQuery(或xqueries复数),因此使用其模块/函数的任何答案都可以(并且希望其他数据库具有相应的模块/函数) .

排序基于包含一系列字符串的变量.序列中的每个字符串都是元素的名称和可选的"降序".

我已经尝试了多种方法,但无法按照预期的方式工作; 特别是对于二级种类.

在下面的示例中,排序是静态的,并且具有主要类型c(升序)和次要类型b(降序)...

so_xquery_question.xml

<doc>
    <foo id="foo1">
        <a>a1</a>
        <b>b1</b>
        <c>c0</c>
    </foo>
    <foo id="foo2">
        <a>a2</a>
        <b>b2</b>
        <c>c0</c>
    </foo>
    <foo id="foo3">
        <a>a3</a>
        <b>b3</b>
        <c>c3</c>
    </foo>
</doc>
Run Code Online (Sandbox Code Playgroud)

XQuery的

let $xml := doc('file:///C:/SO/so_xquery_question.xml')

return
<test>{
for $foo in $xml/doc/foo
order by $foo/c, $foo/b descending
return
    $foo
}</test>
Run Code Online (Sandbox Code Playgroud)

产量

<test>
   <foo id="foo2">
        <a>a2</a>
        <b>b2</b>
        <c>c0</c>
    </foo>
   <foo id="foo1">
        <a>a1</a>
        <b>b1</b>
        <c>c0</c>
    </foo>
   <foo id="foo3">
        <a>a3</a>
        <b>b3</b>
        <c>c3</c>
    </foo>
</test>
Run Code Online (Sandbox Code Playgroud)

输出正确排序; 首先是c(升序)然后是b(降序).

我的最新尝试部分有效.(在Saxon和marklogic中.由于某些未知原因(!@#$),它在eXist中的工作方式不同.)

这里是:

XQuery的

let $orderby := ('c','b descending')
let $xml := doc('file:///C:/SO/so_xquery_question.xml')

return
<test>{
for $foo in $xml/doc/foo
order by
    if ($orderby='b') then $foo/b else (),
    if ($orderby='b descending') then $foo/b else () descending,
    if ($orderby='c') then $foo/c else (),
    if ($orderby='c descending') then $foo/c else () descending
    return
        $foo
}</test>
Run Code Online (Sandbox Code Playgroud)

产量

<test>
   <foo id="foo3">
        <a>a3</a>
        <b>b3</b>
        <c>c3</c>
    </foo>
   <foo id="foo2">
        <a>a2</a>
        <b>b2</b>
        <c>c0</c>
    </foo>
   <foo id="foo1">
        <a>a1</a>
        <b>b1</b>
        <c>c0</c>
    </foo>
</test>
Run Code Online (Sandbox Code Playgroud)

如您所见,它首先排序b(降序).这是因为那是if语句中的顺序order by; 不是变量序列($orderby)的顺序.如果我交换ifs 的顺序(c第一次测试),它排序很好.

我也有这个在eXist工作,但它没有处理descending:

order by util:eval(concat('$foo/',string-join(tokenize($orderby,'\s')[1],', $foo/')))
Run Code Online (Sandbox Code Playgroud)

我有什么方法可以进行动态排序,并考虑以下因素?

  • 可以传递元素名称作为变量进行排序.
  • 可以为变量中的元素名称指定可选的"降序".
  • 维护变量的顺序(主要与次要排序).

Dan*_*ley 1

在尝试实施@mblakele的建议时,我确实让它发挥作用......

X查询

let $orderby := ('c','b descending')
let $xml := doc('file:///C:/SO/so_xquery_question.xml')

return
<test>{
for $foo in $xml/doc/foo
order by
    if ($orderby[1]='b') then $foo/b else (),
    if ($orderby[1]='b descending') then $foo/b else () descending,
    if ($orderby[1]='c') then $foo/c else (),
    if ($orderby[1]='c descending') then $foo/c else () descending,
    if ($orderby[2]='b') then $foo/b else (),
    if ($orderby[2]='b descending') then $foo/b else () descending,
    if ($orderby[2]='c') then $foo/c else (),
    if ($orderby[2]='c descending') then $foo/c else () descending
    return
        $foo
}</test>
Run Code Online (Sandbox Code Playgroud)

输出

<test>
   <foo id="foo2">
        <a>a2</a>
        <b>b2</b>
        <c>c0</c>
    </foo>
   <foo id="foo1">
        <a>a1</a>
        <b>b1</b>
        <c>c0</c>
    </foo>
   <foo id="foo3">
        <a>a3</a>
        <b>b3</b>
        <c>c3</c>
    </foo>
</test>
Run Code Online (Sandbox Code Playgroud)

我正在做的是检查序列中的第一项是否有可能的值,然后检查序列中的第二项。这将确保维持序列的顺序。

优点:

  • 有用。

缺点:

  • 它超级冗长,对于我的 8 个可能的元素名称(128 个不同的if语句!!)来说会很难看。
  • 它在 eXist 中仍然不起作用。