A generic approach to display Notes data via a Bootstrap table

This week I became inspired by a question I noticed on Stackoverflow regarding collecting values from Java objects.

For a project we were discussing what to use for display “Notes View data”:

  • jQuery DataTables plugin and use a customRestService via a Java class as data provider for the JSON.
  • a Repeat control and display an Arraylist of Java objects.

Some of the participants liked the jQuery approach because it provides a lot of functionality out of the box (sorting, search, responsiveness,…) and their lack in knowledge regarding Java.

Others were questioning of the Repeat control approach would be flexible enough so we could display easy different sets of data with different number of columns.

So… to the drawing board.

Mostly in Notes views (e.g. via the View Panel control) one column serves as link (often the first) and all others just display data of all kind. In the DataTables plugin the display is flexible because you can define a custom render function for each column which is great. So you could display a button that will call a dialog to interact with the underlying document for example. However this custom render definition resides in a CSJS library so becomes part of the design.

I had already a custom control that could consume properties for the data and how to set a fixed set of columns. So the last part I needed to make flexible.

Because I have an arraylist of Java objects as source for my Repeat control I need to access the fields “on the fly” when rendering the column values. This turned out to be quiet simple because the fields are already in my Java object.

In my approach I provide the columns via a JSON object that could look as followed:

dyntable00

 

And for my columns I repeat it and collect the value from my underlying Java object:

dyntable01

Note: my code is not final, I would to define more types of data and the option to provide a custom render function for each column. So far I only have computation for string and date fields.

I also applied some basic functionality as Pager controls (top + bottom), a Page Sizer control, icon display and row numbering.

Here is what a result might look like:

dyntable001

Nothing special but now now I have just one custom control to display 80% of my tables/views.

Here are the GIST files for the custom control and its configuration xml file.

IBM Champion Nomination

Is this blog-article useful to you? Perhaps you can nominate me as IBM Champion.

MapReduce views

Introduction

Frank van der Linden posted he made an update to his Cloudant connector plugin, so from now it should be possible to manage design documents.

To become more familiar with couchDB’s design documents I can recommend reading ‘Writing and Querying MapReduce Views in CouchDB‘.

Design documents

Design documents you mostly manage in CouchDB / Cloudant are documents that describe views of documents. Great if you can manage them via http and not as in IBM Notes via the Domino Designer client 😉

You could store other documents in your couchDb which are part of the design of your application (e.g. html, css, js files but that is another story).

Design documents are nothing more than JSON documents which are stored under the convention: must start with the _design/ pattern followed by the identifier e.g.: _design/default.

MapReduce

Views in couchdb are based on the MapReduce principle:

A MapReduce program is composed of a Map() procedure that performs filtering and sorting (such as sorting students by first name into queues, one queue for each name) and a Reduce() procedure that performs a summary operation (such as counting the number of students in each queue, yielding name frequencies).

This video explains it by using playing cards.

Mapping

The filtering and sorting are defined by a JavaScript function. You can state conditions in the function to return the documents you are interested in e.g.:

function(doc) {
if (doc.labels) {
for (var idx in doc.labels) {
if (doc.labels[idx] === “organic”) {
emit(doc._id, doc.name);
}
}
}
}

//docs have to have the field doc.labels and only the id and name from the ones that have the label organic are returned

Reducing

Reducing is optional and results in a summary operation. The options in couchDb are:

  • _sum
  • _count
  • _stats

Count is probably the most common function to use. Notice that the returned key value might be null if values appear in multiple documents. The key is no longer unique and therefor null.

{
“rows”: [{
“key”: null,
“value”: 7
}]
}

If you apply the group parameter in the requested url the values will be grouped so the response could look like:

{
“rows”: [{
“key”: “appel”,
“value”: 3
}, {
“key”: “banaan”,
“value”: 2
}, {
“key”: “peer”,
“value”: 2
}
]
}

Sum works only on numeric values (of course) so if your mapping functions looks like:

function(doc) {
if (doc.fruits) {
for (var i in doc.fruits) {
emit(doc.fruits[i], doc.calories);
}
}
}

with the _sum reduce option the response could look like:

{
“rows”: [{
“key”: “appel”,
“value”: 720
}, {
“key”: “banaan”,
“value”: 1368
}, {
“key”: “peer”,
“value”: 320
}]
}

The _stats option returns a JSON object containing the sum, count, minimum, maximum, and sum over all square roots of mapped values. So for the mapping function above the response could be:

