April 26, 2010

XPages and Caching

Xpages provides you with the functionality to store key/value-pairs in different scopes: request, session, application and view. However, there is no expiry date on this data, at least by default.

I stumbled upon two great blog posts on how to implement a simple, but in many cases more than good enough, caching technique for your XPages variables.

Take a look at Matt White's blog entry or YouAtNotes' blog entry to get all the details!

Also, the rest of their blogs are strongly recommended for excellent XPages tips!

April 16, 2010

Extending the XPages Type Ahead Functionality

Xpages comes with a built-in type-ahead functionality for text input fields for your forms. This functionality is working OK straight out of the box, as long as you don't want to do anything too advanced...

There are several other blog entries out there that explain the basic and some more extended usage of the Type Ahead feature:keithstric.com, Lotus.com, DontPanic82. These entries (and others) all provide great information to get you started, but they didn't quite suit all my needs...

My task was as follows:
  • Develop a type ahead functionality for a company's employee search
    • Large number of employees (have to do a work-around for Notes' @DbLookup 64Kb limit).
  • You should get suggestions based on both first names and last names
    • Typing "and" in the input field should provide both "Andy Johnson", "John Anderson" and "Randy Thompson" as suggestions. (the built-in feature would only do a search for "and" or "and*", not "*and*")
Time to get started on some hacking on my own!


I started off by putting up the basic structure: add an Edit Box from the "Core Controls" menu in the Domino Designer and enabling the type ahead functionality from its property panel. By now, switching to the source view in the Designer, you should see something like this:


Left click somewhere within the xp:typeAhead tags to see all the type Ahead properties in your properties panel. I've ended up with these settings:

The important attributes are "minChars", "partial", "valueList", "valueMarkup" and "var". As long as you do your own computations (which we will discuss in a minute) to find the type ahead suggestions, the setting for "ignoreCase" doesn't matter.
  • minChars: the minimum number of characters that need to be in the input field to start looking for suggestions. I've set this property to 2, as keeping it at 1 (the default) will get me too many suggestions, while setting it to 3 or more kind of defeats the purpose of the type ahead. Set it to whatever suits your task :-)
  • mode: The AJAX request mode. Set it to "partial" to avoid a full page reload.
  • valueMarkup: setting this field to "true" tells the Designer that you will be taking care of creating the markup for the list of suggestions.
  • var: the name of the variable that contains the string entered in the input field. This variable is then accesible when we will create our suggestion list, which brings us to...
  • valueList: Now, this is where the magic happens! This is where you enter your code, so that you get the type ahead suggestions you want presented in the way you want it.

The code I ended up with in the "valueList" property is shown in its full below:

    //Getting the view containing a document for each of the employees
    var searchView:NotesView = session.getDatabase("","myemployees.nsf").getView("employeesTypeAhead");

    // Creating a Lotus Notes search query. Notice the reference to lupkey!
    var query = "(FIELD LastName CONTAINS *" + lupkey +"* OR FIELD FirstName CONTAINS *" + lupkey +"*)";

    // Creating an array to store hits in
    var searchOutput:Array = ["å","åå"];

    // Doing the actual search
    var hits = searchView.FTSearch(query);

    var entries = searchView.getAllEntries();
    var entry = entries.getFirstEntry();

    //Sort the array manually, since Notes doesn't want to sort them alphabetically
    for (i=0; i<hits; i++) {
    searchOutput.push(entry.getColumnValues()[0]);
    entry = entries.getNextEntry();
    }
    searchOutput.sort();

    // Build the resulting output HTML code
    var result = "<ul><li><span class='informal'>Suggestions:</span></li>";

    var limit = Math.min(hits,20);
    for (j=0; j<limit; j++) {
    var name = searchOutput[j].toString();
    var start = name.indexOfIgnoreCase(lupkey)
    var stop = start + lupkey.length;
    //Make the matching part of the name bold
    name = name.insert("</b>",stop).insert("<b>",start);
    result += "<li>" + name + "</li>";
    }

    result += "</ul>";
    return result;

    OK, so I want you to pay special attention to a few things...
    • For the search to work, you should make sure that the documents in the view have fields named "LastName" and "FirstName", or change the code to your needs.
    • By adding a "*" at the start and end of the lupkey variable in my search query, I'm able to get a partial match on any substring in the field values.The built-in feature will only allow partial match from the start of the field value (i.e. searching for "lupkey*").
    • When you do the FTSearch, the entries in the view will automatically be sorted by their search score (some mysterious score calculated by Notes showing how "relevant" the entry is for your query). Since I wanted my results sorted alphabetically, I had to take care of that myself.
    • The return value of the script must be an unordered list (see here for further information)

    Welcome

    I created this blog to share my highs and lows while developing an XPages application.