Roxy and some old CRUD

CRUD! I mean, sorry, Create Read Update Delete. In Ruby on Rails these concepts are very familiar. Because a RoR controller is typically tied to a particular model, and underlying relational table (ick) each controller comes built in with a set of CRUD methods. These are not provided when you create a Roxy controller. “Wait!” I hear you cry, “Wait! MarkLogic is used to search surely. Why, oh why, would you want to create content manually in MarkLogic Server!?!”. Well, I’m glad you asked! 8o)

A great use case for MarkLogic is to re-purpose existing content in to new content which can then be re-sold as a new product. Think of a large publisher. They are sitting on papers and books about Volcanos, Glaciers, City guides, Economics, a whole wealth of information. If I were to need to write a book on the last 25 years of Iceland, its history and make up, some of the above content could well be relevant. I may want to re-use it and include it in my new publication. These means I not only need to search for existing content, but I need to create new content that links these pieces together, perhaps with my own information (preface, contents, etc.) and save it as content in its own right.

In short I need to be able to create, read, update and potentially delete content. This means a commercial MarkLogic system, or indeed any system where you may need to create investigations, reports, or some other deliverable, will need to support CRUD methods in its controllers.

As I said, Roxy doesn’t provide these out of the box. They are trivial to add manually. You just use the command line ml tool as follows:-

ml create book/main html
ml create book/new html
ml create book/create html
ml create book/show html
ml create book/edit html
ml create book/update html
ml create book/delete html

This gives you everything you need for CRUD operations. You have main to list existing content (and Roxy creates this for you so you can search over rather than just list this content – a great time saver). You also have new to act as an entry form to create new content. Create is a PUT/POST operation that creates an item, then forwards to the show method. Edit is like show but is not read only. Update accepts a POST that updates a book, then forwards to edit (or show), and delete allows deletion, and is typically accessed from the show or edit pages, and usually restricted by role.

This is a great start, but has a couple of drawbacks. Firstly, although you can easily use one form to create a relational set of related facts (columns), this is hard for an entire document structure. You may also need extra controller functions. E.g. addChapter, linkChapter, removeChapter, listChapters. You get the idea. These don’t really make sense on their own as they belong as part of the book (<book> may be the document root element in your MarkLogic database, for example), so you’ll want to add these to the same book controller. A Roxy controller can thus be more sophisticated than a basic RoR controller ever would be.

Another unique thing about a document oriented paradigm like in MarkLogic is that you’re not searching content and creating new content as two separate activities. You may be navigating search results and dragging or dropping items on a webpage. This action of dragging and dropping may be updating the book (E.g. via linkChapter) whilst you remain on the search page. Thus you may have an embedded edit view for book within a separate controller’s (perhaps) search view. It makes sense therefore to support a RESTful interaction with the above CRUD methods.

The way to do this may be to add a ‘Create new book’ link on a right hand column on the search page. This may embed a mini new.html form to allow you to enter the title, genre, author info and other basics so you can later easily find the content. Clicking on ‘create’ would invoke the create method via AJAX and replace the new form with an edit form, that has a drop zone for chapters or other content you wish to interactively link in to the new book. When navigating pages this new book edit view must persist, so you’ll want to use xdmp:set-session-field() to store the current new book you’re working on, so the edit form can be rendered via ajax on each search page. Afterall there’s no problem delaying the edit form appearing as first you need to find the content anyway. This has the added benefit of the search page appearing quicker.

Once you’re linked all the existing content you need, you may click on a ‘Manage’ link on the book mini edit form to go to the full book controller’s edit page. This may be more sophisticated, allowing new content authoring and entry of extra book data. E.g. tags, summary, contents et al. This may use AJAX methods to update parts of the book on the fly too, all within the book edit page.

This has two important implications though. Firstly you need to be able to render part of the page dynamically via JavaScript and HTML. Secondly you’ll want to do this a lot if you have many controller that use the same pattern. Altering a html view to only spit out some html could be a pain. The way I get around this is to create a new view. This I call a htf view, or HTML Fragment. You can do this by doing this command:-

ml create book/edit htf

This creates an edit.htf.xqy view, and re-uses the edit controller function. If you re-factor the edit view so that the part that just shows the edit form is generated by a helper library (E.g.s lib-book-ui.xqy’s edit($book,$brevity) ) then you can show a detailed view on the full edit.html page and a more brief form on the edit.htf view.

But how to control the rendering and actions? How do I take the same form and AJAXify it. Happily I have created a CRUD javascript file that wraps any controller that supports the above functions and paradigm to make this easy. I enable this by embedding a hidden input field with class .appid to hold a URI or other identifier for any controller content in the page. This drives my drag/drop JQuery code, and means I can hide the book id that I’m editing. The CRUD.create function looks through my create form and extracts all input fields, and cancels the form http submit by returning false, and performs a PUT request to create.json. I use JSON here because I know if it fails I need to stay on the new mini form, whereas if it succeeds I will then call the edit.htf view via AJAX. So I’m really just expecting create to return either an error, or a success message and a new document URI for my CRUD.edit to load. I can also use CRUD.edit when I navigate to another search page. Saving the new Book in the user’s session allows me to load this wherever it’s needed too.

This provides a nice clean separation. It also removes any need for complex JavaScript from my XQuery code and views. It’s all managed in a single set of CRUD JavaScript methods, which are re-used globally. Using a html fragment view also allows nice separation, and opens up the future possibility of embedding parts of my application in another web application from a third party developer. All without them needing to use some horrendously complicated JavaScript api.

I’m going to work on this JavaScript CRUD object to add update and delete functions (currently only has new, create and edit) and perhaps addInclude, removeInclude, addContent, removeContent to deal with parts of the substructure of the full document. Let me know if you think this is useful.

One comment

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.