diff --git a/js/app/views/EpnTapUI.js b/js/app/views/EpnTapUI.js
index 1531265..41a9a5c 100644
--- a/js/app/views/EpnTapUI.js
+++ b/js/app/views/EpnTapUI.js
@@ -186,7 +186,10 @@ Ext.create('Ext.data.Store', {
 			// console.log(Ext.decode(operation.response.responseText));
 		},
 		'metachange': function(store, meta) {
-			Ext.getCmp('epnTapGranulesGrid').reconfigure(store, meta.columns);
+			if(meta.metaHash != store.metaHash) {
+				Ext.getCmp('epnTapGranulesGrid').reconfigure(store, meta.columns);
+				store.metaHash = meta.metaHash;
+			}
 		}
 	}
 });
diff --git a/php/epntap.php b/php/epntap.php
index c1a906c..f4110bd 100644
--- a/php/epntap.php
+++ b/php/epntap.php
@@ -119,15 +119,8 @@ function getServices() {
 }
 
 function getNbResults() {
-	$url = getParam('url');
-	$tableName = getParam('tableName');
-	$targetName = getParam('targetName');
-	$productTypes = getParam('productTypes');
-	$timeMin = getParam('timeMin');
-	$timeMax = getParam('timeMax');
-
-	$query = "SELECT COUNT(*) AS nb_rows FROM $tableName" . createFilter($targetName, $productTypes, $timeMin, $timeMax);
-	$response = request($url, $query);
+	$query = "SELECT COUNT(*) AS nb_rows FROM " . getParam('tableName') . createFilter(getParam('targetName'), getParam('productTypes'), getParam('timeMin'), getParam('timeMax'));
+	$response = request(getParam('url'), $query);
 	if($response['success']) {
 		$response['success'] = false;
 		$response['msg'] = 'The service returned a bad value, can not get the number of results.';
@@ -153,23 +146,11 @@ function getNbResults() {
 }
 
 function getGranules() {
-	$url = getParam('url');
-	$tableName = getParam('tableName');
-	$targetName = getParam('targetName');
-	$productTypes = getParam('productTypes');
-	$timeMin = getParam('timeMin');
-	$timeMax = getParam('timeMax');
-	$start = getParam('start');
-	$limit = getParam('limit');
-	$nbRes = getParam('nbRes');
-	$sortKey = getParam('sort');
-	$sortDir = getParam('dir');
-	
-	$sort = is_null($sortKey) ? '' : "ORDER BY $sortKey $sortDir";
-	$filter = createFilter($targetName, $productTypes, $timeMin, $timeMax);
-	$query = "SELECT TOP $limit * FROM $tableName $filter $sort OFFSET $start";
+	$sort = is_null(getParam('sort')) ? '' : "ORDER BY " . getParam('sort') . " " . getParam('dir');
+	$filter = createFilter(getParam('targetName'), getParam('productTypes'), getParam('timeMin'), getParam('timeMax'));
+	$query = "SELECT TOP " . getParam('limit') . " * FROM " . getParam('tableName') . " $filter $sort OFFSET " . getParam('start');
 	// error_log('getGranules query: ' . $query);
-	$response = request($url, $query);
+	$response = request(getParam('url'), $query);
 	if($response['success']) {
 		$visibleColumns = ['granule_gid', 'obs_id', 'dataproduct_type', 'time_min', 'time_max', 'instrument_name', 'processing_level', 'access_estsize', 'thumbnail_url', 'access_url']; // rest are hidden
 		$names = ['granule_gid' => 'GID', 'dataproduct_type' => 'Type', 'processing_level' => 'Proc. lvl', 'access_estsize' => 'Size', 'access_url' => 'URL']; // default: pretty printed key name
@@ -186,13 +167,16 @@ function getGranules() {
 				'text' => array_key_exists($key, $names) ? $names[$key] : ucfirst(str_replace('_', ' ', $key)),
 				'width' => array_key_exists($key, $widths) ? $widths[$key] : 50,
 				'hidden' => !in_array($key, $visibleColumns),
+				'filterable' => true,
+				// 'filter' => ['type' => 'list', 'options' => ['Image', 'Map']],
 				'renderer' => 'granule.' . (array_key_exists($key, $renderers) ? $renderers[$key] : 'text')
 			];
 		}
 
-		$response['total'] = $nbRes;
+		$response['total'] = (int)getParam('nbRes');
 		$response['metaData']['fields'] = $fields;
 		$response['metaData']['columns'] = $columns;
+		$response['metaData']['metaHash'] = md5(serialize($response['metaData']));
 	}
 	return $response;
 }
--
libgit2 0.21.2