It’s been brought to my attention that the Roxy basic intro allows you to deploy an app, but doesn’t really cover what you need to develop an app. I thought I’d write a whistlestop guide to give you all an idea of how to quickly get up and running with a workable Roxy app.
Why do I care about Roxy? (Recap)
As I’ve previously mentioned, Roxy is an mVC architecture similar to Ruby on Rails. Note the lowercase ‘m’ because in document-land, we don’t have a model per-se. I tend to use the models folder in Roxy to store algorithm libraries that crunch data so the logic is out of the controllers and can be re-used. Roxy favours convention over configuration. So /articles/main.html would invoke the articles.xqy controller’s main() function, and render the data outputted as XHTML. There are globals for /js /images and /css also that map in to folders with the same names in a public folder in the Roxy app. Very similar to Ruby on Rails.
What you also get with Roxy is a great way to set up a MarkLogic server and to package and distribute your app. All minimum content you need for the app to run can be stored in a folder, as with index configuration. Once you deploy the app to a new server, the relevant app server, xdbc server, content and modules databases, indexes – the whole shebang – is created for you. Kinds neat, right? It also have a Test Driven Development (TDD) framework for unit testing, which is pretty awesome. Although all your code works right first time, right?!? It’s also Open Source under an Apache 2.0 license, so if you want to write a quick app in XQuery – which is much faster for an XML document search app than Java et al – then this is a fab way to get started.
Download and Install Roxy
This is a case of using git to download a blank, unconfigured Roxy application. The install instructions are pretty straightforward, so I’d advise to just go read them.
What you get out of the box
You get a basic search interface, a single view and controller. Search for anything in the system and, if you’ve already loaded a doc, you’ll see it in the results. There will be a named facet but it won’t do anything. You’ll need to configure that. As I said above, Roxy will create you a content DB, modules DB, schema DB, user, role, appserver and xdbc server for you. Saves a lot of time.
What to do first?
You’ll probably be building an application for search – so lets get some search facets configured. Open the file src/app/config/config.xqy. This is a file it is safe to edit (don’t edit one with a similar name in the /roxy/ folder). In here you’ll see you have the ability to define custom URL patterns, or routes, in $c:ROXY-ROUTES (like I’ve done previously to secure my web app with a login page), or search options using $c:SEARCH-OPTIONS. We’ll edit this. First though you’ll need a couple of range indexes. You can configure this in deploy/ml-config.xqy if you like for all future servers – but I tend to do this manually so I can play with it. Head over to port 8001 in the Administration tool and add the following Element Range Index:-
collation: default English
You can add other indexes too if you like. I’m going to add documents like this one:-
<article> <title>Holiday in Derbyshire</title> <author><person>Adam Fowler</person><email>email@example.com</email></author> <content> <para>Derbyshire is fantastic. Go there.</para> <para>Oh yes, fantastic indeed! Especially with <person>Joe Bloggs</person></para> </content> </author>
How to add this? We’ll do that in the next section. For now, lets go add our facet for person. Edit the SEARCH-OPTIONS variable to include this section:-
<constraint name="person"> <range type="xs:string" collation="http://marklogic.com/collation/en" facet="true"> <element ns="" name="person"/> <facet-option>limit=10</facet-option> <facet-option>frequency-order</facet-option> <facet-option>descending</facet-option> </range> </constraint>
Make sure the collation matches the one previously entered, else you’ll get an error message when you search for it. Note the facet=true on the range element. This makes it visible as a facet in the Roxy app as opposed to just a constraint you enter as a keyword in the search. E.g. person:”Adam Fowler”.
Adding new content
You don’t get a document creation upload OOTB in Roxy, so lets go and create one. This will show you how to create a new view and controller, and pass data between them. Open up a command line with the current directory as that containing your Roxy app (and the ml command) and execute this:-
./ml create articles html
type ‘yes’ and enter when prompted. This creates an articles.xqy controller, a main() function, and a main.html view.
Do this again for this command:-
./ml create articles/doupload html
This creates a doupload() function within your existing articles controller, with a doupload.html view (which we won’t use… but still…)
Go and edit src/app/controllers/articles.xqy. In here we’re going to create a variable to show the number of articles in the system, and display this in our view. Just do the data editing/retrieval in here. Edit main() so it looks like this.
So this passes our data to our view. The reason this is separated is because we may have an alternate view called main.xml that returns an XML view of the same data. Hence V and C in mVC. Note we’ve got simple actions so don’t need a model object or library to access this. Hence a lowercase m in mVC when we talk about Roxy. Now go and edit your view main.html so it looks like this.
Note this includes a form to enter information for a new article. We’ll talk about that in a moment.
For now, deploy the app using this command:-
./ml local deploy modules
Now go to <yourserver>:<port>/articles . Note this URL will work the same if you used /articles/main or /articles/main.html too – this is convention over configuration at work. Hopefully you should see ‘0 articles in database’ at the moment.
Lets edit the articles.xqy controller so that it creates a new article when the form is submitted. Note for brevity I’ve not done any checking that required fields are entered. Here’s my doupload() function.
Note this does not pass anything to a view and bypasses the view entirely, passing execution back to the main() function indirectly via the /articles URL one a new article is created. We could instead edit doupload.html to say ‘Creation done’, but that’s an extra click, and I hate adding extra clicks in to demos.
./ml local deploy modules
And hit the /articles URL again. Now enter information in the form and submit. Notice the message ‘1 document in database’ after this is complete.
You can now click on ‘Back to Search’ and see the facet on the left with your author name, and some basic search results.
You now have the basic skills required to create a Roxy app. There are various other considerations you may want to consider, including application interface level security, and session variables. These are out of scope for this article.
Download the final app
I have a Roxy app zip files called articles-roxy.zip on my SugarSync account (13 MB). Feel free to download and play. No warranty of course, your mileage may vary! It creates the app on port 8110. You’ll need to run ./ml local bootstrap and ./ml local deploy modules to install it. This also creates the person index for you.
- Data passed between controllers and views is achieved using a map:map(). Note this does not support storing binary data. So if you, for example, generate an excel file xslx which is a zip file for download, you’re best off having a separate view to set the right content type and generate the binary, passing the excel:worksheet in as a variable rather than the binary output.
- You can also use xdmp:set-session-field as you would in any Java Servlet app. Naturally, use sparingly to save application server memory.
- Any facets you create must have the relevant index configured first, else you’ll get an exception.
- If using your Roxy app in modules mode (default) rather than filesystem mode, you’ll need to do a ./ml local deploy modules after each edit prior to testing. This can get a bit monotonous, but does mean you can look at the current app whilst coding without the risk of breaking the app until you do a redeploy.