Przeglądaj źródła

HUE-1403 [hbase] Searchbar Column Prefix & FilterString Support

Kevin Wang 12 lat temu
rodzic
commit
c73dca8

+ 10 - 4
apps/hbase/src/hbase/api.py

@@ -148,8 +148,14 @@ class HbaseApi(object):
       scan_length = int(query['scan_length'])
       if query['row_key'] == "null":
         query['row_key'] = ""
-      if scan_length == 1:
-        aggregate_data += self.getRows(cluster, tableName, query['columns'] or columns, query['row_key'], query['scan_length'], query['prefix']) #to be replaced in future
-      elif scan_length > 1:
-        aggregate_data += self.getRows(cluster, tableName, query['columns'] or columns, query['row_key'], query['scan_length'], query['prefix'])
+      filterstring = query.get('filter', None)
+      if filterstring:
+        scan = get_thrift_type('TScan')(startRow=query['row_key'], stopRow=None, timestamp=None, columns=query['columns'] or columns, caching=None, filterString=filterstring, batchSize=None)
+        scanner = client.scannerOpenWithScan(tableName, scan, None)
+        aggregate_data += client.scannerGetList(scanner, query['scan_length'])
+      else:
+        if scan_length == 1:
+          aggregate_data += self.getRows(cluster, tableName, query['columns'] or columns, query['row_key'], query['scan_length'], query['prefix']) #to be replaced in future
+        elif scan_length > 1:
+          aggregate_data += self.getRows(cluster, tableName, query['columns'] or columns, query['row_key'], query['scan_length'], query['prefix'])
     return aggregate_data

+ 1 - 1
apps/hbase/src/hbase/views.py

@@ -44,7 +44,7 @@ def app(request):
 def api_router(request, url): #on split, deserialize anything
   def safe_json_load(raw):
     try:
-      return json.loads(re.sub(r'(?:\")([0-9]+)(?:\")', r'\1', str(raw).replace("'", "\"")))
+      return json.loads(re.sub(r'(?:\")([0-9]+)(?:\")', r'\1', str(raw)))
     except:
       return raw
   def deserialize(data):

+ 22 - 0
apps/hbase/static/css/hbase.css

@@ -433,6 +433,26 @@ ul#hbase-breadcrumbs li a
 	background: #5EB568;
 }
 
