Experimenting with AppBuilder’s rewriter
Author: Dave Cassel | Category: Software DevelopmentIn 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: appbuilder, marklogic, xquery