2011-11-30 74 views
6

我有一個包含作者和編輯者的XML文件。XQuery/XPath:使用count()和max()函數返回具有最高計數的元素

<?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>[email protected]</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可以以更簡單的方式完成此操作嗎?

最好的問候,

回答

15

這可有助於:

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] 
+0

感謝您的解決方案,克里斯汀:) 有沒有辦法返回多個作者/編輯器(如果適用)?例如,如果有兩位作者/編輯與作者/編輯共享相同(最多)的作品數量? – Jea

+3

@Jea:無論是在Christian還是我的解決方案中,只需刪除結尾的[[1]],就會得到所有具有最大值的節點。 –

2

你是在正確的軌道上。最簡單的方法是將名稱轉換成字符串(用空格分隔,例如)轉換並使用這些:(請注意下面的代碼是未經測試)

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()] 
+0

@_Oliver:對不起,但即使在XQuery 3.0/XPath 3.0中,這也是錯誤的。提示:查看:'$ names/count(index-of($ names,。)'''names'恰好是一系列原子值,但'/'操作符需要一個節點(-set)作爲其左邊的操作數 –

+0

@_Oliver:你的第一種方法也不會產生任何結果,使用oXygen下的Saxon 9.3.05進行檢查 –

+0

@Dimitre:好的重新定義'/'我已經移除了XPath的例子,這是一個糟糕的解決方案無論如何。 –

7

這裏是一個純的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>[email protected]</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中,您可以自己編寫這些函數(實際上,它們是在規範中用作用戶編寫的示例函數)。

+0

這些例子的鏈接會很好! – grtjn

相關問題