Toon posts:

XML/XSLT Grouping

Pagina: 1
Acties:

Verwijderd

Topicstarter
Ik ben dus bezig om een simpel website-statistieken dingetje te maken in xml
input gaat nu prima, hij maakt per bezoek netjes een nieuwe node aan etc.
maar nu wil ik wat output gaan genereren, dit is de xml

voorbeeld:
XML:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
- <statistieken version="1.0">
- <stat id="1">
      <pagina>Beginpagina</pagina> 
      <referer>www.google.com</referer> 
      <ip>10.0.0.151</ip> 
      <hn>Hostname</hn> 
      <os>Windows</os> 
      <bw>Microsoft Internet Explorer</bw> 
   - <datum>
        <dag>13</dag> 
        <maand>5</maand> 
        <jaar>2005</jaar> 
        <tijd>20:34</tijd> 
     </datum>
  </stat>
- <stat id="2">
      <pagina>Pagina2</pagina> 
      <referer>www.google.com</referer> 
      <ip>10.0.0.162</ip> 
      <hn>Hostname2</hn> 
      <os>Windows</os> 
      <bw>Microsoft Internet Explorer</bw> 
   - <datum>
        <dag>14</dag> 
        <maand>5</maand> 
        <jaar>2005</jaar> 
        <tijd>20:22</tijd> 
     </datum>
  </stat>
</statistieken>

enz. dus, staan er nog wat meer met de maanden 5,6 en 7 erin

Nu heb ik al de hits per maand, maar nu wil de unieke bezoekers (ip adressen) per maand hebben
heb al uitgevonden dat grouping niet echt heel eenvoudig is bij xml en hier op het forum heb ik een topic gevonden waar bij je de datum zou moeten scheiden. Nou dat zou in principe niet hoeven, want je kunt met de Muenchian(wat een naam) techniek het een en ander groeperen, op deze website staat een voorbeeld : http://www.jenitennison.com/xslt/grouping/muenchian.html

alleen ik kom er niet uit.....wat ik tot nu toe krijg is een overzicht per maand(nummer) van alle IP adressen in die maand

XSL :
XML:
1
2
3
4
5
6
7
8
9
10
11
12
    <xsl:key name="bezoeken-per-maand" match="stat" use="datum/maand"/>
    <xsl:template match="statistieken">
    <xsl:for-each select="stat[count(. | key('bezoeken-per-maand', datum/maand)[1]) = 1]">
        <xsl:sort select="datum/maand" />
        <xsl:value-of select="datum/maand" /><br />
        
        <xsl:for-each select="key('bezoeken-per-maand', datum/maand)">
            <xsl:value-of select="ip" /><br />
        </xsl:for-each>
        <br />
    </xsl:for-each>
    </xsl:template>



Output:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
5
10.0.0.151

6
10.0.0.151
10.0.0.151
10.0.0.151

7
10.0.0.151
10.0.0.151
10.0.0.151
10.0.0.151
10.0.0.151
10.0.0.151
10.0.0.151
10.0.0.151
10.0.0.151
10.0.0.151
10.0.0.151
10.0.0.151
10.0.0.162
10.0.0.162
10.0.0.162
10.0.0.162
10.0.0.162
10.0.0.162


alleen wat ik wil krijgen uiteindelijk is het "distinct" ip adres en daar een aantal(count) van....
dus :

Maand : 5
1

Maand : 6
1

Maand : 7
2

kan iemand me op weg helpen of heeft iemand een idee hoe zoiets aangepakt zou kunnen worden?
ik probeer intussen nog even verder te komen hiermee....

  • Glimi
  • Registratie: Augustus 2000
  • Niet online

Glimi

Designer Drugs

(overleden)
Je kunt de unieke IP's per keywaarde krijgen door soort constructie als dit
XML:
1
2
3
4
5
6
7
<xsl:key name="bezoeken-per-maand" match="stat" use="datum/maand"/>
 
<xsl:for-each select="key('bezoeken-per-maand') ">
  <xsl:if test="not( preceding-sibling::select[ ./ip = ip ] )" >
    <xsl:value-of select="count( parent::statistieken[ .ip = ip ] )" />
  </xsl:if>
</xsl:for-each>


Dit is een beetje uit de losse pols, of het allemaal werkt weet ik niet. In ieder geval is het gene waar je naar moet kijken een
code:
1
preceding-sibling::x[ ./y = y]

[ Voor 41% gewijzigd door Glimi op 14-07-2005 15:11 . Reden: Wat een mooie highlighting ;) ]


Verwijderd

Topicstarter
ok top, ik ga eens op onderzoek uit, ik kreeg in ieder geval een foutmelding

"Reference to undeclared namespace prefix: 'preceding-sibling'. "

