【问题标题】:XQuery/XPath: Using count() and max() function for return of element with highest countXQuery/XPath:使用 count() 和 max() 函数返回计数最高的元素
【发布时间】:2012-01-10 05:06:40
【问题描述】:

我有一个包含作者和编辑的 XML 文件。

<?xml version="1.0" encoding="UTF-8"?>
<?oxygen RNGSchema="file:textbook.rnc" type="compact"?>
<books xmlns="books">

    <book ISBN="i0321165810" publishername="OReilly">
        <title>XPath</title>
        <author>
            <name>
                <fname>Priscilla</fname>
                <lname>Walmsley</lname>
            </name>
        </author>
        <year>2007</year>
        <field>Databases</field>
    </book>

    <book ISBN="i0321165812" publishername="OReilly">
        <title>XQuery</title>
        <author>
           <name>
               <fname>Priscilla</fname>
               <lname>Walmsley</lname>
            </name>
        </author>
        <editor>
            <name>
                <fname>Lisa</fname>
                <lname>Williams</lname>
            </name>
        </editor>
        <year>2003</year>
        <field>Databases</field>
    </book>

    <publisher publishername="OReilly">
        <web-site>www.oreilly.com</web-site>
        <address>
            <street_address>hill park</street_address>
            <zip>90210</zip>
            <state>california</state>
        </address>
        <phone>400400400</phone>
        <e-mail>oreilly@oreilly.com</e-mail>
        <contact>
            <field>Databases</field>
            <name>
                <fname>Anna</fname>
                <lname>Smith</lname>
            </name>
        </contact>
    </publisher>
</books>

我正在寻找一种方法来返回被列为作者和/或编辑次数最多的人。该解决方案应该与 XQuery 1.0 (XPath 2.0) 兼容。

我正在考虑使用 FLWOR 查询来遍历所有作者和编辑,然后计算唯一作者/编辑,然后返回与最高计数匹配的作者/编辑。但我一直没能找到合适的解决方案。

有人对如何编写这样的 FLWOR 查询有任何建议吗? 这可以使用 XPath 以更简单的方式完成吗?

