Wednesday, December 14, 2011

escaping xpath query

node name in xpath query cannot start with numbers:
https://issues.apache.org/jira/browse/JCR-579

So, use org.apache.jackrabbit.util.ISO9075 as shown in:
http://wiki.apache.org/jackrabbit/EncodingAndEscaping

Example xpath servlet (/apps/sandbox/xpath/xpath.jsp):
https://gist.github.com/1478007


<%--
runs xpath
--%><%@page
language="java" 
contentType="text/plain; charset=UTF-8"
pageEncoding="UTF-8"
import="javax.jcr.query.*,
java.io.*,
java.util.*,
java.util.regex.*,
org.apache.jackrabbit.util.*"
%><%@include file="/libs/foundation/global.jsp"%><%!
private static Pattern XPATH = Pattern.compile("^/jcr:root(/.+)//(.+)$");
public static String escapeXpath(String xpath) {
    final Matcher matcher = XPATH.matcher(xpath);
    if (matcher.find()) {
        final String path = ISO9075.encodePath(matcher.group(1));
        final String props = matcher.group(2);
        return String.format("/jcr:root%s//%s", path, props);
    } 
    return xpath;
}
%><%
final String xpathOriginal = slingRequest.getParameter("xpath");
final String xpath = escapeXpath(xpathOriginal);
final boolean isXpathEscaped = !xpath.equals(xpathOriginal);


final String displayParam = slingRequest.getParameter("display");
final boolean display = "false".equals(displayParam) ? false : true;//by default, iterates query result.

final QueryManager queryManager = resourceResolver.adaptTo(Session.class).getWorkspace().getQueryManager();
final Query query = queryManager.createQuery(xpath, Query.XPATH);

final String limitParam = slingRequest.getParameter("limit");
final long limit = (null != limitParam && !"".equals(limitParam)) ? Long.parseLong(limitParam, 10) : 100;
query.setLimit(limit);

final String offsetParam = slingRequest.getParameter("offset"); 
final long offset = (null != offsetParam && !"".equals(offsetParam)) ?  Long.parseLong(offsetParam, 10) : -1;
if (offset > 0) {
    query.setOffset(offset);
}

final long t = System.currentTimeMillis();
final QueryResult result = query.execute();
final NodeIterator iter = result.getNodes();
long count = iter.getSize();

out.append("original xpath:\n");
out.append(xpathOriginal + "\n");
out.append("escaped xpath:\n");
out.append(xpath + "\n");
if (isXpathEscaped) {
    out.append("(xpath is escaped)\n");
}

out.append("\nresult:\n\n");

if (display) {
    count = 0;
    while (iter.hasNext()) {
        final Node node = iter.nextNode();
        out.append(node.getPath() + "\n");
        count++;
    }
}

final long took = System.currentTimeMillis() - t;

%>
queried <%= count %> nodes (offset: <%= offset %>, limit: <%= limit %>) in <%= (took / 1000.0) %> secs.

Friday, December 2, 2011

log4j.xml for crx (logging QueryImpl)

I wanted to see impact of repository size on query performance.
You can configure crx logging to log query times.
http://wiki.apache.org/jackrabbit/Search#Analyzing_Query_Performance

cq/crx-quickstart/server/runtime/0/_crx/WEB-INF/log4j.xml
    <appender name="query" class="org.apache.log4j.RollingFileAppender">
        <param name="File" value="crx-quickstart/logs/crx/query.log"/>
        <param name="maxFileSize" value="10MB"/>
        <param name="maxBackupIndex" value="100"/>
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d{dd.MM.yyyy HH:mm:ss} *%-5p* %c{1}: %m (%F, line %L)%n"/>
        </layout>
    </appender>
    <logger name="org.apache.jackrabbit.core.query.QueryImpl" additivity="false">
        <level value="debug"/>
        <appender-ref ref="query" />
    </logger>

it'll be logged to cq/crx-quickstart/logs/crx/query.log
 additivity="false" will skip console logging (if root logger has console logger).

Example log:
02.12.2011 14:46:37 *DEBUG* QueryImpl: executed in 0.01 s. (/jcr:root/content/dam//*[(@jcr:primaryType='dam:AssetContent' and jcr:contains(metadata/@dc:format, 'image'))] order by @jcr:lastModified descending) (QueryImpl.java, line 143)