2011-11-30 16 views
6

Ho un file XML che contiene autori ed editor.XQuery/XPath: utilizzo della funzione count() e max() per la restituzione dell'elemento con il conteggio più elevato

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

Sto cercando un modo per restituire la persona che è stata elencata più volte come autore e/o editore. La soluzione dovrebbe essere compatibile con XQuery 1.0 (XPath 2.0).

Stavo pensando di utilizzare una query FLWOR per scorrere tutti gli autori e gli editor, quindi fare un conteggio di autori/editor unici, quindi restituire gli autori/gli editor che corrispondono al conteggio più alto. Ma non sono stato in grado di trovare la soluzione adeguata.

Qualcuno ha qualche suggerimento su come una tale query FLWOR dovrebbe essere scritta? Questo potrebbe essere fatto in un modo più semplice, usando XPath?

Con i migliori saluti,

Jeanette

risposta

15

Questo può aiutare:

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

Grazie per la soluzione, Christian :) C'è un modo per restituire più di un autore/editor (se applicabile)? Ad esempio se ci sono due autori/editori che condividono lo stesso (massimo) conteggio come autore/editore? – Jea

+3

@Jea: Sia in Christian che nella mia soluzione rimuovi solo la fine '[1]' e otterrai tutti i nodi che hanno il valore massimo. –

2

Sei sulla strada giusta. Il modo più semplice è quello di convertire i nomi in stringhe (separati da uno spazio, ad esempio) e utilizzare questi: (Si noti che il seguente codice è testato)

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 

Oppure, un altro approccio:

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

@_Oliver: Ci dispiace, ma anche in XQuery 3.0/XPath 3.0 questo è un errore. Suggerimento: guarda: '$ names/count (index-of ($ names,.)'. '$ Names' sembra essere una sequenza di valori atomici, ma l'operatore'/'richiede un nodo (-set) come operando a sinistra –

+0

@_Oliver: il tuo primo approccio non produce alcun risultato. Controllato con Saxon 9.3.05 sotto oXygen –

+0

@Dimitre: Good point re '/'. Ho rimosso l'esempio di XPath. Era una soluzione orribile comunque –

7

Qui è una pura espressione XPath 2.0, certamente non per i deboli di cuore:

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

dove il prefisso "b:" è associato allo spazio nomi "books".

XSLT 2.0 - basata verifica:

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

quando questa trasformazione è applicato sul documento XML fornito:

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

è selezionata la desiderata, corretta name elemento e uscita:

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

Ho sempre sentito che questa era un'omissione in XPath: le funzioni max() e min() restituiscono il valore più alto/più basso, mentre quello che di solito si desidera è l'oggetto o gli oggetti in una raccolta che hanno il valore più alto/più basso per alcune espressioni. Una soluzione è ordinare gli oggetti su quel valore e prendere il primo/ultimo dalla lista, che sembra inelegante. Calcolare il valore min/max e quindi selezionare gli articoli il cui valore corrisponde a questo sembra altrettanto sgradevole. In Saxon ci sono da tempo una coppia di funzioni di estensione di ordine superiore saxon: highest() e saxon: lowest() che prendono una sequenza e una funzione, e restituiscono gli item dalla sequenza con i valori più bassi o più alti di il risultato della funzione. La buona notizia è che in XPath 3.0 è possibile scrivere queste funzioni da soli (in effetti, vengono fornite come funzioni scritte dall'utente nelle specifiche).

+0

Un link a quegli esempi sarebbe bello! – grtjn