Using the MarkLogic JavaScript driver in a Roxy hybrid application…

Developers want evermore speedy ways to complete their applications. Many want to use familiar languages and concepts like JavaScript, JSON, CSS and XHTML. In this post I show how you can quickly integrate my MLDB JavaScript API wrapper in to your own Roxy web application without learning any XQuery. I’ll show how to create a search page, and load JSON documents in to MarkLogic. I have both a video with instructional audio, and written instructions for those that need them. Read on for more…


You need to follow the first tutorial on creating a shell hybrid Roxy and REST API application. This tutorial follows on from that.

BEFORE YOU START CODING: The full src directory for the application, and code Gists, are available via links from the bottom of this article.

YouTube video version

Text version

Below is the full list of items covered in the video above. Just incase you’re on a train or can’t understand my crazy British accent!

Creating a new Roxy controller and view

Run the following command to create a new controller with a main function, and a html web view.

./ml create search html

Type ‘yes’ and hit enter when asked

Copy in custom layout and CSS

Roxy follows the same conventions as the MarkLogic application builder – that is, it assumes every application you create will have a search bar on every page. This is coded in to the layout so that page developers don’t need to do anything. We though need to embed our search widget on a single page in the application, and so we need a custom layout that doesn’t show the search bar in the header. The ones linked below and their CSS (the ‘less’ file) do this for you, so you don’t need to learn Roxy layouts at this stage. (Something to take a look at if, like with me, insomnia strikes!)

Copy the one-column.html.xqy file to src/app/views/layouts
Copy the one-column.less file to src/public/css

The widgets I’ve provided as part of MLDB are designed so you can easily use CSS to apply any styling or layout you want. This flexibility is good, but does mean casual users would find it tricky to get started with a good looking layout. To help this along, I’ve also included class links for the 960.css grid layout. This is a fantastic approach to CSS I highly recommend. It means I don’t need to understand how interactions happen between containers and elements, I just need to use the appropriate class definition.

For this exercise, we’ll just use the 960.css. You’ll need to download this from here: (Click the Big ol’ download button!)

Unzip this file and place the 960 folder in full under src/public/css

Edit the search controller to use our new layout

If using TextMate a mac, type ‘mate .’ in the command line. This opens a file view with folder so you can browse an entire application.

Edit the src/app/controllers/search.xqy file

Replace your main() function with the below. This instructs Roxy to use the one-column layout for this page’s rendering in (x)html.

declare function c:main() as item()*
  ch:add-value("message", "This is a test message."),
  ch:add-value("title", "Search"),
  ch:use-view((), "xml"),
  ch:use-layout("one-column", "html")

Edit the search main view to link to our widgets

You could add the relevant CSS and JavaScript files to the layout, but I’m assuming you’re writing a large application where you don’t want to load everything in to the browser for every page, ‘just in case’. Also because I want to show you what pieces you need for each part of the application.

Edit the src/app/views/search/main.html.xqy file. Replace the contents of the div tag (not the div tag itself) with this Gist. (01-main.html)

There are a few obvious files in here. Firstly, the mldb.js driver code itself. Then widgets.css (the default classes) and widgets.js files (widget baseline code). Also the widget-search JavaScript file which contains all search widgets (currently 7 of them – searchbar, facets, pager, sorter, results, search page, co-occurence).

There’s also a less obvious page-search.js. I like putting ALL JavaScript in external files. XQuery files dislike { and } characters as they’re inline code indicators. Also, using an external file keeps your code neat, and should be done as best practice. </lecture>

The last odd reference is the mldb-jquery.js file. I had many options to use as transport wrappers for AJAX calls that work cross browser. I’ve shipped three. jQuery (which Roxy layouts all include links to, and is shipped with Roxy), Prototype.js (which is a favourite library of mine) and xhr which attempts to user browsers’ own XmlHttpRequest objects. The jQuery one has received most testing as it makes the most sense with Roxy applications. Feel free to switch for mldb-prototype.js or mldb-xhr.js too, if you like. (And if / when they don’t work, add the problem and JavaScript console output to my Issues Tracker for MLDB)

Install MLDB in to your application

Go and fetch the latest mldb-browser.tar.gz or file and unzip it directly in to your src/public folder (this should copy css in to /src/public/css and javascript in to /src/public/js folders)

Done! 8o) Easy huh?

Linking it all together

Now we need to link our HTML div tag container to an instance of our search page widget. We do this by creating the /src/public/js/page-search-main.js file:-

