I am currently working on a project that uses the Windows event log. I am using wevtutil to get the results from the event logs. I know that wevtutil supports xpath queries, but since I'm new to xpath I don't know that I can achieve what I'm trying to do.
In SQL, what I would be doing is something like this:
SELECT log.*, COUNT(1) numHits
FROM Application log
GROUP BY Source, Task, Level, Description
ORDER BY numHits DESC
LIMIT 10
Is it possible to do such a thing using xpath?
Edit: Here is a sample Event:
<Event xmlns='http://schemas.microsoft.com/win/2004/08/events/event'>
<System>
<Provider Name='MSSQL$SQLEXPRESS' />
<EventID Qualifiers='16384'>17403</EventID>
<Level>4</Level>
<Task>2</Task>
<Keywords>0x80000000000000</Keywords>
<TimeCreated SystemTime='2010-10-20T20:06:18.000Z' />
<EventRecordID>9448</EventRecordID>
<Channel>Application</Channel>
<Computer>SHAZTOP</Computer>
<Security />
</System>
<EventData>
<Data>73094</Data>
<Binary>
FB4300000A000000130000005300480041005A0054004F0050005C00530051004C004500580050005200450053005300000000000000</Binary>
</EventData>
</Event>
XPath 1.0 has four data types: string, number, boolean and node set.
The only XPath ordering criteria is document order (in the given axis direction). That is how you can limit any result node set as @Dimitre and @Welbog have sugested with fn:position().
But, there is no specification that an XPath engine must provide a node set result in any given order. So, you can't sort nor grouping in XPath 1.0. You can select the firsts of each group, but not efficiently. As example:
//Event[not(System/Level = preceding::Level) or
not(System/Task = preceding::Task)]
XPath 2.0 has the sequence data type. A sequence has the exclicit order of construction. So, you can group. As example:
for $event (//Event)[index-of(//Event/System/concat(Level,'++',Task),
System/concat(Level,'++',Task))[1]]
result //Event[System/Level = $event/System/Level]
[System/Task = $event/System/Task]
But, because XPath 2.0 has not built-in sorting nor recursion mechanism (you could provide an extension function...) you can't sort.
For that you need a language with built-in sorting or a way to express its algorithm. Both XSLT (1.0 or 2.0) and XQuery have these features.
In SQL, what I would be doing is something like this:
SELECT log.*, COUNT(1) numHits FROM Application log GROUP BY Source, Task, Level, Description ORDER BY numHits DESC LIMIT 10Is it possible to do such a thing using xpath?
In case no sorting is necessary, one can get the first $n nodes selected by any XPath expression by:
(ExpressionSelectingNodeSet)[not(position() > $n)]
where $n can be substituted by a specific number
If there is a requirement that the nodes be sorted on one or more sort-keys, then this is not possible pure XPath, but one can easily perform such tasks with XSLT, using the <xsl:sort> instruction and the XPath position() function:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/*">
<nums>
<xsl:for-each select="num">
<xsl:sort data-type="number" order="descending"/>
<xsl:if test="not(position() > 5)">
<xsl:copy-of select="."/>
</xsl:if>
</xsl:for-each>
</nums>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the following XML document:
<nums>
<num>01</num>
<num>02</num>
<num>03</num>
<num>04</num>
<num>05</num>
<num>06</num>
<num>07</num>
<num>08</num>
<num>09</num>
<num>010</num>
</nums>
the correct result, containing only the top 5 numbers is produced:
<nums>
<num>010</num>
<num>09</num>
<num>08</num>
<num>07</num>
<num>06</num>
</nums>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With