Handling a Trillion Data Using ZK"

From Documentation
Line 81: Line 81:
 
</source>
 
</source>
  
In the ''DemoWindowComposer'' class, we create a ''FakerMatrixModel'' instance, which implements [[#MatrixModel|MatrixModel]] to handle '''1,000,000''' columns and '''1,000,000''' rows of data and instantiates an anonymous [[#MatrixRenderer|MatrixRenderer]] class to render the HTML string output for display. We implement [[#MatrixComparatorProvider|MatrixComparatorProvider]] as the sorting function of the <b><i>Biglistbox</i></b> in order to provide each comparator from the given column index of the <b><i>Biglistbox</i></b> when users click on it. If you are familiar with ZK Listbox, you might notice that these implementations above look similar to that of manipulating ZK's Listbox.
+
In the ''DemoWindowComposer'' class, we create a ''FakerMatrixModel'' instance, which implements [[#MatrixModel|MatrixModel]] to handle '''1,000,000''' columns and '''1,000,000''' rows of data and instantiates an anonymous [[#MatrixRenderer|MatrixRenderer]] class to render the HTML string output for display. We implement [[#MatrixComparatorProvider|MatrixComparatorProvider]] as the sorting function of <b><i>Biglistbox</i></b> in order to provide each comparator from the given column index of the <b><i>Biglistbox</i></b> when users click on it. If you are familiar with ZK Listbox, you might notice that these implementations above look similar to that of manipulating ZK's Listbox.
  
 
== MatrixModel ==
 
== MatrixModel ==

Revision as of 06:59, 13 March 2012

WarningTriangle-32x32.png This page is under construction, so we cannot guarantee the accuracy of the content!

DocumentationSmall Talks2012MarchHandling a Trillion Data Using ZK
Handling a Trillion Data Using ZK

Author
Jumper Chen, Senior Engineer, Potix Corporation
Date
March 12, 2012
Version
ZK 6


Introduction

Recently, cloud services and storage are hot technology topics, this has also caused a buzz in the community the desire of displaying huge amounts of data in ZK. After several trials and experiments, we came up with a single component that would satisfy this need and we name it the Biglistbox. This Biglistbox component is able to handle unlimited data set for a big table whilst providing the same and as many as the functionalities of Listbox including selection, sorting, keystroke navigation, ROD(rendering-on-demand), and so on. Performance is also optimized at its full capacity; Biglistbox is designed to use as little server-side memory as possible and minimizing the updated area to its viewport at client-side, displaying data at a speed so fast you have never seen before.

Demo

This demo will demonstrate how one trillion data (1,000,000,000,000 cells) can be handled using Biglistbox

Demo Code Details

In the following subsections, I will guide you through how to implement ZUL, Composer, MatrixModel etc for the Biglistbox.

ZUL File

<biglistbox id="myComp" hflex="1" vflex="1" xmlns:w="client" w:onScroll="jq('$tip').fadeOut();">
    <!-- Template example
    <template name="heads">
        <html><![CDATA[
    <div class="images_${matrixInfo[0]%28}" title="x=${matrixInfo[0]},y=${matrixInfo[1]}">${each[matrixInfo[0]]}</div>
        ]]></html>
    </template>
    <template name="rows">
        <html><![CDATA[
     <div class="images_${matrixInfo[0]%28}" title="x=${matrixInfo[0]},y=${matrixInfo[1]}">${each[matrixInfo[0]]}</div>
        ]]></html>
    </template> -->
</biglistbox>

As you can see, Biglistbox supports V/Hflex functions so that it can automatically adjusts its size providing the powerful template feature to organize the layout of HTML output, and then you can utilize two attributes - rowIndex & colIndex from the matrixInfo object to receive the current index during template rendering phase. In this demo, we used MatrixRenderer to handle complex HTML output.

Composer

public class DemoWindowComposer extends SelectorComposer<Window> {
	//... omitted
	
	public void doAfterCompose(Window comp) throws Exception {
		super.doAfterCompose(comp);

		// specify a trillion faker model
		myComp.setModel(new FakerMatrixModel(1000 * 1000, 1000 * 1000));
		myComp.setColWidth("130px");
		myComp.setMatrixRenderer(new MatrixRenderer<List<String>>() {

			@Override
			public String renderCell(Component owner, List<String> data,
					int rowIndex, int colIndex) throws Exception {
				String d = data.get(colIndex);
				d = d.replace("ZK", "<span class='red' title='ZK'>ZK</span>")
						.replace("Hello", "<span class='blue' title='Hello'>Hello</span>");
				return "<div class='images_" + (colIndex%28) + "' title='x=" + 
				colIndex + ",y=" + rowIndex + "'>" + d + "</div>";
			}

			@Override
			public String renderHeader(Component owner, List<String> data,
					int rowIndex, int colIndex) throws Exception {
				return "<div class='images_" + (colIndex % 28) + "' title='"
						+ images[colIndex % 28] + "'>" + data.get(colIndex)
						+ "</div>";
			}
		});
		myComp.setSortAscending(new MyMatrixComparatorProvider<List<String>>(true));
		myComp.setSortDescending(new MyMatrixComparatorProvider<List<String>>(false));
	}

	//... omitted
}

In the DemoWindowComposer class, we create a FakerMatrixModel instance, which implements MatrixModel to handle 1,000,000 columns and 1,000,000 rows of data and instantiates an anonymous MatrixRenderer class to render the HTML string output for display. We implement MatrixComparatorProvider as the sorting function of Biglistbox in order to provide each comparator from the given column index of the Biglistbox when users click on it. If you are familiar with ZK Listbox, you might notice that these implementations above look similar to that of manipulating ZK's Listbox.

MatrixModel

Here is the source code of the MatrixModel interface

/**
 * The interface defines the methods that components like {@link Biglistbox}
 * to get the content of cells and headers from the matrix data.
 * @author jumperchen
 */
public interface MatrixModel<Row, Head, Cell, Header> extends ListModel<Row> {
	/**
	 * Returns the length of the columns
	 */
	public int getColumnSize();
	/**
	 * Returns the length of the heads
	 */
	public int getHeadSize();
	/**
	 * Returns the data object of the head from the specified index.
	 * @param rowIndex the index of the row in the headers
	 */
	public Head getHeadAt(int rowIndex);
	/**
	 * Returns the data object of the cell from the specified index.
	 * @param rowData the row data
	 * @param columnIndex the index of the column
	 */
	public Cell getCellAt(Row rowData, int columnIndex);
	/**
	 * Returns the data object of the header from the specified index.
	 * @param headData the head data
	 * @param columnIndex the index of the column
	 */
	public Header getHeaderAt(Head headData, int columnIndex);
}

In this demo, we create a FakerMatrixModel to implement the MatrixModel. Here is the fragment code:

public class FakerMatrixModel<Head extends List, Row extends List, Cell, Header> extends
		AbstractListModel<Row> implements MatrixModel<Row, Head, Cell, Header>, Sortable {

	// omitted...

	private boolean _sortDir = true;

	@Override
	public Row getElementAt(int index) {
		final int rowIndex = _sortDir ? index : getSize() - index - 1; // handle the sorting
		final String key = String.valueOf(rowIndex);
		List<String> value = _rowCache.get(key);
		if (value == null) {
			value = new FakerKeyList<String>(_colSize, rowIndex, new Fun() {
				@Override
				public Object apply(int index) {
					return "y = " + rowIndex;
				}});
			_rowCache.put(key, value);
		}
		return (Row) value;
	}

	// omitted...
}

The MatrixModel extends from ListModel interface and uses the getElementAt(int) method to receive the row data from the FakerKeyList object that implements the List interface. The reason we used FakerKeyList is that we encounter a big performance issue come from the default implementation that Java Collection Framework provided, the issue is that Java Collection Framework will go through each entry to gather the value of hashCode for seeking the key in the Map/Set or to check each entry for the equals and toString functions, this implementations will reduce the Biglistbox component's performance. Therefore, using the Biglistbox component with MatrixModel has to implement a clever simple list for traversing the huge data set.

For example,

private class FakerKeyList<T> extends AbstractList<T> {
	final int _size;
	Map<String, T> _updateCache = new HashMap<String,T> ();
	final Fun<?> _fn;
	final String _key;

	public FakerKeyList(int size, int key, Fun<?> fn) {
		_size = size;
		_key = key + "_" + size;
		_fn = fn;
	}

	@Override
	public T get(int index) {
		// if changed, returns the changed value
		Object val = _updateCache.get(String.valueOf(index));
		if (val != null)
			return (T) val;
		return (T) _fn.apply(index);
	}

	@Override
	public int hashCode() {
		return _key.hashCode();
	}
		
	@Override
	public boolean equals(Object obj) {
		if (obj == this)
			return true;
		if (obj instanceof FakerKeyList) {
			return _key.equals(((FakerKeyList)(obj))._key);
		}
		return false;
	}
	
	@Override
	public String toString() {
		return _key;
	}

	// omitted...
}

As you can see, we use a key string as the key for toString, hashCode, and equals methods to speed up the seeking time, and Fun class is a handy class to render the model data for this demo.

Note: By default, ZK do not provide an implementation class for the MatrixModel, because the Biglistbox component is designed to handle unlimited data set, there is no need to handle the model data in memory and most of this usage will be done by application-dependent. However, you can extend your own implementation from the AbstractListModel skeleton class.

MatrixRenderer

Here is the implementation of MatrixRenderer for this demo.

new MatrixRenderer<List<String>>() {

	@Override
	public String renderCell(Component owner, List<String> data,
			int rowIndex, int colIndex) throws Exception {
		String d = data.get(colIndex);
		d = d.replace("ZK", "<span class='red' title='ZK'>ZK</span>")
				.replace("Hello", "<span class='blue' title='Hello'>Hello</span>");
		return "<div class='images_" + (colIndex%28) + "' title='x=" + 
		colIndex + ",y=" + rowIndex + "'>" + d + "</div>";
	}

	@Override
	public String renderHeader(Component owner, List<String> data,
			int rowIndex, int colIndex) throws Exception {
		return "<div class='images_" + (colIndex % 28) + "' title='"
				+ images[colIndex % 28] + "'>" + data.get(colIndex)
				+ "</div>";
	}
}

As you can see, we must implement two methods renderCell and renderHeader that MatrixRenderer defines to generate the HTML output string for cells and headers. In the MatrixRenderer interface, the two methods we designed are to allow developer to generate any kind of HTML result for display.

MyMatrixComparatorProvider

Here is the source code of MyMatrixComparatorProvider to provide a corresponding comparator for Biglistbox to sort.

private class MyMatrixComparatorProvider<T> implements
			MatrixComparatorProvider<List<String>> {
	private int _x = -1;

	private boolean _acs;

	private MyComparator _cmpr;

	public MyMatrixComparatorProvider(boolean asc) {
		_acs = asc;
		_cmpr = new MyComparator(this);
	}

	@Override
	public Comparator<List<String>> getColumnComparator(int columnIndex) {
		this._x = columnIndex;
		return _cmpr;

	}

	// a real String comparator
	private class MyComparator implements Comparator<List<String>> {
		private MyMatrixComparatorProvider _mmc;

		public MyComparator(MyMatrixComparatorProvider mmc) {
			_mmc = mmc;
		}

		@Override
		public int compare(List<String> o1, List<String> o2) {
			return o1.get(_mmc._x).compareTo(o2.get(_mmc._x))
					* (_acs ? 1 : -1);
		}
	}
}

As you can see, the MyMatrixComparatorProvider class must implement the MatrixComparatorProvider interface that defines a single method getColumnComparator(int columnIndex) to receive the correct comparator from the specified column index. In this demo, we use a single comparator with different column index to sort the model data.

Component Usages

Features

Here lists the features of this Biglistbox component we support in this version.

  • Single selection
  • Column sorting
  • Keystroke navigation (Down, Up, Left, Right, Home, End, PageUp, PageDown, and Mousewheel)
  • Autosizing (V/Hflex)
  • Auto-adjust columns and rows for viewport
  • Client-side cache model
  • Render-on-Demand (client and server side)
  • Striping row and mouse event effects
  • Template rendering

Properties

Capture1.PNG

  • cols: specify the column size for the viewport.
  • rows: specify the row size for the viewport.
  • colWidth: specify the column width for each column.
  • rowHeight: specify the row height for each row.
  • matrixRenderer: specify the matrix renderer.
  • model: specify the matrix model.
  • oddRowSclass: specify the sclass for this component to stripe.

Events

  • onSelect: when users clicks or navigates on the row.
  • onSort: when users clicks on a column header
  • onCellClick: when users clicks on the row cell.
  • onScroll: when the scrolling bar is changed either x axis or y axis.
  • onScrollX: when the x axis of the scrolling bar changed
  • onScrollY: when the y axis of the scrolling bar changed

Component Limitation

  • Browsers support for IE8+, Firefox, Opera, Safari, and Chrome only.
  • The max memory usage for client side cache model is limited by end-users computer memory. (turn off by default)
  • Server side performance is depended on the MatrixModel implementation.

What's the Next

We are glad to hear which features are important for our users, that will be part of the next version. Here just lists a few features we can imagine.

  1. Multiple selection
  2. Column/Row size resizing
  3. Frozen table like MS-excel
  4. Tootip/Popup for each cell or header
  5. Embed ZK component inside the cell
  6. Multiple heads
  7. Merge cells (column/row)
  8. Scrolling tips
  9. Cell selection

Please give us feedback to vote which one is important for you.

Download

You can download the sample application code from its github repo here


Comments



Copyright © Potix Corporation. This article is licensed under GNU Free Documentation License.