{
“rows”: [{
“key”: “appel”,
“value”: {
“sum”: 720,
“count”: 2,
“min”: 272,
“max”: 448,
“sumsqr”: 274688
}
}, {
“key”: “banaan”,
“value”: {
“sum”: 1368,
“count”: 3,
“min”: 272,
“max”: 648,
“sumsqr”: 694592
}
}, {
“key”: “peer”,
“value”: {
“sum”: 720,
“count”: 2,
“min”: 272,
“max”: 448,
“sumsqr”: 274688
}
}]
}

With this knowledge fresh in mind you can use them in your view(s) setup. Remember the design documents can be managed via CRUD operations, e.g. on address http://localhost:5984/veganstore/_design/default

{
“_id”: “_design/default”,
“language”: “javascript”,
“views”: {
“store”: {
“map”: “function(doc) { if (doc.label) { emit(doc.label, doc.origin); }}”,
“reduce”: “_stats”
}
}
}

In one design document you can define multiple views…

You can query your views by a set of parameters:

  • conflicts (boolean) – Includes conflicts information in response. Ignored if include_docs isn’t true. Default is false
  • descending (boolean) – Return the documents in descending by key order. Default is false
  • endkey (json) – Stop returning records when the specified key is reached. Optional
  • end_key (json) – Alias for endkey param
  • endkey_docid (string) – Stop returning records when the specified document ID is reached. Requires endkey to be specified for this to have any effect. Optional
  • end_key_doc_id (string) – Alias for endkey_docid param
  • group (boolean) – Group the results using the reduce function to a group or single row. Default is false
  • group_level (number) – Specify the group level to be used. Optional
  • include_docs (boolean) – Include the associated document with each row. Default is false.
  • attachments (boolean) – Include the Base64-encoded content of attachments in the documents that are included if include_docs is true. Ignored if include_docs isn’t true. Default is false.
  • att_encoding_info (boolean) – Include encoding information in attachment stubs if include_docs is true and the particular attachment is compressed. Ignored if include_docs isn’t true. Default isfalse.
  • inclusive_end (boolean) – Specifies whether the specified end key should be included in the result. Default is true
  • key (json) – Return only documents that match the specified key. Optional
  • keys (json-array) – Return only documents where the key matches one of the keys specified in the array. Optional
  • limit (number) – Limit the number of the returned documents to the specified number. Optional
  • reduce (boolean) – Use the reduction function. Default is true
  • skip (number) – Skip this number of records before starting to return the results. Default is 0
  • sorted (boolean) – Sort returned rows (see Sorting Returned Rows). Setting this to false offers a performance boost. The total_rows and offset fields are not available when this is set to false. Default is true
  • stale (string) – Allow the results from a stale view to be used. Supported values: ok and update_after. Optional
  • startkey (json) – Return records starting with the specified key. Optional
  • start_key (json) – Alias for startkey param
  • startkey_docid (string) – Return records starting with the specified document ID. Requires startkey to be specified for this to have any effect. Optional
  • start_key_doc_id (string) – Alias for startkey_docid param
  • update_seq (boolean) – Response includes an update_seq value indicating which sequence id of the database the view reflects. Default is false

With this summary on views in couchDBcloudant fresh in mind let’s try to create some views programmatically in Cloudant with Frank’s updated plugin =)

Building a search function with DataTables plugin (IV)

Introduction

In the previous posts I demonstrated how to setup your application to generate a datatable component from your Notes view(s) and how to apply filtering & search capabilities to the columns.

In this post I will demonstrate how to build an external form to filter the datatable. I guess in most cases you want to provide some sort of search form and displayed separate from your table (left, right, top). For now we restrict the search form with input boxes.

Modifications

I have applied some modifications to the application. The complete and final code you can view in my Github repository.

Modifications:

  • java class that works as a custom service bean
  • javascript library for initializing datatable
  • xpage to display the datatable

Let’s add a form

The first thing we will do is by adding a Bootstrapped styled form to the xpage:

datatables04pre

Notice the following things:

  • The filter class for the input element
  • The data-column-index attribute for the input element

The filter class will be used to register events on:

$(‘.filter’).on(‘keyup change’, function () {
//clear global search values
db.search(”);
db.column($(this).data(‘columnIndex’)).search(this.value).draw();
});

The data-column-index attribute directs to the index of the corresponding column in the datatable.

With the form in place and the script updated we need to extend our java class so the values for the job title are included:

String job = String.valueOf(columnValues.get(10));
if (null!=job){
jo.put(“job”,job);
}

Let’s see the result

For example if I am looking for a person with firstname starting with Car.., from company Firm (something) acting as a host I get presented:

datatables04

Now with the initial search form in place we will extend it with features to improve it’s usability…