In the first post in this serie I covered the basics on how to create a list of Domino view data with pagination and sorting using the Domino JNA project. We can now get into the more interesting stuff: filtering the dataset.
Background on filtering with Domino JNA
Before diving into the code, it might be good te offer some background. Like I mentioned in the first post, Domino JNA surfaces some low level Domino (C) APIs. With those APIs it is possible to apply a selection/ filter on a view. Think of it as opening a view in the Notes client, and selecting a number of documents according to some selection criteria. The nice part about this is that it is really fast and is able to keep the sort order of the view, as well as doing pagination.
A selection is applied to a view by giving it a list of Note IDs:
Set<Integer> selectionList = new HashSet<Integer>(); collection.select(selectionList, true);
How you retrieve the list of Note IDs of documents that you want to select/ filter, is up to you: if a user wants to search for a specific last name, you can perform a view lookup to get a list of all the entries that match that name. But you can also get results from another view. Or, if you linked documents together using some ID, from related documents (e.g. performing JOIN-like queries).
To perform fast view lookups, JNA offers an optimised lookup function that performs better than the standard methods. Instead of using:
I used this optimised lookup function on the contacts list in the demo database to get a unique list of all cities and found the Domino JNA method to be about 7 times faster!
Note IDs as Integers, not Strings
When working with Domino JNA you’ll notice that it uses Integers to represent Note IDs in a database. This is done for efficiency reasons. Converting a Note ID from a String to an Integer can be done with a single line of code:
Integer id = Integer.parseInt(noteId, 16);
Filtering the contacts by cities
The demo application uses a list of 50,000 contacts. Every contact lives in a city, so let’s start with a filter on that. You could of course query the contacts by adding a Full Text index and using that, but I always run into issues. For example it isn’t really good at doing exact matches: if I only want people in city “X”, I don’t want them from “XY” (or “X Y”). Keeping the FT index up to date is also a challenge (BTW: looking forward to the changes in this area in Domino v10!).
For the cities filter I added to the third demo I used a Select2 item picker and populated that with all the available cities by performing a fast view lookup in the view (see the code below, rows 9-27). On my local VM it takes about 900 ms (for about 8,000 unique cities in the 50,000 contacts). The resulting list is cached in the applicationScope. Note that for the demo I populate the Select2 picker with a subset of all the available cities: 8,000 entries would really slow it down in the UI. In a real application you would use a server side lookup.
When the filter is applied, the list is reduced to those contacts that match the city/ cities: for every city selected in the filter, a lookup is performed to get the matching Note IDs (rows 95-99). Reading the entries that should be displayed on the current page is done in the loadEntries() function. It checks if there’s a filter applied (row 49), and if there is, it applies the list of matching Note IDs to the collection. It then iterates over the matching entries only (rows 54-56).
Adding more filters
Using this method, we can add more filters and combine the results. In demo 4 and demo 5 I added an extra filter on country (similar to the city filter) and on last name. Note that the demos do an “OR” lookup with all filters: if you select a city and country, the resulting list shows the results for the city as well as the country. The can easily be changed in the controller class by intersecting the lists of matching IDs.
The last name filter is a bit different: instead of doing a view entries/ column lookup to get an exact match, I do a lookup on a column that contains all last names and Note IDs. That list is cached in the applicationScope and used to find (partial) matches. The design of the ‘contacts’ view I used is available in the demo database.
The last en best demo (demo 6) shows how you can make filtering the list more user friendly/ intuitive: it updates the results after each change of one of the filters.
The source code for all demos is available on Github.
With just a few days ahead until Domino V10 is released, one of the first things that comes to my mind is: how does this all stack up against the new Domino Query Language. I guess that has to be the topic of a follow up post…