github.com/rohankumardubey/aresdb@v0.0.2-0.20190517170215-e54e3ca06b9c/api/ui/debug/js/batch.js (about)

     1  //  Copyright (c) 2017-2018 Uber Technologies, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  if (!String.prototype.format) {
    16      String.prototype.format = function () {
    17          var args = [].slice.call(arguments);
    18          return this.replace(/(\{\d+\})/g, function (a) {
    19              return args[+(a.substr(1, a.length - 2)) || 0];
    20          });
    21      };
    22  }
    23  
    24  const maxRowsPerPage = 100;
    25  const pagesToShow = 10;
    26  const maxValueLength = 100;
    27  var currentTableName = "";
    28  var currentShardID = 0;
    29  var currentBatchID = 0;
    30  
    31  function Iterator(column, numRows) {
    32      this.values = column.values;
    33      this.counts = column.counts;
    34      if (!this.values || !this.counts.length || !this.counts || !this.counts.length) {
    35          this.values = [null];
    36          this.counts = [numRows];
    37      }
    38      this.currentRow = 0;
    39      this.currentIndex = 0;
    40  }
    41  
    42  Iterator.prototype.shouldSkip = function () {
    43      return this.currentRow !== 0 && this.currentRow !== this.counts[this.currentIndex - 1];
    44  };
    45  
    46  Iterator.prototype.advance = function () {
    47      this.currentRow++;
    48      if (this.currentRow === this.counts[this.currentIndex]) {
    49          this.currentIndex++;
    50      }
    51  };
    52  
    53  Iterator.prototype.read = function () {
    54      return {
    55          value: this.values[this.currentIndex] === 'undefined' ? 'NULL' : this.values[this.currentIndex],
    56          count: this.currentIndex === 0 ? this.counts[this.currentIndex] : this.counts[this.currentIndex] - this.counts[this.currentIndex - 1]
    57      }
    58  };
    59  
    60  function renderTables(tables) {
    61      var tableSelect = $('#table-select');
    62      tables.forEach(function (table, id) {
    63          tableSelect.append('<option value' + '=' + id + '>' + table + '</option>');
    64      });
    65  }
    66  
    67  function getBatchSize(shard, id) {
    68      var batch = shard.liveStore.batches[id];
    69      if (shard.liveStore.lastReadRecord.batchID == id) {
    70          return shard.liveStore.lastReadRecord.index;
    71      }
    72      return batch.capacity;
    73  }
    74  
    75  function renderShard(shard) {
    76      var batchTableBody = $('#batch-table-body');
    77      batchTableBody.empty();
    78      Object.keys(shard.liveStore.batches).forEach(function (id) {
    79          var batchSize = getBatchSize(shard, id);
    80          batchTableBody.append('<tr id="batch-tr-{0}" class="clickable" onclick="loadBatchTable({1}, {2})"><td>{3}</td><td>{4}</td></tr>'.format(id, id, batchSize, id, batchSize));
    81      });
    82  
    83      Object.keys(shard.archiveStore.currentVersion.batches).forEach(function (id) {
    84          var batch = shard.archiveStore.currentVersion.batches[id];
    85          var date = new Date(parseInt(id, 10) * 86400000)
    86          var idStr = '{0} {1}-{2}-{3}'.format(id, date.getUTCFullYear(), date.getUTCMonth() + 1, date.getUTCDate());
    87          batchTableBody.append('<tr id="batch-tr-{0}" class="clickable" onclick="loadBatchTable({1}, {2})"><td nowrap>{3}</td><td>{4}</td></tr>'.format(id, id, batch.size, idStr, batch.size));
    88      });
    89  
    90      var numLiveRecords = shard.liveStore.lastReadRecord.index + shard.liveStore.batchSize * Object.keys(shard.liveStore.batches).filter(function (id) {
    91          return id != shard.liveStore.lastReadRecord.batchID;
    92      }).length;
    93  
    94      var primaryKeyView = $('#primary-key-view');
    95      primaryKeyView.empty();
    96      var primaryKeyStats = $('<table></table>');
    97      primaryKeyStats.append('<tr><td><b>Primary Key: </b></td></tr>');
    98      primaryKeyStats.append('<tr><td>' +
    99          ' <label for="primary-key-lookup-input">Primary Key Lookup: </label></td><td><input class="small-input" id="primary-key-lookup-input" type="text"/></td></tr>')
   100      primaryKeyStats.append('<tr><td>Allocated Memory (MB):</td><td>{0}</td></tr>'.format(shard.liveStore.primaryKey.allocatedBytes / 1024 / 1024));
   101      primaryKeyStats.append('<tr><td>Percentage:</td><td>{0}% ({1} / {2})</td></tr>'.format(numLiveRecords * 100 / shard.liveStore.primaryKey.capacity, numLiveRecords, shard.liveStore.primaryKey.capacity));
   102      primaryKeyStats.append('<tr><td>Cutoff:</td><td>{0}</td></tr>'.format(new Date(shard.liveStore.primaryKey.eventTimeCutoff * 1000).toLocaleString()));
   103      primaryKeyView.append(primaryKeyStats);
   104  
   105      var pkLookupInput = $('#primary-key-lookup-input')
   106      pkLookupInput.keyup(function (e) {
   107          if (e.keyCode === 13) {
   108              var uuid = $('#primary-key-lookup-input').val();
   109              $.ajax({
   110                  url: '/dbg/{0}/{1}/primary-keys?key={2}'.format(currentTableName, currentShardID, uuid),
   111                  dataType: 'json',
   112                  success: function (recordID) {
   113                      var batchID = recordID.batchID;
   114                      var row = recordID.index;
   115                      var batchSize = getBatchSize(shard, batchID);
   116                      jumpToRowOfBatch(row, batchID, batchSize, true);
   117                  },
   118                  error: function (xhr) {
   119                      alert(xhr.responseText);
   120                  }
   121              });
   122          }
   123      });
   124  }
   125  
   126  function getPolygonUrl(polygonStr) {
   127      var pointStrs = polygonStr.replace("Polygon", "").split(",");
   128      var sumLat = 0;
   129      var sumLong = 0;
   130      var path = "";
   131      for (var i in pointStrs) {
   132          var pointStr = pointStrs[i];
   133          var point = pointStr.replace(/[\(\)]/g, "").split("+");
   134          var lat = parseFloat(point[1]);
   135          var long = parseFloat(point[0]);
   136          path += '|{0},{1}'.format(lat, long);
   137          sumLat += lat;
   138          sumLong += long;
   139      }
   140      var avgLat = sumLat / pointStrs.length;
   141      var avgLong = sumLong / pointStrs.length;
   142      return 'http://maps.googleapis.com/maps/api/staticmap?center={0},{1}&size=2000x2000&zoom=10&sensor=false&path=color:0xff0000ff|weight:5{2}'.format(avgLat, avgLong, path);
   143  }
   144  
   145  function renderBatch(highlightRowNumber) {
   146      return function (json) {
   147          var header = document.getElementById('table-header');
   148          var html = '<th>Row Number</th>';
   149          for (var i = 0; i < json.columns.length; i++) {
   150              if (json.deleted !== null && json.deleted.includes(i)) {
   151                  html += '<th class="deleted-column data-column">' + json.columns[i] + '<br>' + json.types[i] + '</th>';
   152              } else {
   153                  html += '<th class="data-column">' + json.columns[i] + '<br>' + json.types[i] + '</th>';
   154              }
   155          }
   156          header.innerHTML = html;
   157  
   158          var iterators = json.columns.map(function (columnName, id) {
   159              if (id < json.vectors.length) {
   160                  return new Iterator(json.vectors[id], json.numRows)
   161                      ;
   162              } else {
   163                  return new Iterator({}, json.numRows);
   164              }
   165          });
   166  
   167          var body = document.getElementById('table-data');
   168          html = '';
   169          for (var r = 0; r < json.numRows; r++) {
   170              var rowCls = "";
   171              if (r == highlightRowNumber) {
   172                  rowCls = "highlight";
   173              }
   174              html += '<tr class="{0}"><td class="data-column">'.format(rowCls) + (r + json.startRow) + '</td>';
   175              for (var c = 0; c < iterators.length; c++) {
   176                  if (!iterators[c].shouldSkip()) {
   177                      var valueCount = iterators[c].read();
   178                      var ts = '';
   179                      if (c == 0 && json.types[0] == 'Uint32') {
   180                          ts = '<br>';
   181                          ts += new Date(valueCount.value * 1000).toLocaleString();
   182                      }
   183                      var fullValue = '' + valueCount.value;
   184                      if (fullValue.length > maxValueLength) {
   185                          html += '<td class="data-column short-value-column" data-value="{0}" rowspan="{1}">{2}</td>'.format(fullValue, valueCount.count, fullValue.substr(0, maxValueLength) + ts);
   186                      } else {
   187                          html += '<td class="data-column" rowspan="{0}">{1}</td>'.format(valueCount.count, fullValue + ts);
   188                      }
   189                  }
   190                  iterators[c].advance();
   191              }
   192              html += '</tr>';
   193          }
   194          body.innerHTML = html;
   195  
   196          // add popup for shortened value
   197          var shortColumns = document.getElementsByClassName("short-value-column");
   198          for (var j = 0; j < shortColumns.length; j++) {
   199              shortColumns[j].addEventListener("click", function () {
   200                  var fullValue = (this).getAttribute("data-value");
   201                  if (fullValue.startsWith("Polygon")) {
   202                      var url = getPolygonUrl(fullValue);
   203                      var win = window.open(url, '_blank');
   204                      win.focus();
   205                  } else {
   206                      alert(fullValue);
   207                  }
   208              });
   209          }
   210  
   211          // populate column select
   212          renderColumns(json.columns);
   213      };
   214  }
   215  
   216  function jumpToRowOfBatch(rowNumber, batchID, batchSize, highlight) {
   217      var prevBatchID = currentBatchID
   218      currentBatchID = batchID;
   219      if (currentBatchID != prevBatchID) {
   220          var prevElem = $("#batch-tr-{0}".format(prevBatchID))
   221          if (prevElem.length && prevElem.hasClass("highlight")) {
   222              prevElem.removeClass("highlight");
   223          }
   224  
   225          var curElem = $("#batch-tr-{0}".format(currentBatchID))
   226          if (curElem.length && !curElem.hasClass("highlight")) {
   227              curElem.addClass("highlight");
   228          }
   229      }
   230      var maxPage = Math.floor(batchSize / maxRowsPerPage) + 1;
   231      var startPage = Math.max(0, Math.floor(rowNumber / maxRowsPerPage));
   232      setPages(startPage, startPage + pagesToShow, maxPage);
   233  
   234      var highlightRowNumber = rowNumber % maxRowsPerPage;
   235      if (!highlight) {
   236          highlightRowNumber = -1;
   237      }
   238  
   239      loadBatch(startPage * maxRowsPerPage, maxRowsPerPage, highlightRowNumber);
   240  }
   241  
   242  function loadBatchTable(batchID, batchSize) {
   243      $('#row-jump').show();
   244      var rowNumberInput = $('#row-number-input');
   245      rowNumberInput.focus();
   246      rowNumberInput.keyup(function (e) {
   247          if (e.keyCode === 13) {
   248              var rowNumber = $('#row-number-input').val();
   249              jumpToRowOfBatch(rowNumber, batchID, batchSize, false);
   250          }
   251      });
   252  
   253      jumpToRowOfBatch(0, batchID, batchSize, false);
   254  }
   255  
   256  function setPages(startPage, endPage, maxPage) {
   257      var pagination = $('#batch-pagination');
   258      pagination.empty();
   259      pagination.append('<a href="#" onclick="setPages({0},{1},{2})">&laquo;</a>'.format(Math.max(startPage - pagesToShow, 0), Math.max(pagesToShow, startPage), maxPage));
   260      for (var i = startPage; i < Math.min(endPage, maxPage); i++) {
   261          pagination.append('<a href="#" onclick="loadBatch({0},{1},{2})">{3}</a>'.format(i * maxRowsPerPage, maxRowsPerPage, -1, i + 1));
   262      }
   263      pagination.append('<a href="#" onclick="setPages({0},{1},{2})">&raquo;</a>'.format(endPage, endPage + pagesToShow, maxPage));
   264  }
   265  
   266  function renderColumns(columns) {
   267      var columnSelect = $('#column-select');
   268      columnSelect.empty();
   269      columns.forEach(function (column, id) {
   270          columnSelect.append('<option value' + '=' + id + '>' + column + '</option>');
   271      });
   272  }
   273  
   274  function loadBatch(startRow, numRows, highlightRowNumber) {
   275      $.getJSON(
   276          '/dbg/{0}/{1}/batches/{2}?startRow={3}&numRows={4}'.format(currentTableName, currentShardID, currentBatchID, startRow, numRows),
   277          {},
   278          renderBatch(highlightRowNumber)
   279      );
   280  }
   281  
   282  function listBatches() {
   283      $.getJSON(
   284          '/dbg/{0}/{1}'.format(currentTableName, currentShardID),
   285          {},
   286          renderShard
   287      );
   288  }
   289  
   290  function listTables() {
   291      $.getJSON(
   292          '/schema/tables',
   293          {},
   294          renderTables
   295      );
   296  }
   297  
   298  function getTableSchema(table) {
   299      $.getJSON(
   300          '/schema/tables/{0}'.format(table),
   301          {},
   302          function (schema) {
   303              var columns = schema.columns.map(function (column) {
   304                  return column.name;
   305              });
   306              renderColumns(columns);
   307          }
   308      );
   309  }
   310  
   311  function loadVectorParty(batchID, columnName) {
   312      $.ajax({
   313          url: '/dbg/{0}/{1}/batches/{2}/vector-parties/{3}'.format(currentTableName, currentShardID, batchID, columnName)
   314      }).done(listBatches);
   315  }
   316  
   317  $(document).ready(function () {
   318      $('#row-jump').hide();
   319      initShardPicker();
   320      initBatchLoader();
   321      listTables();
   322  });
   323  
   324  function initShardPicker() {
   325      var shardPicker = $('#shard-pick');
   326      shardPicker.append('<label for="table-select">Table: </label>');
   327      shardPicker.append('<select id="table-select"></select>');
   328      shardPicker.append('<label for="shard-select">Shard: </label>');
   329      shardPicker.append('<select id="shard-select" disabled><option>0</option></select>');
   330      var shardPickButton = $('<button class="medium-button">Submit</button>');
   331      shardPickButton.click(function (event) {
   332          currentTableName = $('#table-select').find(":selected").text();
   333          currentShardID = $('#shard-select').find(":selected").text();
   334          listBatches();
   335          getTableSchema(currentTableName);
   336      });
   337      shardPicker.append(shardPickButton);
   338  }
   339  
   340  function initBatchLoader() {
   341      var batchInput = $('<input class="small-input" id="time-picker"/>').datetimepicker();
   342      var columnSelect = $('<select id="column-select"/>');
   343      var vpLoadButton = $('<button class="medium-button">Load</button>').click(function () {
   344          var time = batchInput.val();
   345          if (!time) {
   346              alert("No batch id specified!");
   347              return
   348          }
   349          var batchID = Math.floor(new Date(time).getTime() / 1000 / 86400);
   350          var column = $('#column-select').find(":selected").text();
   351          if (!column) {
   352              alert("no column selected!");
   353              return
   354          }
   355          loadVectorParty(batchID, column);
   356      });
   357  
   358      var batchLoad = $('#batch-load');
   359      batchLoad.append();
   360      batchLoad.append('<label for="time-picker">BatchID: </label>');
   361      batchLoad.append(batchInput);
   362      batchLoad.append('<label for="column-select">Column: </label>');
   363      batchLoad.append(columnSelect);
   364      batchLoad.append(vpLoadButton);
   365  }