$(document).ready(function() {
  var db = new mldb();

  var wgt = new com.marklogic.widgets.searchpage("search-content");
  var ob = db.options();
  var options = ob.toJson();



The above code creates an MLDB instance (which auto links to the REST server this app is deployed into), adds a new search widgets, and configures it’s options so that it includes a facet constant and some sensible defaults (10 results per page, returned as full JSON documents). We also execute the empty search (return all docs the user has permissions to see).

Deploying the application

You will have already done a full bootstrap and deploy in the previous tutorial, so now you just need to deploy the app again. Type this in the command terminal:-

./ml local deploy modules

Now navigate your browser to:  http://localhost:8101/search

If all has gone well, after a second or so you should see a well laid out search page, with facets (‘browse’) area on the left, and a search bar on the right with search paging, sorting and results areas below it. Type anything and click ‘search’. You’ll notice it works, but no results are returned.

Add some sample content

Now we’ll prove it works by adding some sample content. You can just upload these in to MarkLogic, but its much more fun (and a learning experience!) to do this via MLDB and JavaScript!

Create a new load method in your controller, with an associate load.html.xqy view:-

./ml create search/load html

Again, edit the /src/app/controllers/search.xqy file so it’s load() method looks like this:-

declare function c:load() as item()*
  ch:add-value("message", "This is a test message."),
  ch:add-value("title", "Load"),
  ch:use-view((), "xml"),
  ch:use-layout("one-column", "html")

Now edit the /src/app/views/search/load.html.xqy file, and replace it’s div tag and contents with this Gist (04-load.html).

Create /src/public/js/page-search-load.js so it contains this:-

$(document).ready(function() {
  var db = new mldb();

  var docs = [
    {title: "The Goonies",actor: "Sean Astin", genre: "Comedy", year: "1985"},
    {title: "50 First Dates",actor: "Sean Astin", genre: "Comedy", year: "2004"},
    {title: "Kingdom Hearts",actor: "Sean Astin", genre: "Comedy", year: "2002"},
    {title: "The Sky Is Falling",actor: "Sean Astin", genre: "Comedy", year: "2001"},
    {title: "Dorothy and the Witches of Oz",actor: "Sean Astin", genre: "Fantasy", year: "2012"},
    {title: "The Lord of the Rings: The Return of the King",actor: "Sean Astin", genre: "Fantasy", year: "2003"},
    {title: "The Lord of the Rings: The Two Towers",actor: "Sean Astin", genre: "Fantasy", year: "2002"},
    {title: "The Lord of the Rings: The Fellowship of the Ring",actor: "Sean Astin", genre: "Fantasy", year: "2001"},
    {title: "Teenage Mutant Ninja Turtles",actor: "Sean Astin", genre: "Adventure", year: "2013"},
    {title: "Dumb and Dumber To",actor: "Jim Carrey", genre: "Comedy", year: "2014"},
    {title: "Kick-Ass 2",actor: "Jim Carrey", genre: "Comedy", year: "2013"},
    {title: "Mr. Popper's Penguins",actor: "Jim Carrey", genre: "Comedy", year: "2011"},
    {title: "I Love You Philip Morris",actor: "Jim Carrey", genre: "Comedy", year: "2009"},
    {title: "Ace Ventura: When Nature Calls",actor: "Jim Carrey", genre: "Comedy", year: "1995"},
    {title: "Ace Ventura: Pet Detective",actor: "Jim Carrey", genre: "Comedy", year: "1994"},
    {title: "A Christmas Carol",actor: "Jim Carrey", genre: "Drama", year: "2009"},
    {title: "The Number 23",actor: "Jim Carrey", genre: "Drama", year: "2007"}

  for (var i = 0;i < docs.length;i++) {[i],"/movies/" + i,{collection: "movies"}, function(result) {
      // do nothing - assume it works

Save all the files, then redeploy using this:-

./ml local deploy modules

Now visit this page, and wait 15 seconds (so the underlying javascript completes). You will see no confirmation message. http://localhost:8101/search/load

When done, visit the search page again: http://localhost:8101/search

You should see results in the search page. Voila! Not so difficult is it?

Type in ‘drama’ and click search – you see only two results. Even with no specific search indexes, full text search using the universal index still works and enables you to find all relevant information.

Next steps

At the moment the search results show the XML or JSON content (depending on what the document contains). In most cases you’ll want to customise these results. You’ll also want to define constraints and facets to control the search, and sort options. I cover all this in my next blog entry…


You can access the final complete application here: [tar.gz via SugarSync public link]

The Gists for this application are contained here: [Gist website]

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.