+#search-tags span.filter
+{
+	color: #2DA2E0;
+}
+
+#search-tags span.filter span.compare_op
+{
+	color: #8746DB;
+}
+
+#search-tags span.filter span.arg
+{
+	color: #8746DB;
+}
+
+#search-tags span.filter span.linker
+{
+	color: #46A35C;
+}
+
 #search-tags span.selection, #search-typeahead span.selection
 {
 	background: yellow;
@@ -587,3 +607,5 @@ textarea
 {
 	font-size: 50px;
 }
+
+

+ 1 - 1
apps/hbase/static/js/app.js

@@ -217,7 +217,7 @@ function bindSubmit()
     if(submitVal)
     {
       if(use_post)
-        submitVal = "hbase-post-key-" + submitVal;
+        submitVal = "hbase-post-key-" + JSON.stringify(submitVal);
       else
         submitVal = prepForTransport(submitVal);
       data.push(submitVal);

+ 51 - 15
apps/hbase/static/js/controls.js

@@ -16,13 +16,26 @@
 
 var searchRenderers = {
   'rowkey': { //class to tag selection
-     select: /([^,]+\[[^\]\[]+\]|[^,]+)/g, //select the substring to process, useful as JS has no lookbehinds old: ([^,]+\[([^,]+(,|)+)+\]|[^,]+)
+     select: /[^\,\{\[]+(([\{\[][^\}\]]+[\}\]])+|)([^\,]+|)/g, //select the substring to process, useful as JS has no lookbehinds old: ([^,]+\[([^,]+(,|)+)+\]|[^,]+)
      tag: /.+/g, //select the matches to wrap with tags
-     strip: /,(?![^\[\]\:]+[^\]\[]+\])/g, //strip delimiters and post-process string to make nice
      nested: {
-        'scan': { select: /\+[0-9 ]+/g, tag: /.+/g, strip: /a^/g },
-        'columns': { select: /\[.+\]/g, tag: /[^:,\[\]]+:([^,\[\]]+|)/g, strip: /a^/g }, //forced to do this select due to lack of lookbehinds /[\[\]]/g
-        'prefix': { select: /[^\*]+\*/g, tag: /\*/g, strip: /a^/g }
+        'scan': { select: /\+[0-9 ]+/g, tag: /.+/g},
+        'columns': { select: /\[.+\]/g, tag: /[^:,\[\]]+:([^,\[\]]+|)/g}, //forced to do this select due to lack of lookbehinds /[\[\]]/g
+        'prefix': { select: /[^\*]+\*/g, tag: /\*/g},
+        'filter': {
+          select: /\{[^\{\}]+\}/,
+          tag:/.+/g,
+          nested: {
+            'linker': {
+              select: /AND|OR|SKIP|WHILE/g,
+              tag: /.+/g
+            }/*,
+            'compare_op': {
+              select: /[\<\=\!\>]{1,2}/g,
+              tag: /.+/g
+            }*/ //will be added eventually after html bug is figured out
+          }
+        }
      }
   }
 };
@@ -144,21 +157,23 @@ var SmartViewModel = function(options)
 
   self.searchQuery.subscribe(function goToRow(value) //make this as nice as the renderfucnction and split into two, also fire not down on keyup events
   {
-    var inputs = value.split(searchRenderers['rowkey']['select']);
+    var inputs = value.match(searchRenderers['rowkey']['select']);
     self.querySet.removeAll();
     for(var i=0;i<inputs.length;i++)
     {
       if(inputs[i].trim() != "" && inputs[i].trim() != ',')
       {
         var p = inputs[i].split('+');
-        var scan = p.length > 1 ? parseInt(p[1]) : 0;
+        var scan = p.length > 1 ? parseInt(p[1].trim()) : 0;
         var extract = inputs[i].match(searchRenderers['rowkey']['nested']['columns']['select']);
         var columns = extract != null ? extract[0].match(searchRenderers['rowkey']['nested']['columns']['tag']) : [];
+        var filter = inputs[i].match(searchRenderers['rowkey']['nested']['filter']['select']);
         self.querySet.push(new QuerySetPeice({
-          'row_key': p[0].replace(/\[.+\]|\*/g,'').trim(), //clean up with column regex selectors instead
+          'row_key': p[0].replace(/[\[\{].+[\]\}]|\*/g,'').trim(), //clean up with column regex selectors instead
           'scan_length': scan ? scan + 1 : 1,
           'columns': columns,
-          'prefix': inputs[i].match(searchRenderers['rowkey']['nested']['prefix']['select']) != null
+          'prefix': inputs[i].match(searchRenderers['rowkey']['nested']['prefix']['select']) != null,
+          'filter': filter != null && filter.length > 0 ? escape(filter[0].slice(1, -1)) : null
         }));
       }
     }
@@ -449,7 +464,8 @@ var ColumnRow = function(options)
     {
       if(data.length > 0 && !skipPut)
         self.value(data[0].value);
-      callback();
+      if(typeof callback !== "undefined" && callback != null)
+        callback();
       self.isLoading(false);
     });
   };
@@ -458,7 +474,7 @@ var ColumnRow = function(options)
   {
     //change transport prep to object wrapper
     logGA('put_column');
-    API.queryTable('putColumn', self.parent.row, self.name, "hbase-post-key-" + value).done(function(data)
+    API.queryTable('putColumn', self.parent.row, self.name, "hbase-post-key-" + JSON.stringify(value)).done(function(data)
     {
       self.reload(function(){}, true);
     });
@@ -550,6 +566,7 @@ var QuerySetPeice = function(options)
     scan_length: 1,
     prefix: false,
     columns: [],
+    filter: null,
     onValidate: function(){}
   }, options);
   BaseModel.apply(self,[options]);
@@ -558,6 +575,7 @@ var QuerySetPeice = function(options)
   self.scan_length = ko.observable(options.scan_length);
   self.columns = ko.observableArray(options.columns);
   self.prefix = ko.observable(options.prefix);
+  self.filter = ko.observable(options.filter);
   self.editing = ko.observable(true);
 
   self.validate = function()
@@ -625,6 +643,18 @@ var tagsearch = function()
       shortcut: ']',
       mode: ['columns'],
       selected: false
+    },
+    {
+      hint: 'Start FilterString',
+      shortcut: '{',
+      mode: ['rowkey'],
+      selected: false
+    },
+    {
+      hint: 'End FilterString',
+      shortcut: '}',
+      mode: ['filter'],
+      selected: false
     }
   ]);
   self.activeHints = ko.computed(function()
@@ -655,6 +685,10 @@ var tagsearch = function()
     'prefix': {
       hint: 'Rows starting with',
       type: 'String'
+    },
+    'filter': {
+      hint: 'Thrift FilterString',
+      type: 'String'
     }
   }
 
