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

Xml XQuery中基于变量的动态排序(order by),xml,sorting,xquery,marklogic,exist-db,Xml,Sorting,Xquery,Marklogic,Exist Db,我试图在XQuery中实现动态排序。我目前正在使用Saxon PE 9.5进行开发,但将在eXist和marklogic中使用XQuery(或XQuery复数),因此任何使用其模块/函数的答案都可以(希望另一个db将具有相应的模块/函数) 排序基于包含字符串序列的变量。序列中的每个字符串都是元素的名称和可选的“降序” 我尝试了多种方法,但都没有达到预期的效果;尤其是第二类 在下面的示例中,排序是静态的,主要排序为c(升序),次要排序为b(降序) so\uxquery\uquestion.xml

我试图在XQuery中实现动态排序。我目前正在使用Saxon PE 9.5进行开发,但将在eXist和marklogic中使用XQuery(或XQuery复数),因此任何使用其模块/函数的答案都可以(希望另一个db将具有相应的模块/函数)

排序基于包含字符串序列的变量。序列中的每个字符串都是元素的名称和可选的“降序”

我尝试了多种方法,但都没有达到预期的效果;尤其是第二类

在下面的示例中,排序是静态的,主要排序为
c
(升序),次要排序为
b
(降序)

so\uxquery\uquestion.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>
输出

<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>
<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>
<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>
有没有什么方法可以进行动态排序,并将以下内容考虑在内

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

    • 这是XQuery 1.0中的一个漏洞,我认为3.0并没有解决它

      对于非评估方法,您是否尝试过类似的方法

      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
      

      不过,我可能会将关键点和方向分为两个不同的变量。

      在尝试实施@mblakele的建议时,我确实做到了这一点

      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>
      
      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>
      
      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>
      
      let$orderby:=('c','b递减'))
      让$xml:=doc('file:///C:/SO/so_xquery_question.xml')
      返回
      {
      对于$xml/doc/foo中的$foo
      订购人
      如果($orderby[1]='b'),则$foo/b else(),
      如果($orderby[1]='b descending'),则$foo/b else()降序,
      如果($orderby[1]='c'),则$foo/c else(),
      如果($orderby[1]='c descending'),则$foo/c else()降序,
      如果($orderby[2]='b'),则$foo/b else(),
      如果($orderby[2]='b descending'),则$foo/b else()降序,
      如果($orderby[2]='c'),则$foo/c else(),
      如果($orderby[2]='c descending'),则$foo/c else()降序
      返回
      $foo
      }
      
      输出

      <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>
      
      <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>
      
      <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>
      
      
      a2
      b2
      c0
      a1
      b1
      c0
      a3
      b3
      c3
      
      我要做的是检查序列中的第一项是否有可能的值,然后检查序列中的第二项。这将确保序列的顺序得以保持

      优点:

      • 它起作用了
      缺点:

      • 它非常冗长,对于我的8个可能的元素名来说会很难看 (128个不同的
        if
        语句!!)
      • 它在现实中仍然不起作用
      在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)
      
      xquery版本“3.0”;
      让$xml:=
      a1
      b1
      c0
      a2
      b2
      c0
      a3
      b3
      c3
      让$order by:=('c','b递减')
      让$sort:=
      if($order by[1]等式'c'和$order by[2]等式'b')
      然后“对于$xml/foo中的$foo,按$foo/c、$foo/b降序返回$foo”
      else()
      返回
      util:eval($sort)
      
      这是冗长的——当然需要填写逻辑(并且可以连接$sort)


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

      我曾经考虑过,但没有尝试过。我的实际数据有8个可能的元素,这些元素可能在
      $orderby
      变量中指定,并且
      if
      语句的数量将非常多。我会试试看会发生什么。希望eXist能够像marklogic一样处理它+一个很好的建议,我认为我不能让它工作;特别是如果我有两个以上的元素可以排序。即使只是在2上排序,我也不确定如何修改您的建议,使其也能处理值为
      ('c descending','b')
      的变量。主要排序总是
      b
      c
      ,具体取决于
      if
      语句的顺序。我确实想出了一种方法,但仍然非常冗长。我将添加它作为一个答案,但希望有人有一个更好的解决方案(在eXist和marklogic中都有效)。我有一个类似的排序问题,没有eval,这是我能做的最好的了。不过,存在的问题可能是一个bug。谢谢您的建议。我不确定这对我的实际数据是否可行。对于我来说,有太多的可能性需要考虑,而且代码仍然是可维护的。如果我决定限制排序选项,我可能会走这条路。再次感谢!