Dus met die preceding-sibling gaat er nog iets niet goed, maar ik ga het even rustig bekijken
bedankt!

  • Glimi
  • Registratie: Augustus 2000
  • Niet online

Glimi

Designer Drugs

(overleden)
Verwijderd schreef op donderdag 14 juli 2005 @ 15:06:
ok top, ik ga eens op onderzoek uit, ik kreeg in ieder geval een foutmelding

"Reference to undeclared namespace prefix: 'preceding-sibling'. "

Dus met die preceding-sibling gaat er nog iets niet goed, maar ik ga het even rustig bekijken
bedankt!
Ja excuus, na een axis (zoals preceding-sibling) zet je twee maal een dubbele punt, in plaats van één. Eén betekent een namespace

Verwijderd

Topicstarter
hey daar hoef je je excuses niet voor aan te bieden! zal even lekker worden zeg :)
ben allang al blij dat je me helpt !

edit:
XML:
1
2
3
4
5
6
7
8
9
10
11
<xsl:key name="bezoeken-per-maand" match="stat" use="datum/maand" />

<xsl:template match="statistieken"> 
  
    <xsl:for-each select="key('bezoeken-per-maand', datum/maand) "> 
        <xsl:if test="not( preceding-sibling::select[ ./ip = ip ] )" > 
        <xsl:value-of select="count( parent::statistieken[ ./ip = ip ] )" /> 
    </xsl:if> 
    </xsl:for-each>

    </xsl:template>


ok ik heb nu dit, alleen hij laat nu geen output zien? :/
volgens mij zou die het zo dan moeten doen?

[ Voor 69% gewijzigd door Verwijderd op 14-07-2005 15:19 ]


  • Glimi
  • Registratie: Augustus 2000
  • Niet online

Glimi

Designer Drugs

(overleden)
Na een avond prutsen kan ik alleen maar tot de conclusie komen dat dit niet kan in XSLT 1.0 (2.0 is geen probleem, daar hebben we distinct-values() ).

Het punt is namelijk dat we willen filteren op de voorgaande nodes binnen een nodeset of filteren op meerdere eigenschappen voor één voorganger.
Xpath's zoals
code:
1
//ip[ . != preceding::ip and parent::stat/datum/maand != preceding::maand ]

Hebben niet het gewenste effect, omdat de preceding:: selectors niet op hetzelfde element hoeven te slaan. Dus als het IP al op een andere maand is langsgeweest, dan wordt het IP toch uitgesloten.

Heeft iemand een idee hoe dit in 1.0 wel aan te pakken?

[edit] Oja, het was wel mogelijk geweest als we dus van een ip willen weten of het al voorgekomen is in dezelfde maand, maar daar hebben we niets aan. We willen namelijk de nodeset van de de ip's die langsgeweest zijn die maand, dus we kunnen het niet per IP behandelen.

DSpyRe Ik zou als tijdelijke oplossing willen voorstellen om het als je eerste oplossing te doen, maar dan gefiltert (code post ik zo) in een XML, zodat je daarna nog een XSLT er over kan gooien die een count() doet per maand.

[ Voor 28% gewijzigd door Glimi op 16-07-2005 00:19 ]


  • Glimi
  • Registratie: Augustus 2000
  • Niet online

Glimi

Designer Drugs

(overleden)
Ok, hier de 'work-around' met een dubbele XSLT transformatie. Eventueel zou je dit ook kunnen met het creeëren van nodes in je tree, maar daar ben ik nu ff te gaar voor ;)

Men neme deze XML als basis
XML:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<?xml version="1.0" encoding="UTF-8"?>
<statistieken version="1.0">
    <stat id="1">
        <ip>10.0.0.151</ip>
        <datum>
            <maand>5</maand>
        </datum>
    </stat>
    <stat id="2">
        <ip>10.0.0.162</ip>
        <datum>
            <maand>5</maand>
        </datum>
    </stat>
    <stat id="3">
        <ip>10.0.0.151</ip>
        <datum>
            <maand>5</maand>
        </datum>
    </stat>
    <stat id="4">
        <ip>10.0.0.151</ip>
        <datum>
            <maand>6</maand>
        </datum>
    </stat>
    <stat id="5">
        <ip>10.0.0.151</ip>
        <datum>
            <maand>5</maand>
        </datum>
    </stat>
    <stat id="6">
        <ip>10.0.0.152</ip>
        <datum>
            <maand>6</maand>
        </datum>
    </stat>
        <stat id="7">
        <ip>10.0.0.153</ip>
        <datum>
            <maand>6</maand>
        </datum>
    </stat>
</statistieken>


