[This is part 2 of a series on the creation of a Facebook application to let fans of New Haven pizza order a virtual pie. Part 1 is here.]
Now that I've created our XML data model for the Pizza places, I can move on to developing the XQuery code to power the application. Creating applications using XQuery is something I do a lot at MarkLogic and seems to be getting some mainstream attention lately. IBM has been developing resources such as this article on using XQuery to develop applications, this tutorial XQuery idioms and this guide to using XQuery to create dashboards. An open source group called 28msec recently published this paper which takes the even better approach of describing an end-to-end XQuery development paradigm.
MarkLogic's own Norm Walsh has also been leading the way with an all XQuery AtomPub server and a recent project that was all XML + XQuery. And the results of using XQuery from top to bottom can be seen in such great applications as the MarkMail email archive and the Digital Library from the Princeton Theological Seminary Library. The Princeton app opens up the archives of the world's 3rd largest seminary library to the public with rich search and actual images of the original books all with just the XML about the books and XQuery.
For our application, we'll be using XQuery to create an application to let New Haven pizza lovers create a pie from their favorite pizza place that is accurate in the toppings you can select and also only available when the actual places are open in New Haven (a big part of New Haven pizza is waiting for it).
Our XML data for the application looks like this:
<new-haven-pizza>
<restaurant>
<name>Sally's</name>
<hours>
<set>
<day>Tuesday</day>
...
<open>17:00:00</open>
<close>22:30:00</close>
</set>
</hours>
<apizza>
<sizes>
<size>Small</size>
...
</sizes>
<toppings>
<topping>Mozzarella</topping>
<topping>Anchovies</topping>
<special>...</special>
apizza>
</restaurant>
</new-haven-pizza>
There are some variations that we explored in Part 1 and we'll account for these in our code.
I plan to have the application be a Facebook app that allows users to select a restaurant and then order a pie. To do this we need a set of services to return values for each step in the process. I'll collect these services in a single XQuery module and then call them from the main code of the Facebook interface (that I'll put together in Part 3).
The first service is a list of restaurants. Since I want to be true to the restaurants hours, I need to use some of the extensive XQuery date functions to see if things are open as well as use the day of week name function from FunctX, the XQuery function library.
I'll start with this query that returns the list of restaurants:
for $restaurant in /new-haven-pizza/restaurant/name return $restaurant
I need to add to this basic function to check the day and time and only show what's open. I also need to have an error message and, since this is a service that will be feeding a web application directly, I'll go ahead and make the everything return the appropriate XHTML. So <select> elements from forms and <p>s for error messages etc.
module namespace nh="http://xquery.typepad.com/nh-apizza";
(: import the functx module with the day of week name function :)
import module namespace functx="http://www.functx.com" at "functx.xqy";
(: get restaurant function :)
declare function nh:get-restaurants() {
(: get the current date and time :)
let $current-daytime := fn:current-dateTime()
(: use the day of week name function to see what day it is :)
let $day := functx:day-of-week-name($current-daytime)
(: get the time :)
let $time := xs:time($current-daytime)
(: use the day and time to get the restaurants that are open :)
let $restaurants := /new-haven-pizza/restaurant[./hours/set/day eq $day]
(: [xs:time(./hours/set/open) < $time]
[xs:time(./hours/set/close) > $time] :)
return
(: return the restaurants Or an error :)
if ($restaurants) then
<select name="restaurant">
{for $restaurant in $restaurants
return
(: lets use the fullname if its available for the label :)
<option value="{fn:string($restaurant/name)}">{fn:string(if ($restaurant/full-name) then $restaurant/full-name else $restaurant/name)}</option>
}</select>
else
<p>Nothing is open right now! It is {$day} {$time} Eastern Time.</p>
};
The next functions pick up from here by listing the options for making a pie once you have selected a restaurant. These are the sizes, the toppings (with the specials as toppings if the restaurant allows it) and the specials.
For each function we do a simple value test on the name element and then go get what we need:
<select name="size">
{for $size in /new-haven-pizza/restaurant[./name eq $restaurant]/(apizza | tomato-pie)/sizes/size
return
<option value="{fn:string($size)}">{fn:string($size)}</option>
}</select>
};
To get the full topping list and include the specials that could be used as toppings (an oddity of Modern) we need to do some additional checking on the <special> element and also make use of the flexible XPath model (element1 | element2) to return topping *and* special elements in the same statement:
declare function nh:get-toppings($restaurant) {
for $topping in /new-haven-pizza/restaurant[./name eq $restaurant]/(apizza | tomato-pie)/toppings/(topping | special[../../combine-specials eq "yes"])
return
<input type="checkbox" name="topping" value="{fn:string($topping)}">{fn:string($topping)}</input>
};
And then the pretty simple list of specials:
declare function nh:get-specials($restaurant) {
for $topping in /new-haven-pizza/restaurant[./name eq $restaurant]/(apizza | tomato-pie)/toppings/special
return
<input type="radio" name="special" value="{fn:string($topping)}">{fn:string($topping)}</input>
};
All of these functions provide the information needed to create the pie selection forms. Once we gather that data back, I need a pie maker function to present the finished pie:
let $pie := if ($special) then
$special
else
fn:string-join($toppings, ', ')
return
<p>Here is your {$size} {$pie} from {$restaurant}. Enjoy and thanks for using the New Haven Pizza Maker!</p>
};
And thats really it! Its under 60 lines of code to make a complete application. This simplicity has a lot to do with the fact that I'm never leaving the world of XML. There is no impedance mismatch (that Dave Kellogg blogged about in this post) and XQuery has everything you need to execute a content application, all in one language whose output *is* XML.
This is why XQuery applications are a growing trend - people are discovering what Joris Petrus Maria Graaumans proved in his Doctoral Thesis three years ago - XQuery is just plain better! My entry on his paper is here and the whole thing is here.
In the next part of this series I'll put it all together, build the Facebook interface and launch the New Haven Pizza maker.
See you next time,
Matt
Excellent Blog every one can get lots of information for any topics from this blog nice work keep it up.
Posted by: custom dissertation | August 02, 2009 at 06:31 AM