@@ -681,7 +715,7 @@ var tagsearch = function()
           hasMatched = true;
           return "<span class='" + keys[i] + " tagsearchTag' title='" + keys[i] + "' data-toggle='tooltip'>" + ('nested' in renderers[keys[i]] ? self.render(tagged, renderers[keys[i]].nested) : tagged) + "</span>";
         });
-        if(hasMatched)
+        if(hasMatched && renderers[keys[i]]['strip'])
           processed = processed.replace(renderers[keys[i]].strip, '');
         return processed;
       });
@@ -700,15 +734,17 @@ var tagsearch = function()
       self.mode('idle');
       return;
     }
-    var tokens = "[]+,-";
+    var tokens = "[]+,*{}";
     var m = 'rowkey';
     for(var i=selection.length - 1; i>=0; i--)
     {
       if(tokens.indexOf(selection[i]) != -1)
       {
-        if(selection[i] == '[')
+        if(selection[i] == '{')
+          m = 'filter';
+        else if(selection[i] == '[')
           m = 'columns';
-        else if(selection[i] == ']')
+        else if(selection[i] == ']' || selection[i] == '}')
           m = 'rowkey';
         else if(selection[i] == '+')
           m = 'scan';

+ 5 - 5
apps/hbase/static/js/utils.js

@@ -118,8 +118,8 @@ function launchModal(modal, data)
         }
             var cm = CodeMirror.fromTextArea(target, {
               mode: mime,
-            tabMode: 'indent',
-            lineNumbers: true
+              tabMode: 'indent',
+              lineNumbers: true
             });
             setTimeout(function(){cm.refresh()}, 401); //CM invis bug workaround
             element.find('input[type=submit]').click(function()
@@ -132,7 +132,7 @@ function launchModal(modal, data)
       break;
   }
   element.modal('show');
-  logGA(modal.slice(0, modal.indexOf('_modal') != -1 ? modal.indexOf('_modal') : modal.length));
+logGA(modal.slice(0, modal.indexOf('_modal') != -1 ? modal.indexOf('_modal') : modal.length));
 }
 
 function parseXML(xml)
@@ -229,9 +229,9 @@ function resetElements()
 
 function prepForTransport(value)
 {
-  value = value.replace(/\"/g,'\\"').replace(/\//g,'\\/');
+  value = value.replace(/\"/g,'\\\"').replace(/\//g,'\\/');
   if(isNaN(parseInt(value)) && value.trim() != '')
-    value = '"'+value+'"';
+    value = '"' + value + '"';
   return encodeURIComponent(value);
 };