Men gooit er deze XSLT overheen
XML:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:output indent="yes" method="xml" encoding="UTF-8" omit-xml-declaration="no"/>

    <!-- Unique months and IP's-->
    <xsl:variable name="uniqueMonths" select="//maand[not( . = preceding::maand)]" />
    <xsl:key name="ipsPerMonth" match="//ip" use="parent::stat/datum/maand" />
    
    <xsl:template match="/">
        <root>
            <xsl:apply-templates/>
        </root>
    </xsl:template>

    <xsl:template match="statistieken">
        <bla>
            <xsl:for-each select="$uniqueMonths">           
                <xsl:sort data-type="number" order="ascending" select="." />
                
                <xsl:variable name="currMonth" select="." />
                <month>
                        <xsl:value-of select="." />
                </month>
                <uniqueIPs>
                    <xsl:for-each select="key( 'ipsPerMonth', string( . ) )" >
                    
                        <xsl:variable name="currIP" select="." />
                        <xsl:variable name="previousIPs" select="preceding::ip[ . = $currIP and parent::stat/datum/maand = $currMonth ]" />
                        <!-- If empty -->
                        <xsl:if test="not( $previousIPs )">
                            <uniqueIP>
                                <xsl:value-of select="." />
                            </uniqueIP>
                        </xsl:if>
                    </xsl:for-each>
                </uniqueIPs>
            </xsl:for-each>
        </bla>
    </xsl:template>

</xsl:stylesheet>


Dan krijg je deze XML
XML:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0" encoding="UTF-8"?>
<root>
    <bla>
        <month>5</month>
        <uniqueIPs>
            <uniqueIP>10.0.0.151</uniqueIP>
            <uniqueIP>10.0.0.162</uniqueIP>
        </uniqueIPs>
        <month>6</month>
        <uniqueIPs>
            <uniqueIP>10.0.0.151</uniqueIP>
            <uniqueIP>10.0.0.152</uniqueIP>
            <uniqueIP>10.0.0.153</uniqueIP>
        </uniqueIPs>
    </bla>
</root>


Deze nabewerken met deze XSLT
XML:
1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:output indent="yes" method="html" encoding="UTF-8" omit-xml-declaration="no"/>
    
    <xsl:template match="month">
            <xsl:value-of select="." /><br />
    </xsl:template>
    
    <xsl:template match="uniqueIPs">
        <xsl:value-of select="count( uniqueIP )" /><br />
    </xsl:template>
</xsl:stylesheet>


Geeft ongeveer je gewenste resultaat ;) De opmaak in de laatste XSLT moet je zelf maar even doen ;)
Succes!

  • djc
  • Registratie: December 2001
  • Laatst online: 08-09-2025

djc

Glimi schreef op zaterdag 16 juli 2005 @ 00:33:
Dan krijg je deze XML
XML:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0" encoding="UTF-8"?>
<root>
    <bla>
        <month>5</month>
        <uniqueIPs>
            <uniqueIP>10.0.0.151</uniqueIP>
            <uniqueIP>10.0.0.162</uniqueIP>
        </uniqueIPs>
        <month>6</month>
        <uniqueIPs>
            <uniqueIP>10.0.0.151</uniqueIP>
            <uniqueIP>10.0.0.152</uniqueIP>
            <uniqueIP>10.0.0.153</uniqueIP>
        </uniqueIPs>
    </bla>
</root>
Is het niet veel logischer om een month en zijn uniqueIPs samen in een parent element te hangen? Nu heb je nogal een losse verbinding tussen twee opeenvolgende elementen, maar als je bijvoorbeeld zoiets doet:

code:
1
2
3
4
5
6
<root>
   <month id="6">
       <uniqueIP>10.0.0.151</uniqueIP>
       <uniqueIP>10.0.0.152</uniqueIP>
   </month>
</root>

Rustacean


  • Glimi
  • Registratie: Augustus 2000
  • Niet online

Glimi

Designer Drugs

(overleden)
Manuzhai schreef op zaterdag 16 juli 2005 @ 10:06:
Is het niet veel logischer om een month en zijn uniqueIPs samen in een parent element te hangen? Nu heb je nogal een losse verbinding tussen twee opeenvolgende elementen, maar als je bijvoorbeeld zoiets doet:

XML:
1
2
3
4
5
6
<root>
   <month id="6">
       <uniqueIP>10.0.0.151</uniqueIP>
       <uniqueIP>10.0.0.152</uniqueIP>
   </month>
</root>
Dat is logischer en beter en ook simpel te zetten in mijn XSLT. Echter het was vrij laat en of een tussenstap nou geweldig logisch is of niet vind ik niet zo heel erg boeiend.
Dat het vrij laat was, zie je ook wel aan de opmerking over creëren van nodes in de tree. Dat lijkt me nogal sterk bij een functionele programeertaal 8)7

[ Voor 6% gewijzigd door Glimi op 16-07-2005 10:32 ]

Pagina: 1