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})">«</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})">»</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 }