#Steve Meunch's Grouping revelation, with some replication for performance testing. 9 May 2000. """ From: "Steve Muench" To: Subject: Re: grouping (was: if or template?) Date: Tue, 9 May 2000 01:10:53 -0700 (02:10 MDT) | > | > | >select="//tracker-id[generate-id(.)=generate-id(key('tid',.)[1])]"> | > | >I hope Steve will forgive me for announcing this discovery | >before he does, | >I'm quite excited by it because it gives much better performance. | | All it does to me is make me scratch my head! | Steve/Mike, would you give us the idiots view on this please, | whats happening? *Why* does it provide the unique tracker-id please? When you're doing grouping, you basically want to select exactly one of each unique thing. //tracker-id would select all tracker-id elements in the document. Declaring a 'tid' key like: The key('tid','tidvalue') function looks up all nodes having tracker-id = 'tidvalue'. In order to support this lookup, the processor will be keeping a list in memory like this: "tid" Key lookup Table ====================== tracker-id Ref to tracker-id elements value having that value ----------- -------------------------- abc123 node(109),node(344),node(496) def456 node(15) hij332 node(89),node(101) Where the notation node(nnn) means "the node whose node-id is nnn" as defined by generated-id(). To be concrete, the processor is likely keeping some kind of Hashtable with the tracker-id *value* as the hash key, and a node-list as the hash value. //tracker-id[generate-id(.)=generate-id(key('tid',.)[1])] selects all tracker-id elements in the document having a node-id equal to the node-id of the first node in the "key lookup table's" list of nodes having the current tracker-id. Said more simply, it selects the first tracker-id element for each unique tracker-id value. Or even more simply, it selects a list of distinct tracker-id values. Here's an example. Take the "Task.xml" File below... [snip xml_source_1] The stylesheet: [snip sheet_str_1] Produces a sorted, grouped list of tasks by owner and is much faster than the equivalent "scan-my-preceding" approach... [snip expected_1] For testing, here is a slower.xsl stylesheet that does the same job without using the key() technique: [snip sheet_str_2] as you scale up the size of the Task.xml input file, the performance difference can be dramatic. Try copy/pasting the elements in the Task.xml above to creates a couple thousand elements to give it a spin... [snip] """ #" from Xml.Xslt import test_harness sheet_1 = """ """ sheet_2 = """ """ source_1 = """ Task1Steve Task2Mike Task3Dave Task4Steve Task5Mike Task6Mike Task7Uche Task8Jeremy Task9Phil Task10Chime Task11Glenn Task12Uche Task13Steve Task14Phil Task15Dave Task16Jeremy Task17Steve Task18Glenn Task19Uche Task20Uche """ expected_1 = """ Task10 Chime Task3 Dave Task15 Dave Task11 Glenn Task18 Glenn Task8 Jeremy Task16 Jeremy Task2 Mike Task5 Mike Task6 Mike Task9 Phil Task14 Phil Task1 Steve Task4 Steve Task13 Steve Task17 Steve Task7 Uche Task12 Uche Task19 Uche Task20 Uche """ def Test(tester): source = test_harness.FileInfo(string=source_1) sheet = test_harness.FileInfo(string=sheet_1) test_harness.XsltTest(tester, source, [sheet], expected_1, title='test 1') source = test_harness.FileInfo(string=source_1) sheet = test_harness.FileInfo(string=sheet_2) test_harness.XsltTest(tester, source, [sheet], expected_1, title='test 2') return