2011-11-30 9 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

解決のおかげで、クリスチャン:) 複数の著者/編集者(該当する場合)を返す方法はありますか?例えば、著者/編集者と同じ(最大)数を共有する2人の著者/編集者がいるとしますか? – Jea

+3

@Jea:クリスチャンと私の解答の両方で、結末 '[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:良い点re// '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 - ベースの検証

<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要素が選択され、出力:

<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文書に適用され

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

私はこれがXPathで省略されているといつも感じていました。max()とmin()関数は最高値と最低値を返しますが、通常はコレクション内のオブジェクトいくつかの式の最高/最低値。 1つの解決策は、その値でオブジェクトをソートし、リストから最初/最後を取ることです。最小値/最大値を計算し、値がこれに一致する項目を選択することは、同様に魅力的ではないようです。サクソンでは、シーケンスと関数をとり、最も低い値または最も高い値を持つシーケンスから項目を返す、高次の拡張関数saxonのペアが古くからありました:highest()とsaxon:lowest()関数の結果良いことに、XPath 3.0では、これらの関数を自分で記述することができます(実際には、それらは仕様のサンプルのユーザ関数として与えられています)。

+0

これらの例へのリンクは素晴らしいでしょう! – grtjn

関連する問題