【问题讨论】:

    标签: xpath xquery xpath-2.0


    【解决方案1】:

    你在正确的轨道上。最简单的方法是将名称转换为字符串(例如用空格分隔)并使用它们:(注意以下代码未经测试)

    let $names := (//editor | //author)/concat(fname, ' ', lname)
    let $distinct-names := distinct-values($names)
    let $name-count := for $name in $distinct-names return count($names[. = $name])
    for $name at $pos in $distinct-names
    where $name-count[$pos] = max($name-count)
    return $name
    

    或者,另一种方法:

    (
      let $people := (//editor | //author)
      for $person in $people
      order by count($people[fname = $person/fname and
                             lname = $person/lname])
      return $person
    )[last()]
    

    【讨论】:

    • @_Oliver:对不起,但即使在 XQuery 3.0 / XPath 3.0 中,这也是错误的。提示:看:$names/count(index-of($names,.)$names 恰好是一个原子值序列,但 / 运算符需要一个节点(-set)作为其左操作数。
    • @_Oliver:您的第一种方法也不会产生任何结果。在 oXygen 下使用 Saxon 9.3.05 检查。
    • @Dimitre:好点重新'/'。我已经删除了 XPath 示例。无论如何,这是一个可怕的解决方案。
    【解决方案2】:

    这可能会有所帮助:

    declare default element namespace 'books';
    (for $name in distinct-values($doc/books/*/*/name)
     let $entries := $doc/books/*[data(*/name) = $name]
     order by count($entries) descending
     return $entries/*/name)[1]
    

    【讨论】:

    • 感谢您的解决方案,Christian :) 有没有办法返回多个作者/编辑(如果适用)?例如,如果有两个作者/编辑共享相同(最大)计数作为作者/编辑?
    • @Jea:在 Christian's 和我的解决方案中,只需删除结尾 [1],您将获得所有具有最大值的节点。
    【解决方案3】:

    这是一个纯粹的 XPath 2.0 表达式,诚然不适合胆小的人

    (for $m in max(for $n in distinct-values(/*/b:book/(b:author | b:editor)
                                            /b:name/concat(b:fname, '|', b:lname)),
                   $cnt in count(/*/b:book/(b:author | b:editor)
                                 /b:name[$n eq concat(b:fname, '|', b:lname) ])
                   return $cnt
                   ),
         $name in /*/b:book/(b:author | b:editor)/b:name,
         $fullName in $name/concat(b:fname, '|',  b:lname),
         $count in count( /*/b:book/(b:author | b:editor)
                       /b:name[$fullName eq concat(b:fname, '|',  b:lname)])
      return
         if($count eq $m)
           then $name
           else ()
       )[1]
    

    前缀"b:"与命名空间"books"相关联。

    基于 XSLT 2.0 的验证

    <xsl:stylesheet version="2.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     xmlns:b="books">
     <xsl:output omit-xml-declaration="yes" indent="yes"/>
     <xsl:strip-space elements="*"/>
    
     <xsl:template match="/">
       <xsl:sequence select=
       "(for $m in max(for $n in distinct-values(/*/b:book/(b:author | b:editor)
                                                /b:name/concat(b:fname, '|', b:lname)),
                       $cnt in count(/*/b:book/(b:author | b:editor)
                                     /b:name[$n eq concat(b:fname, '|', b:lname) ])
                       return $cnt
                       ),
             $name in /*/b:book/(b:author | b:editor)/b:name,
             $fullName in $name/concat(b:fname, '|',  b:lname),
             $count in count( /*/b:book/(b:author | b:editor)
                           /b:name[$fullName eq concat(b:fname, '|',  b:lname)])
          return
             if($count eq $m)
               then $name
               else ()
           )[1]
       "/>
     </xsl:template>
    </xsl:stylesheet>
    

    当此转换应用于提供的 XML 文档时

    <books xmlns="books">
        <book ISBN="i0321165810" publishername="OReilly">
            <title>XPath</title>
            <author>
                <name>
                    <fname>Priscilla</fname>
                    <lname>Walmsley</lname>
                </name>
            </author>
            <year>2007</year>
            <field>Databases</field>
        </book>
        <book ISBN="i0321165812" publishername="OReilly">
            <title>XQuery</title>
            <author>
                <name>
                    <fname>Priscilla</fname>
                    <lname>Walmsley</lname>
                </name>
            </author>
            <editor>
                <name>
                    <fname>Lisa</fname>
                    <lname>Williams</lname>
                </name>
            </editor>
            <year>2003</year>
            <field>Databases</field>
        </book>
        <publisher publishername="OReilly">
            <web-site>www.oreilly.com</web-site>
            <address>
                <street_address>hill park</street_address>
                <zip>90210</zip>
                <state>california</state>
            </address>
            <phone>400400400</phone>
            <e-mail>oreilly@oreilly.com</e-mail>
            <contact>
                <field>Databases</field>
                <name>
                    <fname>Anna</fname>
                    <lname>Smith</lname>
                </name>
            </contact>
        </publisher>
    </books>
    

    选择并输出所需的正确 name 元素

    <name xmlns="books">
       <fname>Priscilla</fname>
       <lname>Walmsley</lname>
    </name>
    

    【讨论】:

      【解决方案4】:

      我一直觉得这是 XPath 中的一个遗漏:max() 和 min() 函数返回最高/最低值,而您通常想要的是集合中具有最高/最低值的对象。某些表达式的最低值。一种解决方案是根据该值对对象进行排序并从列表中获取第一个/最后一个,这似乎不雅。计算最小值/最大值然后选择其值匹配的项目似乎同样没有吸引力。在撒克逊语中,长期以来有一对高阶扩展函数 saxon:highest() 和 saxon:lowest() 接受一个序列和一个函数,并从序列中返回具有最低或最高值的项目函数结果。好消息是,在 XPath 3.0 中,您可以自己编写这些函数(事实上,它们在规范中作为示例用户编写的函数给出)。

      【讨论】:

      • 这些例子的链接会很好!
      猜你喜欢
      • 2020-08-31
      • 2016-12-01
      • 2022-01-11
      • 1970-01-01
      • 2018-07-18
      • 1970-01-01
      • 2021-08-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多