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

时间:2014-05-20 20:23:45

标签: 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>

的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>

输出

<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>

输出正确排序;首先是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>

输出

<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>

如您所见,它首先在b(降序)上排序。这是因为这是iforder by语句的顺序;不是变量序列($orderby)的顺序。如果我交换了if s的顺序(首先测试c),那就很好了。

我也在eXist中使用它,但它不处理descending

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

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

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

3 个答案:

答案 0 :(得分:2)

这是XQuery 1.0中的一个漏洞,我认为3.0没有修复它。

对于非eval方法,您尝试过这样的事情吗?

if ($orderby='b') then $foo/b
else if ($orderby='c') then $foo/c else (),
if ($orderby='b descending') then $foo/b
else if ($orderby='c descending') then $foo/c else () descending

但是我可能会将键和方向分成两个不同的变量。

答案 1 :(得分:1)

在尝试实施@ mblakele的建议时,我确实让这个工作......

<强>的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[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>

<强>输出

<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>

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

<强>优点:

  • 有效。

缺点:

  • 它的超级详细,对我的8个可能的元素名称来说会很难看 (128个不同的if陈述!!)。
  • 它仍然无法在eXist中运行。

答案 2 :(得分:1)

在eXist-db中,可以使用util:eval()进行双重排序。我不明白为什么这是必要的,但它确实有效。

xquery version "3.0";
let $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>
let $order-by := ('c','b descending')
let $sort :=
    if ($order-by[1] eq 'c' and $order-by[2] eq 'b descending')
    then 'for $foo in $xml/foo order by $foo/c, $foo/b descending return $foo'
    else ()
return
    util:eval($sort)

它的冗长 - 当然还需要填写逻辑(你可以连接$ sort)。

我看到基于BaseX和Zorba中的变量的二级排序存在类似的问题。