Experimenting with AppBuilder’s rewriter

Author: Dave Cassel  |  Category: Software Development

In a recent post, I wrote a tutorial showing how to add a view to an Application Builder-based application. As one step, I made a copy of the rewrite.xqy module under the custom directory so that I had an easy way to add my new view. There was one thing about the approach that I didn’t like: I don’t like copying & pasting code. If I find myself doing so, then I look for a way to abstract what’s being copied. I recently put together a different way of doing the rewrite code, and I’d like to share my experimental results with you.

Take a look at this expression from the original rewrite.xqy:

let $new-url :=
    if (fn:matches($url,"^/search"))
        then local:construct-new($url,"/search","search")
    else
        if (fn:matches($url,"^/detail"))
        then local:construct-new($url,"/detail","detail")
    else
        if (fn:matches($url,"^/help"))
        then local:construct-new($url,"/help","help")
    else
        if (fn:matches($url,"^/contact"))
        then local:construct-new($url,"/contact","contact")
    else
        if (fn:matches($url,"^/terms"))
        then local:construct-new($url,"/terms","terms")
    else
        if (fn:matches($url,"^/$"))
        then local:construct-new($url,"/","intro")
    else $url

This code handles the six views in a default Application Builder application. On the plus side, this approach allows for a variety of ways of looking for the current view, and can handle those views in different ways. Adding a new view is a simple matter of copying, say, the block for the “terms” view and changing it for your new view.

What I don’t like about this approach is that you can get a pretty long sequence of if-then-elses pretty quickly. For my pin collecting site, I’ve added five more views: add, edit, login, logout, register. If I’d stuck with the built-in approach, that expression would be pretty long. However, it occurred to me that I wanted to look for all those views the same way, and I wanted to do the same thing with them. Given that, I should be able to abstract out some code. Here’s what I came up with.

let $url := xdmp:get-request-url()
let $views := ("search", "detail", "help", "contact", "terms",
    "add", "edit", "login", "logout", "register")
let $matched :=
    fn:analyze-string($url,
        fn:concat("^/(", fn:string-join($views, "|"), ")"))/s:match/s:group
return
    if ($matched) then
         local:construct-new($url, fn:concat("/", $matched), $matched)
    else if (fn:matches($url,"^/$")) then
        local:construct-new($url,"/","intro")
    else $url

I set up a sequence with the names of the views, then build a regular expression that looks for any one of them.

Okay, I confess — I did simplify the above code a little. My code has some additional stuff to handle application-level security. But what I’m showing is enough to make it really easy to add new views, and I think it’s less error prone than copying & pasting code.

My one concern was performance — how would a regular expression for, say, 10 views compare with an if-then-else expression for 10 views. It turned out to be a reasonable concern.

I set up a test to compare the two approaches. I ran 10,000 iterations hitting each of 10 views, for 100,000 hits, using the Profile button in CQ to time them. The differences were pretty significant: about 10.1 seconds for the analyze-string() approach compared to about 2.3 seconds for the if-then-else approach. At an relative scale, who wouldn’t go with an approach that is a 4x improvement over another? On the other hand, it’s worth noting that the absolute timing is still pretty quick: 10.1 seconds for 100,000 hits is 0.000101 seconds per hit — and that’s for the slow approach. I think that’s fast enough that a developer can make the choice without worrying about it too much.

For now, I’m going to stick with the analyze-string() approach, but I’m glad that I took the trouble to run some performance tests. As I add more views, I’ll probably repeat the test to keep an eye on the impact.

Which code style do you like better? Is the performance penalty of the analyze-string approach enough to bother you? Have you thought of other approaches to working with views? Let me know in the comments.

Tags: , ,

Leave a Reply